Merge "Revert^3 "Enable input window rotation flag"" into sc-v2-dev
diff --git a/Android.bp b/Android.bp
index 94941fd..a99cef8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -297,6 +297,7 @@
         ],
         include_dirs: [
             "frameworks/av/aidl",
+            "frameworks/native/libs/permission/aidl",
             "packages/modules/Connectivity/framework/aidl-export",
         ],
     },
@@ -340,6 +341,8 @@
         "modules-utils-preconditions",
         "modules-utils-os",
         "framework-permission-aidl-java",
+        "spatializer-aidl-java",
+        "audiopolicy-types-aidl-java",
     ],
 }
 
@@ -538,6 +541,7 @@
         ],
         include_dirs: [
             "frameworks/av/aidl",
+            "frameworks/native/libs/permission/aidl",
             "packages/modules/Connectivity/framework/aidl-export",
         ],
     },
diff --git a/ApiDocs.bp b/ApiDocs.bp
index 0aed5d9..aae4a71 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -113,7 +113,10 @@
     // TODO(b/169090544): remove below aidl includes.
     aidl: {
         local_include_dirs: ["media/aidl"],
-        include_dirs: ["frameworks/av/aidl"],
+        include_dirs: [
+            "frameworks/av/aidl",
+            "frameworks/native/libs/permission/aidl",
+        ],
     },
 }
 
@@ -199,7 +202,10 @@
     // TODO(b/169090544): remove below aidl includes.
     aidl: {
         local_include_dirs: ["media/aidl"],
-        include_dirs: ["frameworks/av/aidl"],
+        include_dirs: [
+            "frameworks/av/aidl",
+            "frameworks/native/libs/permission/aidl",
+        ],
     },
 }
 
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 db23a6d..c33d5ec 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -332,17 +332,20 @@
 
             long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
             int callingUid = Binder.getCallingUid();
-            UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
             EXECUTOR.execute(() -> {
                 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
                 AppSearchUserInstance instance = null;
                 int operationSuccessCount = 0;
                 int operationFailureCount = 0;
                 try {
-                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
-                    verifyUserUnlocked(callingUser);
-                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
-                    verifyNotInstantApp(userContext, packageName);
+                    verifyCaller(callingUid, packageName);
+
+                    // Obtain the user where the client wants to run the operations in. This should
+                    // end up being the same as userHandle, assuming it is not a special user and
+                    // the client is allowed to run operations in that user.
+                    UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+                    verifyUserUnlocked(targetUser);
+
                     List<AppSearchSchema> schemas = new ArrayList<>(schemaBundles.size());
                     for (int i = 0; i < schemaBundles.size(); i++) {
                         schemas.add(new AppSearchSchema(schemaBundles.get(i)));
@@ -359,7 +362,7 @@
                         }
                         schemasVisibleToPackages.put(entry.getKey(), packageIdentifiers);
                     }
-                    instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
+                    instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
                     SetSchemaResponse setSchemaResponse = instance.getAppSearchImpl().setSchema(
                             packageName,
                             databaseName,
@@ -418,15 +421,18 @@
             Objects.requireNonNull(callback);
 
             int callingUid = Binder.getCallingUid();
-            UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
             EXECUTOR.execute(() -> {
                 try {
-                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
-                    verifyUserUnlocked(callingUser);
-                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
-                    verifyNotInstantApp(userContext, packageName);
+                    verifyCaller(callingUid, packageName);
+
+                    // Obtain the user where the client wants to run the operations in. This should
+                    // end up being the same as userHandle, assuming it is not a special user and
+                    // the client is allowed to run operations in that user.
+                    UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+                    verifyUserUnlocked(targetUser);
+
                     AppSearchUserInstance instance =
-                            mAppSearchUserInstanceManager.getUserInstance(callingUser);
+                            mAppSearchUserInstanceManager.getUserInstance(targetUser);
                     GetSchemaResponse response =
                             instance.getAppSearchImpl().getSchema(packageName, databaseName);
                     invokeCallbackOnResult(
@@ -450,15 +456,18 @@
             Objects.requireNonNull(callback);
 
             int callingUid = Binder.getCallingUid();
-            UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
             EXECUTOR.execute(() -> {
                 try {
-                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
-                    verifyUserUnlocked(callingUser);
-                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
-                    verifyNotInstantApp(userContext, packageName);
+                    verifyCaller(callingUid, packageName);
+
+                    // Obtain the user where the client wants to run the operations in. This should
+                    // end up being the same as userHandle, assuming it is not a special user and
+                    // the client is allowed to run operations in that user.
+                    UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+                    verifyUserUnlocked(targetUser);
+
                     AppSearchUserInstance instance =
-                            mAppSearchUserInstanceManager.getUserInstance(callingUser);
+                            mAppSearchUserInstanceManager.getUserInstance(targetUser);
                     List<String> namespaces =
                             instance.getAppSearchImpl().getNamespaces(packageName, databaseName);
                     invokeCallbackOnResult(
@@ -485,20 +494,23 @@
 
             long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
             int callingUid = Binder.getCallingUid();
-            UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
             EXECUTOR.execute(() -> {
                 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
                 AppSearchUserInstance instance = null;
                 int operationSuccessCount = 0;
                 int operationFailureCount = 0;
                 try {
-                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
-                    verifyUserUnlocked(callingUser);
-                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
-                    verifyNotInstantApp(userContext, packageName);
+                    verifyCaller(callingUid, packageName);
+
+                    // Obtain the user where the client wants to run the operations in. This should
+                    // end up being the same as userHandle, assuming it is not a special user and
+                    // the client is allowed to run operations in that user.
+                    UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+                    verifyUserUnlocked(targetUser);
+
                     AppSearchBatchResult.Builder<String, Void> resultBuilder =
                             new AppSearchBatchResult.Builder<>();
-                    instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
+                    instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
                     for (int i = 0; i < documentBundles.size(); i++) {
                         GenericDocument document = new GenericDocument(documentBundles.get(i));
                         try {
@@ -571,20 +583,23 @@
 
             long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
             int callingUid = Binder.getCallingUid();
-            UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
             EXECUTOR.execute(() -> {
                 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
                 AppSearchUserInstance instance = null;
                 int operationSuccessCount = 0;
                 int operationFailureCount = 0;
                 try {
-                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
-                    verifyUserUnlocked(callingUser);
-                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
-                    verifyNotInstantApp(userContext, packageName);
+                    verifyCaller(callingUid, packageName);
+
+                    // Obtain the user where the client wants to run the operations in. This should
+                    // end up being the same as userHandle, assuming it is not a special user and
+                    // the client is allowed to run operations in that user.
+                    UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+                    verifyUserUnlocked(targetUser);
+
                     AppSearchBatchResult.Builder<String, Bundle> resultBuilder =
                             new AppSearchBatchResult.Builder<>();
-                    instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
+                    instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
                     for (int i = 0; i < ids.size(); i++) {
                         String id = ids.get(i);
                         try {
@@ -652,18 +667,21 @@
 
             long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
             int callingUid = Binder.getCallingUid();
-            UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
             EXECUTOR.execute(() -> {
                 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
                 AppSearchUserInstance instance = null;
                 int operationSuccessCount = 0;
                 int operationFailureCount = 0;
                 try {
-                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
-                    verifyUserUnlocked(callingUser);
-                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
-                    verifyNotInstantApp(userContext, packageName);
-                    instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
+                    verifyCaller(callingUid, packageName);
+
+                    // Obtain the user where the client wants to run the operations in. This should
+                    // end up being the same as userHandle, assuming it is not a special user and
+                    // the client is allowed to run operations in that user.
+                    UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+                    verifyUserUnlocked(targetUser);
+
+                    instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
                     SearchResultPage searchResultPage = instance.getAppSearchImpl().query(
                             packageName,
                             databaseName,
@@ -718,18 +736,21 @@
 
             long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
             int callingUid = Binder.getCallingUid();
-            UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
             EXECUTOR.execute(() -> {
                 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
                 AppSearchUserInstance instance = null;
                 int operationSuccessCount = 0;
                 int operationFailureCount = 0;
                 try {
-                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
-                    verifyUserUnlocked(callingUser);
-                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
-                    verifyNotInstantApp(userContext, packageName);
-                    instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
+                    verifyCaller(callingUid, packageName);
+
+                    // Obtain the user where the client wants to run the operations in. This should
+                    // end up being the same as userHandle, assuming it is not a special user and
+                    // the client is allowed to run operations in that user.
+                    UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+                    verifyUserUnlocked(targetUser);
+
+                    instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
 
                     boolean callerHasSystemAccess =
                             instance.getVisibilityStore().doesCallerHaveSystemAccess(packageName);
@@ -783,17 +804,18 @@
             Objects.requireNonNull(callback);
 
             int callingUid = Binder.getCallingUid();
-            UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
-            // TODO(b/162450968) check nextPageToken is being advanced by the same uid as originally
-            // opened it
             EXECUTOR.execute(() -> {
                 try {
-                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
-                    verifyUserUnlocked(callingUser);
-                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
-                    verifyNotInstantApp(userContext, packageName);
+                    verifyCaller(callingUid, packageName);
+
+                    // Obtain the user where the client wants to run the operations in. This should
+                    // end up being the same as userHandle, assuming it is not a special user and
+                    // the client is allowed to run operations in that user.
+                    UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+                    verifyUserUnlocked(targetUser);
+
                     AppSearchUserInstance instance =
-                            mAppSearchUserInstanceManager.getUserInstance(callingUser);
+                            mAppSearchUserInstanceManager.getUserInstance(targetUser);
                     SearchResultPage searchResultPage =
                             instance.getAppSearchImpl().getNextPage(packageName, nextPageToken);
                     invokeCallbackOnResult(
@@ -812,15 +834,18 @@
             Objects.requireNonNull(userHandle);
 
             int callingUid = Binder.getCallingUid();
-            UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
             EXECUTOR.execute(() -> {
                 try {
-                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
-                    verifyUserUnlocked(callingUser);
-                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
-                    verifyNotInstantApp(userContext, packageName);
+                    verifyCaller(callingUid, packageName);
+
+                    // Obtain the user where the client wants to run the operations in. This should
+                    // end up being the same as userHandle, assuming it is not a special user and
+                    // the client is allowed to run operations in that user.
+                    UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+                    verifyUserUnlocked(targetUser);
+
                     AppSearchUserInstance instance =
-                            mAppSearchUserInstanceManager.getUserInstance(callingUser);
+                            mAppSearchUserInstanceManager.getUserInstance(targetUser);
                     instance.getAppSearchImpl().invalidateNextPageToken(packageName, nextPageToken);
                 } catch (Throwable t) {
                     Log.e(TAG, "Unable to invalidate the query page token", t);
@@ -846,15 +871,18 @@
             Objects.requireNonNull(callback);
 
             int callingUid = Binder.getCallingUid();
-            UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
             EXECUTOR.execute(() -> {
                 try {
-                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
-                    verifyUserUnlocked(callingUser);
-                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
-                    verifyNotInstantApp(userContext, packageName);
+                    verifyCaller(callingUid, packageName);
+
+                    // Obtain the user where the client wants to run the operations in. This should
+                    // end up being the same as userHandle, assuming it is not a special user and
+                    // the client is allowed to run operations in that user.
+                    UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+                    verifyUserUnlocked(targetUser);
+
                     AppSearchUserInstance instance =
-                            mAppSearchUserInstanceManager.getUserInstance(callingUser);
+                            mAppSearchUserInstanceManager.getUserInstance(targetUser);
                     // we don't need to append the file. The file is always brand new.
                     try (DataOutputStream outputStream = new DataOutputStream(
                             new FileOutputStream(fileDescriptor.getFileDescriptor()))) {
@@ -895,15 +923,18 @@
             Objects.requireNonNull(callback);
 
             int callingUid = Binder.getCallingUid();
-            UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
             EXECUTOR.execute(() -> {
                 try {
-                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
-                    verifyUserUnlocked(callingUser);
-                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
-                    verifyNotInstantApp(userContext, packageName);
+                    verifyCaller(callingUid, packageName);
+
+                    // Obtain the user where the client wants to run the operations in. This should
+                    // end up being the same as userHandle, assuming it is not a special user and
+                    // the client is allowed to run operations in that user.
+                    UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+                    verifyUserUnlocked(targetUser);
+
                     AppSearchUserInstance instance =
-                            mAppSearchUserInstanceManager.getUserInstance(callingUser);
+                            mAppSearchUserInstanceManager.getUserInstance(targetUser);
 
                     GenericDocument document;
                     ArrayList<Bundle> migrationFailureBundles = new ArrayList<>();
@@ -957,15 +988,18 @@
             Objects.requireNonNull(callback);
 
             int callingUid = Binder.getCallingUid();
-            UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
             EXECUTOR.execute(() -> {
                 try {
-                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
-                    verifyUserUnlocked(callingUser);
-                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
-                    verifyNotInstantApp(userContext, packageName);
+                    verifyCaller(callingUid, packageName);
+
+                    // Obtain the user where the client wants to run the operations in. This should
+                    // end up being the same as userHandle, assuming it is not a special user and
+                    // the client is allowed to run operations in that user.
+                    UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+                    verifyUserUnlocked(targetUser);
+
                     AppSearchUserInstance instance =
-                            mAppSearchUserInstanceManager.getUserInstance(callingUser);
+                            mAppSearchUserInstanceManager.getUserInstance(targetUser);
 
                     if (systemUsage
                             && !instance.getVisibilityStore()
@@ -1004,20 +1038,23 @@
 
             long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
             int callingUid = Binder.getCallingUid();
-            UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
             EXECUTOR.execute(() -> {
                 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
                 AppSearchUserInstance instance = null;
                 int operationSuccessCount = 0;
                 int operationFailureCount = 0;
                 try {
-                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
-                    verifyUserUnlocked(callingUser);
-                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
-                    verifyNotInstantApp(userContext, packageName);
+                    verifyCaller(callingUid, packageName);
+
+                    // Obtain the user where the client wants to run the operations in. This should
+                    // end up being the same as userHandle, assuming it is not a special user and
+                    // the client is allowed to run operations in that user.
+                    UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+                    verifyUserUnlocked(targetUser);
+
                     AppSearchBatchResult.Builder<String, Void> resultBuilder =
                             new AppSearchBatchResult.Builder<>();
-                    instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
+                    instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
                     for (int i = 0; i < ids.size(); i++) {
                         String id = ids.get(i);
                         try {
@@ -1090,18 +1127,21 @@
 
             long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
             int callingUid = Binder.getCallingUid();
-            UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
             EXECUTOR.execute(() -> {
                 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
                 AppSearchUserInstance instance = null;
                 int operationSuccessCount = 0;
                 int operationFailureCount = 0;
                 try {
-                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
-                    verifyUserUnlocked(callingUser);
-                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
-                    verifyNotInstantApp(userContext, packageName);
-                    instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
+                    verifyCaller(callingUid, packageName);
+
+                    // Obtain the user where the client wants to run the operations in. This should
+                    // end up being the same as userHandle, assuming it is not a special user and
+                    // the client is allowed to run operations in that user.
+                    UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+                    verifyUserUnlocked(targetUser);
+
+                    instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
                     instance.getAppSearchImpl().removeByQuery(
                             packageName,
                             databaseName,
@@ -1154,15 +1194,18 @@
             Objects.requireNonNull(callback);
 
             int callingUid = Binder.getCallingUid();
-            UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
             EXECUTOR.execute(() -> {
                 try {
-                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
-                    verifyUserUnlocked(callingUser);
-                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
-                    verifyNotInstantApp(userContext, packageName);
+                    verifyCaller(callingUid, packageName);
+
+                    // Obtain the user where the client wants to run the operations in. This should
+                    // end up being the same as userHandle, assuming it is not a special user and
+                    // the client is allowed to run operations in that user.
+                    UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+                    verifyUserUnlocked(targetUser);
+
                     AppSearchUserInstance instance =
-                            mAppSearchUserInstanceManager.getUserInstance(callingUser);
+                            mAppSearchUserInstanceManager.getUserInstance(targetUser);
                     StorageInfo storageInfo = instance.getAppSearchImpl()
                             .getStorageInfoForDatabase(packageName, databaseName);
                     Bundle storageInfoBundle = storageInfo.getBundle();
@@ -1184,18 +1227,21 @@
 
             long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
             int callingUid = Binder.getCallingUid();
-            UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
             EXECUTOR.execute(() -> {
                 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
                 AppSearchUserInstance instance = null;
                 int operationSuccessCount = 0;
                 int operationFailureCount = 0;
                 try {
-                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
-                    verifyUserUnlocked(callingUser);
-                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
-                    verifyNotInstantApp(userContext, packageName);
-                    instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
+                    verifyCaller(callingUid, packageName);
+
+                    // Obtain the user where the client wants to run the operations in. This should
+                    // end up being the same as userHandle, assuming it is not a special user and
+                    // the client is allowed to run operations in that user.
+                    UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+                    verifyUserUnlocked(targetUser);
+
+                    instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
                     instance.getAppSearchImpl().persistToDisk(PersistType.Code.FULL);
                     ++operationSuccessCount;
                 } catch (Throwable t) {
@@ -1236,7 +1282,6 @@
 
             long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
             int callingUid = Binder.getCallingUid();
-            UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
 
             EXECUTOR.execute(() -> {
                 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
@@ -1244,12 +1289,18 @@
                 int operationSuccessCount = 0;
                 int operationFailureCount = 0;
                 try {
-                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
-                    verifyUserUnlocked(callingUser);
-                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
-                    verifyNotInstantApp(userContext, packageName);
+                    verifyCaller(callingUid, packageName);
+
+                    // Obtain the user where the client wants to run the operations in. This should
+                    // end up being the same as userHandle, assuming it is not a special user and
+                    // the client is allowed to run operations in that user.
+                    UserHandle targetUser = handleIncomingUser(userHandle, callingUid);
+                    verifyUserUnlocked(targetUser);
+
+                    Context targetUserContext = mContext.createContextAsUser(targetUser,
+                            /*flags=*/ 0);
                     instance = mAppSearchUserInstanceManager.getOrCreateUserInstance(
-                            userContext, callingUser, AppSearchConfig.getInstance(EXECUTOR));
+                            targetUserContext, targetUser, AppSearchConfig.getInstance(EXECUTOR));
                     ++operationSuccessCount;
                     invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
                 } catch (Throwable t) {
@@ -1278,29 +1329,6 @@
             });
         }
 
-        private void verifyCallingPackage(
-                @NonNull Context userContext,
-                @NonNull UserHandle actualCallingUser,
-                int actualCallingUid,
-                @NonNull String claimedCallingPackage) {
-            Objects.requireNonNull(actualCallingUser);
-            Objects.requireNonNull(claimedCallingPackage);
-
-            int claimedCallingUid = PackageUtil.getPackageUid(
-                    userContext, claimedCallingPackage);
-            if (claimedCallingUid == INVALID_UID) {
-                throw new SecurityException(
-                        "Specified calling package [" + claimedCallingPackage + "] not found");
-            }
-            if (claimedCallingUid != actualCallingUid) {
-                throw new SecurityException(
-                        "Specified calling package ["
-                                + claimedCallingPackage
-                                + "] does not match the calling uid "
-                                + actualCallingUid);
-            }
-        }
-
         /** Invokes the {@link IAppSearchResultCallback} with the result. */
         private void invokeCallbackOnResult(
                 IAppSearchResultCallback callback, AppSearchResult<?> result) {
@@ -1354,33 +1382,72 @@
     /**
      * Helper for dealing with incoming user arguments to system service calls.
      *
-     * @param requestedUser The user which the caller is requesting to execute as.
+     * @param targetUserHandle The user which the caller is requesting to execute as.
      * @param callingUid The actual uid of the caller as determined by Binder.
      * @return the user handle that the call should run as. Will always be a concrete user.
      */
     @NonNull
-    private UserHandle handleIncomingUser(@NonNull UserHandle requestedUser, int callingUid) {
-        UserHandle callingUser = UserHandle.getUserHandleForUid(callingUid);
-        if (callingUser.equals(requestedUser)) {
-            return requestedUser;
+    private UserHandle handleIncomingUser(@NonNull UserHandle targetUserHandle, int callingUid) {
+        UserHandle callingUserHandle = UserHandle.getUserHandleForUid(callingUid);
+        if (callingUserHandle.equals(targetUserHandle)) {
+            return targetUserHandle;
         }
 
         // Duplicates UserController#ensureNotSpecialUser
-        if (requestedUser.getIdentifier() < 0) {
+        if (targetUserHandle.getIdentifier() < 0) {
             throw new IllegalArgumentException(
-                    "Call does not support special user " + requestedUser);
+                    "Call does not support special user " + targetUserHandle);
         }
 
         throw new SecurityException(
-                "Requested user, " + requestedUser + ", is not the same as the calling user, "
-                        + callingUser + ".");
+                "Requested user, " + targetUserHandle + ", is not the same as the calling user, "
+                        + callingUserHandle + ".");
     }
 
     /**
-     * Helper for ensuring instant apps can't make calls to AppSearch.
+     * Verify various aspects of the calling user.
      *
-     * @param userContext Context of the user making the call.
-     * @param packageName Package name of the caller.
+     * @param callingUid Uid of the caller, usually retrieved from Binder for authenticity.
+     * @param claimedCallingPackage Package name the caller claims to be.
+     */
+    private void verifyCaller(int callingUid, @NonNull String claimedCallingPackage) {
+        // Obtain the user where the client is running in. Note that this could be different from
+        // the userHandle where the client wants to run the AppSearch operation in.
+        UserHandle callingUserHandle = UserHandle.getUserHandleForUid(callingUid);
+        Context callingUserContext = mContext.createContextAsUser(callingUserHandle,
+                /*flags=*/ 0);
+
+        verifyCallingPackage(callingUserContext, callingUid, claimedCallingPackage);
+        verifyNotInstantApp(callingUserContext, claimedCallingPackage);
+    }
+
+    /**
+     * Check that the caller's supposed package name matches the uid making the call.
+     *
+     * @throws SecurityException if the package name and uid don't match.
+     */
+    private void verifyCallingPackage(
+            @NonNull Context actualCallingUserContext,
+            int actualCallingUid,
+            @NonNull String claimedCallingPackage) {
+        int claimedCallingUid = PackageUtil.getPackageUid(
+                actualCallingUserContext, claimedCallingPackage);
+        if (claimedCallingUid == INVALID_UID) {
+            throw new SecurityException(
+                    "Specified calling package [" + claimedCallingPackage + "] not found");
+        }
+        if (claimedCallingUid != actualCallingUid) {
+            throw new SecurityException(
+                    "Specified calling package ["
+                            + claimedCallingPackage
+                            + "] does not match the calling uid "
+                            + actualCallingUid);
+        }
+    }
+
+    /**
+     * Ensure instant apps can't make calls to AppSearch.
+     *
      * @throws SecurityException if the caller is an instant app.
      */
     private void verifyNotInstantApp(@NonNull Context userContext, @NonNull String packageName) {
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 45588e8..9eb7bb71 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -3941,6 +3941,10 @@
         if (idleUntil) {
             mAlarmManager.setIdleUntil(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                     mNextAlarmTime, "DeviceIdleController.deep", mDeepAlarmListener, mHandler);
+        } else if (mState == STATE_LOCATING) {
+            // Use setExact so we don't keep the GPS active for too long.
+            mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                    mNextAlarmTime, "DeviceIdleController.deep", mDeepAlarmListener, mHandler);
         } else {
             if (mConstants.USE_WINDOW_ALARMS) {
                 mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
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 86fa06c..9572808 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -2603,7 +2603,7 @@
             final int uid = mPackageManagerInternal.getPackageUid(packageName, 0, userId);
             if (callingUid != uid && !UserHandle.isCore(callingUid)) {
                 throw new SecurityException("Uid " + callingUid
-                        + " cannot query hasScheduleExactAlarm for uid " + uid);
+                        + " cannot query hasScheduleExactAlarm for package " + packageName);
             }
             return (uid > 0) ? hasScheduleExactAlarmInternal(packageName, uid) : false;
         }
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 78670c7..591e8ba 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -690,7 +690,6 @@
 
     @VisibleForTesting
     class PendingJobComparator implements Comparator<JobStatus> {
-        private final SparseBooleanArray mUidHasEjCache = new SparseBooleanArray();
         private final SparseLongArray mEarliestRegEnqueueTimeCache = new SparseLongArray();
 
         /**
@@ -699,14 +698,11 @@
         @GuardedBy("mLock")
         @VisibleForTesting
         void refreshLocked() {
-            mUidHasEjCache.clear();
             mEarliestRegEnqueueTimeCache.clear();
             for (int i = 0; i < mPendingJobs.size(); ++i) {
                 final JobStatus job = mPendingJobs.get(i);
                 final int uid = job.getSourceUid();
-                if (job.isRequestedExpeditedJob()) {
-                    mUidHasEjCache.put(uid, true);
-                } else {
+                if (!job.isRequestedExpeditedJob()) {
                     final long earliestEnqueueTime =
                             mEarliestRegEnqueueTimeCache.get(uid, Long.MAX_VALUE);
                     mEarliestRegEnqueueTimeCache.put(uid,
@@ -736,9 +732,7 @@
                     return o1EJ ? -1 : 1;
                 }
             }
-            final boolean uid1HasEj = mUidHasEjCache.get(o1.getSourceUid());
-            final boolean uid2HasEj = mUidHasEjCache.get(o2.getSourceUid());
-            if ((uid1HasEj || uid2HasEj) && (o1EJ || o2EJ)) {
+            if (o1EJ || o2EJ) {
                 // We MUST prioritize EJs ahead of regular jobs within a single app. Since we do
                 // that, in order to satisfy the transitivity constraint of the comparator, if
                 // any UID has an EJ, we must ensure that the EJ is ordered ahead of the regular
@@ -759,9 +753,13 @@
                     } else if (uid1EarliestRegEnqueueTime > uid2EarliestRegEnqueueTime) {
                         return 1;
                     }
-                } else if (o1EJ && uid1EarliestRegEnqueueTime < o2.enqueueTime) {
+                } else if (o1EJ && uid1EarliestRegEnqueueTime <= o2.enqueueTime) {
+                    // Include = to ensure that if we sorted an EJ ahead of a regular job at time X
+                    // then we make sure to sort it ahead of all regular jobs at time X.
                     return -1;
-                } else if (o2EJ && uid2EarliestRegEnqueueTime < o1.enqueueTime) {
+                } else if (o2EJ && uid2EarliestRegEnqueueTime <= o1.enqueueTime) {
+                    // Include = to ensure that if we sorted an EJ ahead of a regular job at time X
+                    // then we make sure to sort it ahead of all regular jobs at time X.
                     return 1;
                 }
             }
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index 1bf732b..d963e68 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -120,7 +120,7 @@
     srcs: [
         "java/android/media/ApplicationMediaCapabilities.java",
         "java/android/media/MediaFeature.java",
-        "java/android/media/MediaTranscodeManager.java",
+        "java/android/media/MediaTranscodingManager.java",
     ],
     path: "java",
 }
diff --git a/apex/media/framework/api/system-current.txt b/apex/media/framework/api/system-current.txt
index ce68447..6eea769 100644
--- a/apex/media/framework/api/system-current.txt
+++ b/apex/media/framework/api/system-current.txt
@@ -1,15 +1,15 @@
 // Signature format: 2.0
 package android.media {
 
-  public final class MediaTranscodeManager {
-    method @Nullable public android.media.MediaTranscodeManager.TranscodingSession enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener);
+  public final class MediaTranscodingManager {
+    method @Nullable public android.media.MediaTranscodingManager.TranscodingSession enqueueRequest(@NonNull android.media.MediaTranscodingManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodingManager.OnTranscodingFinishedListener);
   }
 
-  @java.lang.FunctionalInterface public static interface MediaTranscodeManager.OnTranscodingFinishedListener {
-    method public void onTranscodingFinished(@NonNull android.media.MediaTranscodeManager.TranscodingSession);
+  @java.lang.FunctionalInterface public static interface MediaTranscodingManager.OnTranscodingFinishedListener {
+    method public void onTranscodingFinished(@NonNull android.media.MediaTranscodingManager.TranscodingSession);
   }
 
-  public abstract static class MediaTranscodeManager.TranscodingRequest {
+  public abstract static class MediaTranscodingManager.TranscodingRequest {
     method public int getClientPid();
     method public int getClientUid();
     method @Nullable public android.os.ParcelFileDescriptor getDestinationFileDescriptor();
@@ -18,13 +18,13 @@
     method @NonNull public android.net.Uri getSourceUri();
   }
 
-  public static class MediaTranscodeManager.TranscodingRequest.VideoFormatResolver {
-    ctor public MediaTranscodeManager.TranscodingRequest.VideoFormatResolver(@NonNull android.media.ApplicationMediaCapabilities, @NonNull android.media.MediaFormat);
+  public static class MediaTranscodingManager.TranscodingRequest.VideoFormatResolver {
+    ctor public MediaTranscodingManager.TranscodingRequest.VideoFormatResolver(@NonNull android.media.ApplicationMediaCapabilities, @NonNull android.media.MediaFormat);
     method @Nullable public android.media.MediaFormat resolveVideoFormat();
     method public boolean shouldTranscode();
   }
 
-  public static final class MediaTranscodeManager.TranscodingSession {
+  public static final class MediaTranscodingManager.TranscodingSession {
     method public boolean addClientUid(int);
     method public void cancel();
     method @NonNull public java.util.List<java.lang.Integer> getClientUids();
@@ -33,7 +33,7 @@
     method public int getResult();
     method public int getSessionId();
     method public int getStatus();
-    method public void setOnProgressUpdateListener(@NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingSession.OnProgressUpdateListener);
+    method public void setOnProgressUpdateListener(@NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodingManager.TranscodingSession.OnProgressUpdateListener);
     field public static final int ERROR_DROPPED_BY_SERVICE = 1; // 0x1
     field public static final int ERROR_NONE = 0; // 0x0
     field public static final int ERROR_SERVICE_DIED = 2; // 0x2
@@ -47,21 +47,21 @@
     field public static final int STATUS_RUNNING = 2; // 0x2
   }
 
-  @java.lang.FunctionalInterface public static interface MediaTranscodeManager.TranscodingSession.OnProgressUpdateListener {
-    method public void onProgressUpdate(@NonNull android.media.MediaTranscodeManager.TranscodingSession, @IntRange(from=0, to=100) int);
+  @java.lang.FunctionalInterface public static interface MediaTranscodingManager.TranscodingSession.OnProgressUpdateListener {
+    method public void onProgressUpdate(@NonNull android.media.MediaTranscodingManager.TranscodingSession, @IntRange(from=0, to=100) int);
   }
 
-  public static final class MediaTranscodeManager.VideoTranscodingRequest extends android.media.MediaTranscodeManager.TranscodingRequest {
+  public static final class MediaTranscodingManager.VideoTranscodingRequest extends android.media.MediaTranscodingManager.TranscodingRequest {
     method @NonNull public android.media.MediaFormat getVideoTrackFormat();
   }
 
-  public static final class MediaTranscodeManager.VideoTranscodingRequest.Builder {
-    ctor public MediaTranscodeManager.VideoTranscodingRequest.Builder(@NonNull android.net.Uri, @NonNull android.net.Uri, @NonNull android.media.MediaFormat);
-    method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest build();
-    method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest.Builder setClientPid(int);
-    method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest.Builder setClientUid(int);
-    method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest.Builder setDestinationFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
-    method @NonNull public android.media.MediaTranscodeManager.VideoTranscodingRequest.Builder setSourceFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
+  public static final class MediaTranscodingManager.VideoTranscodingRequest.Builder {
+    ctor public MediaTranscodingManager.VideoTranscodingRequest.Builder(@NonNull android.net.Uri, @NonNull android.net.Uri, @NonNull android.media.MediaFormat);
+    method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest build();
+    method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest.Builder setClientPid(int);
+    method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest.Builder setClientUid(int);
+    method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest.Builder setDestinationFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
+    method @NonNull public android.media.MediaTranscodingManager.VideoTranscodingRequest.Builder setSourceFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
   }
 
 }
diff --git a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java b/apex/media/framework/java/android/media/MediaFrameworkInitializer.java
index de2924e..75a56b72 100644
--- a/apex/media/framework/java/android/media/MediaFrameworkInitializer.java
+++ b/apex/media/framework/java/android/media/MediaFrameworkInitializer.java
@@ -75,8 +75,8 @@
     public static void registerServiceWrappers() {
         SystemServiceRegistry.registerContextAwareService(
                 Context.MEDIA_TRANSCODING_SERVICE,
-                MediaTranscodeManager.class,
-                context -> new MediaTranscodeManager(context)
+                MediaTranscodingManager.class,
+                context -> new MediaTranscodingManager(context)
         );
         if (SdkLevel.isAtLeastS()) {
             SystemServiceRegistry.registerContextAwareService(
diff --git a/apex/media/framework/java/android/media/MediaTranscodeManager.java b/apex/media/framework/java/android/media/MediaTranscodingManager.java
similarity index 98%
rename from apex/media/framework/java/android/media/MediaTranscodeManager.java
rename to apex/media/framework/java/android/media/MediaTranscodingManager.java
index 5742d43..93d58d0 100644
--- a/apex/media/framework/java/android/media/MediaTranscodeManager.java
+++ b/apex/media/framework/java/android/media/MediaTranscodingManager.java
@@ -54,7 +54,7 @@
 /**
  Android 12 introduces Compatible media transcoding feature.  See
  <a href="https://developer.android.com/about/versions/12/features#compatible_media_transcoding">
- Compatible media transcoding</a>. MediaTranscodeManager provides an interface to the system's media
+ Compatible media transcoding</a>. MediaTranscodingManager provides an interface to the system's media
  transcoding service and can be used to transcode media files, e.g. transcoding a video from HEVC to
  AVC.
 
@@ -69,7 +69,7 @@
  <p>
  To transcode a media file, first create a {@link TranscodingRequest} through its builder class
  {@link VideoTranscodingRequest.Builder}. Transcode requests are then enqueue to the manager through
- {@link MediaTranscodeManager#enqueueRequest(
+ {@link MediaTranscodingManager#enqueueRequest(
          TranscodingRequest, Executor, OnTranscodingFinishedListener)}
  TranscodeRequest are processed based on client process's priority and request priority. When a
  transcode operation is completed the caller is notified via its
@@ -87,8 +87,8 @@
  */
 @MinSdk(Build.VERSION_CODES.S)
 @SystemApi
-public final class MediaTranscodeManager {
-    private static final String TAG = "MediaTranscodeManager";
+public final class MediaTranscodingManager {
+    private static final String TAG = "MediaTranscodingManager";
 
     /** Maximum number of retry to connect to the service. */
     private static final int CONNECT_SERVICE_RETRY_COUNT = 100;
@@ -127,7 +127,7 @@
     private final Object mLock = new Object();
     @GuardedBy("mLock")
     @NonNull private ITranscodingClient mTranscodingClient = null;
-    private static MediaTranscodeManager sMediaTranscodeManager;
+    private static MediaTranscodingManager sMediaTranscodingManager;
 
     private void handleTranscodingFinished(int sessionId, TranscodingResultParcel result) {
         synchronized (mPendingTranscodingSessions) {
@@ -306,7 +306,7 @@
                 }
 
                 try {
-                    // Do not set hasRetried for retry initiated by MediaTranscodeManager.
+                    // Do not set hasRetried for retry initiated by MediaTranscodingManager.
                     session.retryInternal(false /*setHasRetried*/);
                 } catch (Exception re) {
                     // TODO(hkuang): Return correct error code to the client.
@@ -423,7 +423,7 @@
     /**
      * @hide
      */
-    public MediaTranscodeManager(@NonNull Context context) {
+    public MediaTranscodingManager(@NonNull Context context) {
         mContext = context;
         mContentResolver = mContext.getContentResolver();
         mPackageName = mContext.getPackageName();
@@ -1348,7 +1348,7 @@
                     @IntRange(from = 0, to = 100) int progress);
         }
 
-        private final MediaTranscodeManager mManager;
+        private final MediaTranscodingManager mManager;
         private Executor mListenerExecutor;
         private OnTranscodingFinishedListener mListener;
         private int mSessionId = -1;
@@ -1374,7 +1374,7 @@
         private final TranscodingRequest mRequest;
 
         private TranscodingSession(
-                @NonNull MediaTranscodeManager manager,
+                @NonNull MediaTranscodingManager manager,
                 @NonNull TranscodingRequest request,
                 @NonNull TranscodingSessionParcel parcel,
                 @NonNull @CallbackExecutor Executor executor,
@@ -1675,10 +1675,10 @@
 
     /**
      * Enqueues a TranscodingRequest for execution.
-     * <p> Upon successfully accepting the request, MediaTranscodeManager will return a
+     * <p> Upon successfully accepting the request, MediaTranscodingManager will return a
      * {@link TranscodingSession} to the client. Client should use {@link TranscodingSession} to
      * track the progress and get the result.
-     * <p> MediaTranscodeManager will return null if fails to accept the request due to service
+     * <p> MediaTranscodingManager will return null if fails to accept the request due to service
      * rebooting. Client could retry again after receiving null.
      *
      * @param transcodingRequest The TranscodingRequest to enqueue.
diff --git a/cmds/bootanimation/Android.bp b/cmds/bootanimation/Android.bp
index b2b66c2..3534624 100644
--- a/cmds/bootanimation/Android.bp
+++ b/cmds/bootanimation/Android.bp
@@ -71,7 +71,7 @@
         "libui",
         "libjnigraphics",
         "libEGL",
-        "libGLESv1_CM",
+        "libGLESv2",
         "libgui",
     ],
 }
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 3109c5c..7ed0bed 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -52,9 +52,8 @@
 #include <gui/DisplayEventReceiver.h>
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
-
-#include <GLES/gl.h>
-#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
 #include <EGL/eglext.h>
 
 #include "BootAnimation.h"
@@ -108,6 +107,93 @@
 static const char DISPLAYS_PROP_NAME[] = "persist.service.bootanim.displays";
 static const int ANIM_ENTRY_NAME_MAX = ANIM_PATH_MAX + 1;
 static constexpr size_t TEXT_POS_LEN_MAX = 16;
+static const int DYNAMIC_COLOR_COUNT = 4;
+static const char U_TEXTURE[] = "uTexture";
+static const char U_FADE[] = "uFade";
+static const char U_CROP_AREA[] = "uCropArea";
+static const char U_START_COLOR_PREFIX[] = "uStartColor";
+static const char U_END_COLOR_PREFIX[] = "uEndColor";
+static const char U_COLOR_PROGRESS[] = "uColorProgress";
+static const char A_UV[] = "aUv";
+static const char A_POSITION[] = "aPosition";
+static const char VERTEX_SHADER_SOURCE[] = R"(
+    precision mediump float;
+    attribute vec4 aPosition;
+    attribute highp vec2 aUv;
+    varying highp vec2 vUv;
+    void main() {
+        gl_Position = aPosition;
+        vUv = aUv;
+    })";
+static const char IMAGE_FRAG_DYNAMIC_COLORING_SHADER_SOURCE[] = R"(
+    precision mediump float;
+    const float cWhiteMaskThreshold = 0.05f;
+    uniform sampler2D uTexture;
+    uniform float uFade;
+    uniform float uColorProgress;
+    uniform vec4 uStartColor0;
+    uniform vec4 uStartColor1;
+    uniform vec4 uStartColor2;
+    uniform vec4 uStartColor3;
+    uniform vec4 uEndColor0;
+    uniform vec4 uEndColor1;
+    uniform vec4 uEndColor2;
+    uniform vec4 uEndColor3;
+    varying highp vec2 vUv;
+    void main() {
+        vec4 mask = texture2D(uTexture, vUv);
+        float r = mask.r;
+        float g = mask.g;
+        float b = mask.b;
+        float a = mask.a;
+        // If all channels have values, render pixel as a shade of white.
+        float useWhiteMask = step(cWhiteMaskThreshold, r)
+            * step(cWhiteMaskThreshold, g)
+            * step(cWhiteMaskThreshold, b)
+            * step(cWhiteMaskThreshold, a);
+        vec4 color = r * mix(uStartColor0, uEndColor0, uColorProgress)
+                + g * mix(uStartColor1, uEndColor1, uColorProgress)
+                + b * mix(uStartColor2, uEndColor2, uColorProgress)
+                + a * mix(uStartColor3, uEndColor3, uColorProgress);
+        color = mix(color, vec4(vec3((r + g + b + a) * 0.25f), 1.0), useWhiteMask);
+        gl_FragColor = vec4(color.x, color.y, color.z, (1.0 - uFade)) * color.a;
+    })";
+static const char IMAGE_FRAG_SHADER_SOURCE[] = R"(
+    precision mediump float;
+    uniform sampler2D uTexture;
+    uniform float uFade;
+    varying highp vec2 vUv;
+    void main() {
+        vec4 color = texture2D(uTexture, vUv);
+        gl_FragColor = vec4(color.x, color.y, color.z, (1.0 - uFade)) * color.a;
+    })";
+static const char TEXT_FRAG_SHADER_SOURCE[] = R"(
+    precision mediump float;
+    uniform sampler2D uTexture;
+    uniform vec4 uCropArea;
+    varying highp vec2 vUv;
+    void main() {
+        vec2 uv = vec2(mix(uCropArea.x, uCropArea.z, vUv.x),
+                       mix(uCropArea.y, uCropArea.w, vUv.y));
+        gl_FragColor = texture2D(uTexture, uv);
+    })";
+
+static GLfloat quadPositions[] = {
+    -0.5f, -0.5f,
+    +0.5f, -0.5f,
+    +0.5f, +0.5f,
+    +0.5f, +0.5f,
+    -0.5f, +0.5f,
+    -0.5f, -0.5f
+};
+static GLfloat quadUVs[] = {
+    0.0f, 1.0f,
+    1.0f, 1.0f,
+    1.0f, 0.0f,
+    1.0f, 0.0f,
+    0.0f, 0.0f,
+    0.0f, 1.0f
+};
 
 // ---------------------------------------------------------------------------
 
@@ -163,7 +249,8 @@
     requestExit();
 }
 
-static void* decodeImage(const void* encodedData, size_t dataLength, AndroidBitmapInfo* outInfo) {
+static void* decodeImage(const void* encodedData, size_t dataLength, AndroidBitmapInfo* outInfo,
+    bool premultiplyAlpha) {
     AImageDecoder* decoder = nullptr;
     AImageDecoder_createFromBuffer(encodedData, dataLength, &decoder);
     if (!decoder) {
@@ -177,6 +264,10 @@
     outInfo->stride = AImageDecoder_getMinimumStride(decoder);
     outInfo->flags = 0;
 
+    if (!premultiplyAlpha) {
+        AImageDecoder_setUnpremultipliedRequired(decoder, true);
+    }
+
     const size_t size = outInfo->stride * outInfo->height;
     void* pixels = malloc(size);
     int result = AImageDecoder_decodeImage(decoder, pixels, outInfo->stride, size);
@@ -190,13 +281,14 @@
 }
 
 status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets,
-        const char* name) {
+        const char* name, bool premultiplyAlpha) {
     Asset* asset = assets.open(name, Asset::ACCESS_BUFFER);
     if (asset == nullptr)
         return NO_INIT;
 
     AndroidBitmapInfo bitmapInfo;
-    void* pixels = decodeImage(asset->getBuffer(false), asset->getLength(), &bitmapInfo);
+    void* pixels = decodeImage(asset->getBuffer(false), asset->getLength(), &bitmapInfo,
+        premultiplyAlpha);
     auto pixelDeleter = std::unique_ptr<void, decltype(free)*>{ pixels, free };
 
     asset->close();
@@ -209,7 +301,6 @@
     const int w = bitmapInfo.width;
     const int h = bitmapInfo.height;
 
-    GLint crop[4] = { 0, h, w, -h };
     texture->w = w;
     texture->h = h;
 
@@ -237,18 +328,19 @@
             break;
     }
 
-    glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
-    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
-    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 
     return NO_ERROR;
 }
 
-status_t BootAnimation::initTexture(FileMap* map, int* width, int* height) {
+status_t BootAnimation::initTexture(FileMap* map, int* width, int* height,
+    bool premultiplyAlpha) {
     AndroidBitmapInfo bitmapInfo;
-    void* pixels = decodeImage(map->getDataPtr(), map->getDataLength(), &bitmapInfo);
+    void* pixels = decodeImage(map->getDataPtr(), map->getDataLength(), &bitmapInfo,
+        premultiplyAlpha);
     auto pixelDeleter = std::unique_ptr<void, decltype(free)*>{ pixels, free };
 
     // FileMap memory is never released until application exit.
@@ -263,7 +355,6 @@
     const int w = bitmapInfo.width;
     const int h = bitmapInfo.height;
 
-    GLint crop[4] = { 0, h, w, -h };
     int tw = 1 << (31 - __builtin_clz(w));
     int th = 1 << (31 - __builtin_clz(h));
     if (tw < w) tw <<= 1;
@@ -297,7 +388,10 @@
             break;
     }
 
-    glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 
     *width = w;
     *height = h;
@@ -470,7 +564,9 @@
     eglInitialize(display, nullptr, nullptr);
     EGLConfig config = getEglConfig(display);
     EGLSurface surface = eglCreateWindowSurface(display, config, s.get(), nullptr);
-    EGLContext context = eglCreateContext(display, config, nullptr, nullptr);
+    // Initialize egl context with client version number 2.0.
+    EGLint contextAttributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
+    EGLContext context = eglCreateContext(display, config, nullptr, contextAttributes);
     EGLint w, h;
     eglQuerySurface(display, surface, EGL_WIDTH, &w);
     eglQuerySurface(display, surface, EGL_HEIGHT, &h);
@@ -503,11 +599,6 @@
 void BootAnimation::projectSceneToWindow() {
     glViewport(0, 0, mWidth, mHeight);
     glScissor(0, 0, mWidth, mHeight);
-    glMatrixMode(GL_PROJECTION);
-    glLoadIdentity();
-    glOrthof(0, static_cast<float>(mWidth), 0, static_cast<float>(mHeight), -1, 1);
-    glMatrixMode(GL_MODELVIEW);
-    glLoadIdentity();
 }
 
 void BootAnimation::resizeSurface(int newWidth, int newHeight) {
@@ -600,8 +691,71 @@
     }
 }
 
+GLuint compileShader(GLenum shaderType, const GLchar *source) {
+    GLuint shader = glCreateShader(shaderType);
+    glShaderSource(shader, 1, &source, 0);
+    glCompileShader(shader);
+    GLint isCompiled = 0;
+    glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
+    if (isCompiled == GL_FALSE) {
+        SLOGE("Compile shader failed. Shader type: %d", shaderType);
+        return 0;
+    }
+    return shader;
+}
+
+GLuint linkShader(GLuint vertexShader, GLuint fragmentShader) {
+    GLuint program = glCreateProgram();
+    glAttachShader(program, vertexShader);
+    glAttachShader(program, fragmentShader);
+    glLinkProgram(program);
+    GLint isLinked = 0;
+    glGetProgramiv(program, GL_LINK_STATUS, (int *)&isLinked);
+    if (isLinked == GL_FALSE) {
+        SLOGE("Linking shader failed. Shader handles: vert %d, frag %d",
+            vertexShader, fragmentShader);
+        return 0;
+    }
+    return program;
+}
+
+void BootAnimation::initShaders() {
+    bool dynamicColoringEnabled = mAnimation != nullptr && mAnimation->dynamicColoringEnabled;
+    GLuint vertexShader = compileShader(GL_VERTEX_SHADER, (const GLchar *)VERTEX_SHADER_SOURCE);
+    GLuint imageFragmentShader =
+        compileShader(GL_FRAGMENT_SHADER, dynamicColoringEnabled
+            ? (const GLchar *)IMAGE_FRAG_DYNAMIC_COLORING_SHADER_SOURCE
+            : (const GLchar *)IMAGE_FRAG_SHADER_SOURCE);
+    GLuint textFragmentShader =
+        compileShader(GL_FRAGMENT_SHADER, (const GLchar *)TEXT_FRAG_SHADER_SOURCE);
+
+    // Initialize image shader.
+    mImageShader = linkShader(vertexShader, imageFragmentShader);
+    GLint positionLocation = glGetAttribLocation(mImageShader, A_POSITION);
+    GLint uvLocation = glGetAttribLocation(mImageShader, A_UV);
+    mImageTextureLocation = glGetUniformLocation(mImageShader, U_TEXTURE);
+    mImageFadeLocation = glGetUniformLocation(mImageShader, U_FADE);
+    glEnableVertexAttribArray(positionLocation);
+    glVertexAttribPointer(positionLocation, 2,  GL_FLOAT, GL_FALSE, 0, quadPositions);
+    glVertexAttribPointer(uvLocation, 2, GL_FLOAT, GL_FALSE, 0, quadUVs);
+    glEnableVertexAttribArray(uvLocation);
+
+    // Initialize text shader.
+    mTextShader = linkShader(vertexShader, textFragmentShader);
+    positionLocation = glGetAttribLocation(mTextShader, A_POSITION);
+    uvLocation = glGetAttribLocation(mTextShader, A_UV);
+    mTextTextureLocation = glGetUniformLocation(mTextShader, U_TEXTURE);
+    mTextCropAreaLocation = glGetUniformLocation(mTextShader, U_CROP_AREA);
+    glEnableVertexAttribArray(positionLocation);
+    glVertexAttribPointer(positionLocation, 2,  GL_FLOAT, GL_FALSE, 0, quadPositions);
+    glVertexAttribPointer(uvLocation, 2, GL_FLOAT, GL_FALSE, 0, quadUVs);
+    glEnableVertexAttribArray(uvLocation);
+}
+
 bool BootAnimation::threadLoop() {
     bool result;
+    initShaders();
+
     // We have no bootanimation file, so we use the stock android logo
     // animation.
     if (mZipFileName.isEmpty()) {
@@ -623,6 +777,8 @@
 }
 
 bool BootAnimation::android() {
+    glActiveTexture(GL_TEXTURE0);
+
     SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
             elapsedRealtime());
     initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");
@@ -631,19 +787,14 @@
     mCallbacks->init({});
 
     // clear screen
-    glShadeModel(GL_FLAT);
     glDisable(GL_DITHER);
     glDisable(GL_SCISSOR_TEST);
     glClearColor(0,0,0,1);
     glClear(GL_COLOR_BUFFER_BIT);
     eglSwapBuffers(mDisplay, mSurface);
 
-    glEnable(GL_TEXTURE_2D);
-    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
-
     // Blend state
     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
 
     const nsecs_t startTime = systemTime();
     do {
@@ -666,12 +817,12 @@
         glEnable(GL_SCISSOR_TEST);
         glDisable(GL_BLEND);
         glBindTexture(GL_TEXTURE_2D, mAndroid[1].name);
-        glDrawTexiOES(x,                 yc, 0, mAndroid[1].w, mAndroid[1].h);
-        glDrawTexiOES(x + mAndroid[1].w, yc, 0, mAndroid[1].w, mAndroid[1].h);
+        drawTexturedQuad(x,                 yc, mAndroid[1].w, mAndroid[1].h);
+        drawTexturedQuad(x + mAndroid[1].w, yc, mAndroid[1].w, mAndroid[1].h);
 
         glEnable(GL_BLEND);
         glBindTexture(GL_TEXTURE_2D, mAndroid[0].name);
-        glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h);
+        drawTexturedQuad(xc, yc, mAndroid[0].w, mAndroid[0].h);
 
         EGLBoolean res = eglSwapBuffers(mDisplay, mSurface);
         if (res == EGL_FALSE)
@@ -766,6 +917,20 @@
     return true;
 }
 
+// Parse a color represented as a signed decimal int string.
+// E.g. "-2757722" (whose hex 2's complement is 0xFFD5EBA6).
+// If the input color string is empty, set color with values in defaultColor.
+static void parseColorDecimalString(const std::string& colorString,
+    float color[3], float defaultColor[3]) {
+    if (colorString == "") {
+        memcpy(color, defaultColor, sizeof(float) * 3);
+        return;
+    }
+    int colorInt = atoi(colorString.c_str());
+    color[0] = ((float)((colorInt >> 16) & 0xFF)) / 0xFF; // r
+    color[1] = ((float)((colorInt >> 8) & 0xFF)) / 0xFF; // g
+    color[2] = ((float)(colorInt & 0xFF)) / 0xFF; // b
+}
 
 static bool readFile(ZipFileRO* zip, const char* name, String8& outString) {
     ZipEntryRO entry = zip->findEntryByName(name);
@@ -798,10 +963,10 @@
 
         status = initTexture(font->map, &font->texture.w, &font->texture.h);
 
-        glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-        glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-        glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
-        glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
     } else if (fallback != nullptr) {
         status = initTexture(&font->texture, mAssets, fallback);
     } else {
@@ -816,40 +981,11 @@
     return status;
 }
 
-void BootAnimation::fadeFrame(const int frameLeft, const int frameBottom, const int frameWidth,
-                              const int frameHeight, const Animation::Part& part,
-                              const int fadedFramesCount) {
-    glEnable(GL_BLEND);
-    glEnableClientState(GL_VERTEX_ARRAY);
-    glDisable(GL_TEXTURE_2D);
-    // avoid creating a hole due to mixing result alpha with GL_REPLACE texture
-    glBlendFuncSeparateOES(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
-
-    const float alpha = static_cast<float>(fadedFramesCount) / part.framesToFadeCount;
-    glColor4f(part.backgroundColor[0], part.backgroundColor[1], part.backgroundColor[2], alpha);
-
-    const float frameStartX = static_cast<float>(frameLeft);
-    const float frameStartY = static_cast<float>(frameBottom);
-    const float frameEndX = frameStartX + frameWidth;
-    const float frameEndY = frameStartY + frameHeight;
-    const GLfloat frameRect[] = {
-        frameStartX, frameStartY,
-        frameEndX,   frameStartY,
-        frameEndX,   frameEndY,
-        frameStartX, frameEndY
-    };
-    glVertexPointer(2, GL_FLOAT, 0, frameRect);
-    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
-
-    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-    glEnable(GL_TEXTURE_2D);
-    glDisableClientState(GL_VERTEX_ARRAY);
-    glDisable(GL_BLEND);
-}
-
 void BootAnimation::drawText(const char* str, const Font& font, bool bold, int* x, int* y) {
     glEnable(GL_BLEND);  // Allow us to draw on top of the animation
     glBindTexture(GL_TEXTURE_2D, font.texture.name);
+    glUseProgram(mTextShader);
+    glUniform1i(mTextTextureLocation, 0);
 
     const int len = strlen(str);
     const int strWidth = font.char_width * len;
@@ -865,8 +1001,6 @@
         *y = mHeight + *y - font.char_height;
     }
 
-    int cropRect[4] = { 0, 0, font.char_width, -font.char_height };
-
     for (int i = 0; i < len; i++) {
         char c = str[i];
 
@@ -878,13 +1012,13 @@
         const int charPos = (c - FONT_BEGIN_CHAR);  // Position in the list of valid characters
         const int row = charPos / FONT_NUM_COLS;
         const int col = charPos % FONT_NUM_COLS;
-        cropRect[0] = col * font.char_width;  // Left of column
-        cropRect[1] = row * font.char_height * 2; // Top of row
-        // Move down to bottom of regular (one char_heigh) or bold (two char_heigh) line
-        cropRect[1] += bold ? 2 * font.char_height : font.char_height;
-        glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, cropRect);
-
-        glDrawTexiOES(*x, *y, 0, font.char_width, font.char_height);
+        // Bold fonts are expected in the second half of each row.
+        float v0 = (row + (bold ? 0.5f : 0.0f)) / FONT_NUM_ROWS;
+        float u0 = ((float)col) / FONT_NUM_COLS;
+        float v1 = v0 + 1.0f / FONT_NUM_ROWS / 2;
+        float u1 = u0 + 1.0f / FONT_NUM_COLS;
+        glUniform4f(mTextCropAreaLocation, u0, v0, u1, v1);
+        drawTexturedQuad(*x, *y, font.char_width, font.char_height);
 
         *x += font.char_width;
     }
@@ -938,6 +1072,8 @@
         return false;
     }
     char const* s = desString.string();
+    std::string dynamicColoringPartName = "";
+    bool postDynamicColoring = false;
 
     // Parse the description file
     for (;;) {
@@ -952,11 +1088,19 @@
         int pause = 0;
         int progress = 0;
         int framesToFadeCount = 0;
+        int colorTransitionStart = 0;
+        int colorTransitionEnd = 0;
         char path[ANIM_ENTRY_NAME_MAX];
         char color[7] = "000000"; // default to black if unspecified
         char clockPos1[TEXT_POS_LEN_MAX + 1] = "";
         char clockPos2[TEXT_POS_LEN_MAX + 1] = "";
+        char dynamicColoringPartNameBuffer[ANIM_ENTRY_NAME_MAX];
         char pathType;
+        // start colors default to black if unspecified
+        char start_color_0[7] = "000000";
+        char start_color_1[7] = "000000";
+        char start_color_2[7] = "000000";
+        char start_color_3[7] = "000000";
 
         int nextReadPos;
 
@@ -971,6 +1115,18 @@
             } else {
               animation.progressEnabled = false;
             }
+        } else if (sscanf(l, "dynamic_colors %" STRTO(ANIM_PATH_MAX) "s #%6s #%6s #%6s #%6s %d %d",
+            dynamicColoringPartNameBuffer,
+            start_color_0, start_color_1, start_color_2, start_color_3,
+            &colorTransitionStart, &colorTransitionEnd)) {
+            animation.dynamicColoringEnabled = true;
+            parseColor(start_color_0, animation.startColors[0]);
+            parseColor(start_color_1, animation.startColors[1]);
+            parseColor(start_color_2, animation.startColors[2]);
+            parseColor(start_color_3, animation.startColors[3]);
+            animation.colorTransitionStart = colorTransitionStart;
+            animation.colorTransitionEnd = colorTransitionEnd;
+            dynamicColoringPartName = std::string(dynamicColoringPartNameBuffer);
         } else if (sscanf(l, "%c %d %d %" STRTO(ANIM_PATH_MAX) "s%n",
                           &pathType, &count, &pause, path, &nextReadPos) >= 4) {
             if (pathType == 'f') {
@@ -983,6 +1139,16 @@
             //       "clockPos1=%s, clockPos2=%s",
             //       pathType, count, pause, path, framesToFadeCount, color, clockPos1, clockPos2);
             Animation::Part part;
+            if (path == dynamicColoringPartName) {
+                // Part is specified to use dynamic coloring.
+                part.useDynamicColoring = true;
+                part.postDynamicColoring = false;
+                postDynamicColoring = true;
+            } else {
+                // Part does not use dynamic coloring.
+                part.useDynamicColoring = false;
+                part.postDynamicColoring =  postDynamicColoring;
+            }
             part.playUntilComplete = pathType == 'c';
             part.framesToFadeCount = framesToFadeCount;
             part.count = count;
@@ -1166,19 +1332,16 @@
 
     // Blend required to draw time on top of animation frames.
     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-    glShadeModel(GL_FLAT);
     glDisable(GL_DITHER);
     glDisable(GL_SCISSOR_TEST);
     glDisable(GL_BLEND);
 
-    glBindTexture(GL_TEXTURE_2D, 0);
     glEnable(GL_TEXTURE_2D);
-    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
-    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
-    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
-    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
+    glBindTexture(GL_TEXTURE_2D, 0);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
     bool clockFontInitialized = false;
     if (mClockEnabled) {
         clockFontInitialized =
@@ -1193,6 +1356,10 @@
         mTimeCheckThread->run("BootAnimation::TimeCheckThread", PRIORITY_NORMAL);
     }
 
+    if (mAnimation != nullptr && mAnimation->dynamicColoringEnabled) {
+        initDynamicColors();
+    }
+
     playAnimation(*mAnimation);
 
     if (mTimeCheckThread != nullptr) {
@@ -1218,6 +1385,55 @@
         (lastDisplayedProgress == 0 || lastDisplayedProgress == 100);
 }
 
+// Linear mapping from range <a1, a2> to range <b1, b2>
+float mapLinear(float x, float a1, float a2, float b1, float b2) {
+    return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );
+}
+
+void BootAnimation::drawTexturedQuad(float xStart, float yStart, float width, float height) {
+    // Map coordinates from screen space to world space.
+    float x0 = mapLinear(xStart, 0, mWidth, -1, 1);
+    float y0 = mapLinear(yStart, 0, mHeight, -1, 1);
+    float x1 = mapLinear(xStart + width, 0, mWidth, -1, 1);
+    float y1 = mapLinear(yStart + height, 0, mHeight, -1, 1);
+    // Update quad vertex positions.
+    quadPositions[0] = x0;
+    quadPositions[1] = y0;
+    quadPositions[2] = x1;
+    quadPositions[3] = y0;
+    quadPositions[4] = x1;
+    quadPositions[5] = y1;
+    quadPositions[6] = x1;
+    quadPositions[7] = y1;
+    quadPositions[8] = x0;
+    quadPositions[9] = y1;
+    quadPositions[10] = x0;
+    quadPositions[11] = y0;
+    glDrawArrays(GL_TRIANGLES, 0,
+        sizeof(quadPositions) / sizeof(quadPositions[0]) / 2);
+}
+
+void BootAnimation::initDynamicColors() {
+    for (int i = 0; i < DYNAMIC_COLOR_COUNT; i++) {
+        parseColorDecimalString(
+            android::base::GetProperty("persist.bootanim.color" + std::to_string(i + 1), ""),
+            mAnimation->endColors[i], mAnimation->startColors[i]);
+    }
+    glUseProgram(mImageShader);
+    SLOGI("[BootAnimation] Dynamically coloring boot animation.");
+    for (int i = 0; i < DYNAMIC_COLOR_COUNT; i++) {
+        float *startColor = mAnimation->startColors[i];
+        float *endColor = mAnimation->endColors[i];
+        glUniform4f(glGetUniformLocation(mImageShader,
+            (U_START_COLOR_PREFIX + std::to_string(i)).c_str()),
+            startColor[0], startColor[1], startColor[2], 1 /* alpha */);
+        glUniform4f(glGetUniformLocation(mImageShader,
+            (U_END_COLOR_PREFIX + std::to_string(i)).c_str()),
+            endColor[0], endColor[1], endColor[2], 1 /* alpha */);
+    }
+    mImageColorProgressLocation = glGetUniformLocation(mImageShader, U_COLOR_PROGRESS);
+}
+
 bool BootAnimation::playAnimation(const Animation& animation) {
     const size_t pcount = animation.parts.size();
     nsecs_t frameDuration = s2ns(1) / animation.fps;
@@ -1230,7 +1446,6 @@
     for (size_t i=0 ; i<pcount ; i++) {
         const Animation::Part& part(animation.parts[i]);
         const size_t fcount = part.frames.size();
-        glBindTexture(GL_TEXTURE_2D, 0);
 
         // Handle animation package
         if (part.animation != nullptr) {
@@ -1261,6 +1476,19 @@
             for (size_t j=0 ; j<fcount ; j++) {
                 if (shouldStopPlayingPart(part, fadedFramesCount, lastDisplayedProgress)) break;
 
+                // Color progress is
+                // - the animation progress, normalized from
+                //   [colorTransitionStart,colorTransitionEnd] to [0, 1] for the dynamic coloring
+                //   part.
+                // - 0 for parts that come before,
+                // - 1 for parts that come after.
+                float colorProgress = part.useDynamicColoring
+                    ? fmin(fmax(
+                        ((float)j - animation.colorTransitionStart) /
+                            fmax(animation.colorTransitionEnd -
+                                animation.colorTransitionStart, 1.0f), 0.0f), 1.0f)
+                    : (part.postDynamicColoring ? 1 : 0);
+
                 processDisplayEvents();
 
                 const int animationX = (mWidth - animation.width) / 2;
@@ -1272,44 +1500,38 @@
                 if (r > 0) {
                     glBindTexture(GL_TEXTURE_2D, frame.tid);
                 } else {
-                    if (part.count != 1) {
-                        glGenTextures(1, &frame.tid);
-                        glBindTexture(GL_TEXTURE_2D, frame.tid);
-                        glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-                        glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-                    }
+                    glGenTextures(1, &frame.tid);
+                    glBindTexture(GL_TEXTURE_2D, frame.tid);
                     int w, h;
-                    initTexture(frame.map, &w, &h);
+                    // Set decoding option to alpha unpremultiplied so that the R, G, B channels
+                    // of transparent pixels are preserved.
+                    initTexture(frame.map, &w, &h, false /* don't premultiply alpha */);
                 }
 
                 const int xc = animationX + frame.trimX;
                 const int yc = animationY + frame.trimY;
-                Region clearReg(Rect(mWidth, mHeight));
-                clearReg.subtractSelf(Rect(xc, yc, xc+frame.trimWidth, yc+frame.trimHeight));
-                if (!clearReg.isEmpty()) {
-                    Region::const_iterator head(clearReg.begin());
-                    Region::const_iterator tail(clearReg.end());
-                    glEnable(GL_SCISSOR_TEST);
-                    while (head != tail) {
-                        const Rect& r2(*head++);
-                        glScissor(r2.left, mHeight - r2.bottom, r2.width(), r2.height());
-                        glClear(GL_COLOR_BUFFER_BIT);
-                    }
-                    glDisable(GL_SCISSOR_TEST);
-                }
+                glClear(GL_COLOR_BUFFER_BIT);
                 // specify the y center as ceiling((mHeight - frame.trimHeight) / 2)
                 // which is equivalent to mHeight - (yc + frame.trimHeight)
                 const int frameDrawY = mHeight - (yc + frame.trimHeight);
-                glDrawTexiOES(xc, frameDrawY, 0, frame.trimWidth, frame.trimHeight);
 
+                float fade = 0;
                 // if the part hasn't been stopped yet then continue fading if necessary
                 if (exitPending() && part.hasFadingPhase()) {
-                    fadeFrame(xc, frameDrawY, frame.trimWidth, frame.trimHeight, part,
-                              ++fadedFramesCount);
+                    fade = static_cast<float>(++fadedFramesCount) / part.framesToFadeCount;
                     if (fadedFramesCount >= part.framesToFadeCount) {
                         fadedFramesCount = MAX_FADED_FRAMES_COUNT; // no more fading
                     }
                 }
+                glUseProgram(mImageShader);
+                glUniform1i(mImageTextureLocation, 0);
+                glUniform1f(mImageFadeLocation, fade);
+                if (animation.dynamicColoringEnabled) {
+                    glUniform1f(mImageColorProgressLocation, colorProgress);
+                }
+                glEnable(GL_BLEND);
+                drawTexturedQuad(xc, frameDrawY, frame.trimWidth, frame.trimHeight);
+                glDisable(GL_BLEND);
 
                 if (mClockEnabled && mTimeIsAccurate && validClock(part)) {
                     drawClock(animation.clockFont, part.clockPosX, part.clockPosY);
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index f8a31c6..7a597da 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -31,7 +31,7 @@
 #include <binder/IBinder.h>
 
 #include <EGL/egl.h>
-#include <GLES/gl.h>
+#include <GLES2/gl2.h>
 
 namespace android {
 
@@ -53,7 +53,7 @@
     };
 
     struct Font {
-        FileMap* map;
+        FileMap* map = nullptr;
         Texture texture;
         int char_width;
         int char_height;
@@ -62,7 +62,7 @@
     struct Animation {
         struct Frame {
             String8 name;
-            FileMap* map;
+            FileMap* map = nullptr;
             int trimX;
             int trimY;
             int trimWidth;
@@ -90,6 +90,10 @@
             uint8_t* audioData;
             int audioLength;
             Animation* animation;
+            // Controls if dynamic coloring is enabled for this part.
+            bool useDynamicColoring = false;
+            // Defines if this part is played after the dynamic coloring part.
+            bool postDynamicColoring = false;
 
             bool hasFadingPhase() const {
                 return !playUntilComplete && framesToFadeCount > 0;
@@ -105,6 +109,12 @@
         ZipFileRO* zip;
         Font clockFont;
         Font progressFont;
+         // Controls if dynamic coloring is enabled for the whole animation.
+        bool dynamicColoringEnabled = false;
+        int colorTransitionStart = 0; // Start frame of dynamic color transition.
+        int colorTransitionEnd = 0; // End frame of dynamic color transition.
+        float startColors[4][3]; // Start colors of dynamic color transition.
+        float endColors[4][3];   // End colors of dynamic color transition.
     };
 
     // All callbacks will be called from this class's internal thread.
@@ -163,9 +173,12 @@
     int displayEventCallback(int fd, int events, void* data);
     void processDisplayEvents();
 
-    status_t initTexture(Texture* texture, AssetManager& asset, const char* name);
-    status_t initTexture(FileMap* map, int* width, int* height);
+    status_t initTexture(Texture* texture, AssetManager& asset, const char* name,
+        bool premultiplyAlpha = true);
+    status_t initTexture(FileMap* map, int* width, int* height,
+        bool premultiplyAlpha = true);
     status_t initFont(Font* font, const char* fallback);
+    void initShaders();
     bool android();
     bool movie();
     void drawText(const char* str, const Font& font, bool bold, int* x, int* y);
@@ -173,6 +186,7 @@
     void drawProgress(int percent, const Font& font, const int xPos, const int yPos);
     void fadeFrame(int frameLeft, int frameBottom, int frameWidth, int frameHeight,
                    const Animation::Part& part, int fadedFramesCount);
+    void drawTexturedQuad(float xStart, float yStart, float width, float height);
     bool validClock(const Animation::Part& part);
     Animation* loadAnimation(const String8&);
     bool playAnimation(const Animation&);
@@ -192,6 +206,7 @@
     void checkExit();
 
     void handleViewport(nsecs_t timestep);
+    void initDynamicColors();
 
     sp<SurfaceComposerClient>       mSession;
     AssetManager mAssets;
@@ -218,6 +233,13 @@
     sp<TimeCheckThread> mTimeCheckThread = nullptr;
     sp<Callbacks> mCallbacks;
     Animation* mAnimation = nullptr;
+    GLuint mImageShader;
+    GLuint mTextShader;
+    GLuint mImageFadeLocation;
+    GLuint mImageTextureLocation;
+    GLuint mTextCropAreaLocation;
+    GLuint mTextTextureLocation;
+    GLuint mImageColorProgressLocation;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/cmds/bootanimation/OWNERS b/cmds/bootanimation/OWNERS
new file mode 100644
index 0000000..b6fb007
--- /dev/null
+++ b/cmds/bootanimation/OWNERS
@@ -0,0 +1,3 @@
+dupin@google.com
+shanh@google.com
+jreck@google.com
diff --git a/core/api/current.txt b/core/api/current.txt
index 03d5835..3ddb374 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -2012,6 +2012,9 @@
   public static final class R.id {
     ctor public R.id();
     field public static final int accessibilityActionContextClick = 16908348; // 0x102003c
+    field public static final int accessibilityActionDragCancel;
+    field public static final int accessibilityActionDragDrop;
+    field public static final int accessibilityActionDragStart;
     field public static final int accessibilityActionHideTooltip = 16908357; // 0x1020045
     field public static final int accessibilityActionImeEnter = 16908372; // 0x1020054
     field public static final int accessibilityActionMoveWindow = 16908354; // 0x1020042
@@ -50637,6 +50640,9 @@
     method public void setPackageName(CharSequence);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 4; // 0x4
+    field public static final int CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 512; // 0x200
+    field public static final int CONTENT_CHANGE_TYPE_DRAG_DROPPED = 256; // 0x100
+    field public static final int CONTENT_CHANGE_TYPE_DRAG_STARTED = 128; // 0x80
     field public static final int CONTENT_CHANGE_TYPE_PANE_APPEARED = 16; // 0x10
     field public static final int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 32; // 0x20
     field public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 8; // 0x8
@@ -50935,6 +50941,9 @@
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_COPY;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_CUT;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_DISMISS;
+    field @NonNull public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_DRAG_CANCEL;
+    field @NonNull public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_DRAG_DROP;
+    field @NonNull public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_DRAG_START;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_EXPAND;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_FOCUS;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_HIDE_TOOLTIP;
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 2d73aa67..2846b36 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3262,11 +3262,13 @@
   public final class DisplayManager {
     method @RequiresPermission(android.Manifest.permission.ACCESS_AMBIENT_LIGHT_STATS) public java.util.List<android.hardware.display.AmbientBrightnessDayStats> getAmbientBrightnessStats();
     method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public android.hardware.display.BrightnessConfiguration getBrightnessConfiguration();
+    method @Nullable @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public android.hardware.display.BrightnessConfiguration getBrightnessConfigurationForDisplay(@NonNull String);
     method @RequiresPermission(android.Manifest.permission.BRIGHTNESS_SLIDER_USAGE) public java.util.List<android.hardware.display.BrightnessChangeEvent> getBrightnessEvents();
     method @Nullable @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public android.hardware.display.BrightnessConfiguration getDefaultBrightnessConfiguration();
     method public android.util.Pair<float[],float[]> getMinimumBrightnessCurve();
     method public android.graphics.Point getStableDisplaySize();
     method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration);
+    method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfigurationForDisplay(@NonNull android.hardware.display.BrightnessConfiguration, @NonNull String);
     method @Deprecated @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_SATURATION) public void setSaturationLevel(float);
   }
 
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index df670f4..0732c61 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1133,10 +1133,10 @@
 package android.hardware.devicestate {
 
   public final class DeviceStateManager {
-    method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void cancelRequest(@NonNull android.hardware.devicestate.DeviceStateRequest);
+    method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void cancelRequest(@NonNull android.hardware.devicestate.DeviceStateRequest);
     method @NonNull public int[] getSupportedStates();
     method public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
-    method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void requestState(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
+    method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void requestState(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
     method public void unregisterCallback(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
     field public static final int MAXIMUM_DEVICE_STATE = 255; // 0xff
     field public static final int MINIMUM_DEVICE_STATE = 0; // 0x0
@@ -3183,7 +3183,6 @@
 
   public final class SplashScreenView extends android.widget.FrameLayout {
     method @Nullable public android.view.View getBrandingView();
-    method @ColorInt public int getIconBackgroundColor();
   }
 
   public final class StartingWindowInfo implements android.os.Parcelable {
@@ -3324,7 +3323,7 @@
     ctor public WindowProviderService();
     method public final void attachToWindowToken(@NonNull android.os.IBinder);
     method @NonNull public int getInitialDisplayId();
-    method @Nullable public android.os.Bundle getWindowContextOptions();
+    method @CallSuper @Nullable public android.os.Bundle getWindowContextOptions();
     method public abstract int getWindowType();
   }
 
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java
index 2efdf51..db7ab1a 100644
--- a/core/java/android/app/ActivityClient.java
+++ b/core/java/android/app/ActivityClient.java
@@ -207,7 +207,8 @@
     }
 
     /**
-     * Returns the activity token below in the same task if it belongs to the same process.
+     * Returns the non-finishing activity token below in the same task if it belongs to the same
+     * process.
      */
     @Nullable
     public IBinder getActivityTokenBelow(IBinder activityToken) {
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 8e1f263..76f8731 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -215,6 +215,14 @@
             "android.activity.launchRootTaskToken";
 
     /**
+     * The {@link com.android.server.wm.TaskFragment} token the activity should be launched into.
+     * @see #setLaunchTaskFragmentToken(IBinder)
+     * @hide
+     */
+    public static final String KEY_LAUNCH_TASK_FRAGMENT_TOKEN =
+            "android.activity.launchTaskFragmentToken";
+
+    /**
      * The windowing mode the activity should be launched into.
      * @hide
      */
@@ -396,6 +404,7 @@
     private int mCallerDisplayId = INVALID_DISPLAY;
     private WindowContainerToken mLaunchTaskDisplayArea;
     private WindowContainerToken mLaunchRootTask;
+    private IBinder mLaunchTaskFragmentToken;
     @WindowConfiguration.WindowingMode
     private int mLaunchWindowingMode = WINDOWING_MODE_UNDEFINED;
     @WindowConfiguration.ActivityType
@@ -1138,6 +1147,7 @@
         mCallerDisplayId = opts.getInt(KEY_CALLER_DISPLAY_ID, INVALID_DISPLAY);
         mLaunchTaskDisplayArea = opts.getParcelable(KEY_LAUNCH_TASK_DISPLAY_AREA_TOKEN);
         mLaunchRootTask = opts.getParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN);
+        mLaunchTaskFragmentToken = opts.getBinder(KEY_LAUNCH_TASK_FRAGMENT_TOKEN);
         mLaunchWindowingMode = opts.getInt(KEY_LAUNCH_WINDOWING_MODE, WINDOWING_MODE_UNDEFINED);
         mLaunchActivityType = opts.getInt(KEY_LAUNCH_ACTIVITY_TYPE, ACTIVITY_TYPE_UNDEFINED);
         mLaunchTaskId = opts.getInt(KEY_LAUNCH_TASK_ID, -1);
@@ -1473,6 +1483,17 @@
     }
 
     /** @hide */
+    public IBinder getLaunchTaskFragmentToken() {
+        return mLaunchTaskFragmentToken;
+    }
+
+    /** @hide */
+    public ActivityOptions setLaunchTaskFragmentToken(IBinder taskFragmentToken) {
+        mLaunchTaskFragmentToken = taskFragmentToken;
+        return this;
+    }
+
+    /** @hide */
     public int getLaunchWindowingMode() {
         return mLaunchWindowingMode;
     }
@@ -1882,6 +1903,9 @@
         if (mLaunchRootTask != null) {
             b.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, mLaunchRootTask);
         }
+        if (mLaunchTaskFragmentToken != null) {
+            b.putBinder(KEY_LAUNCH_TASK_FRAGMENT_TOKEN, mLaunchTaskFragmentToken);
+        }
         if (mLaunchWindowingMode != WINDOWING_MODE_UNDEFINED) {
             b.putInt(KEY_LAUNCH_WINDOWING_MODE, mLaunchWindowingMode);
         }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index e3abe4d..ae812ec 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -371,11 +371,12 @@
     @UnsupportedAppUsage(trackingBug = 176961850, maxTargetSdk = Build.VERSION_CODES.R,
             publicAlternatives = "Use {@code Context#getResources()#getConfiguration()} instead.")
     Configuration mConfiguration;
+    @GuardedBy("this")
+    private boolean mUpdateHttpProxyOnBind = false;
     @UnsupportedAppUsage
     Application mInitialApplication;
     @UnsupportedAppUsage
-    final ArrayList<Application> mAllApplications
-            = new ArrayList<Application>();
+    final ArrayList<Application> mAllApplications = new ArrayList<>();
     /**
      * Bookkeeping of instantiated backup agents indexed first by user id, then by package name.
      * Indexing by user id supports parallel backups across users on system packages as they run in
@@ -529,6 +530,9 @@
         // A reusable token for other purposes, e.g. content capture, translation. It shouldn't be
         // used without security checks
         public IBinder shareableActivityToken;
+        // The token of the initial TaskFragment that embedded this activity. Do not rely on it
+        // after creation because the activity could be reparented.
+        @Nullable public IBinder mInitialTaskFragmentToken;
         int ident;
         @UnsupportedAppUsage
         Intent intent;
@@ -622,7 +626,8 @@
                 List<ReferrerIntent> pendingNewIntents, ActivityOptions activityOptions,
                 boolean isForward, ProfilerInfo profilerInfo, ClientTransactionHandler client,
                 IBinder assistToken, FixedRotationAdjustments fixedRotationAdjustments,
-                IBinder shareableActivityToken, boolean launchedFromBubble) {
+                IBinder shareableActivityToken, boolean launchedFromBubble,
+                IBinder initialTaskFragmentToken) {
             this.token = token;
             this.assistToken = assistToken;
             this.shareableActivityToken = shareableActivityToken;
@@ -644,6 +649,7 @@
             mActivityOptions = activityOptions;
             mPendingFixedRotationAdjustments = fixedRotationAdjustments;
             mLaunchedFromBubble = launchedFromBubble;
+            mInitialTaskFragmentToken = initialTaskFragmentToken;
             init();
         }
 
@@ -1189,8 +1195,18 @@
         }
 
         public void updateHttpProxy() {
-            ActivityThread.updateHttpProxy(
-                    getApplication() != null ? getApplication() : getSystemContext());
+            final Application app;
+            synchronized (ActivityThread.this) {
+                app = getApplication();
+                if (null == app) {
+                    // The app is not bound yet. Make a note to update the HTTP proxy when the
+                    // app is bound.
+                    mUpdateHttpProxyOnBind = true;
+                    return;
+                }
+            }
+            // App is present, update the proxy inline.
+            ActivityThread.updateHttpProxy(app);
         }
 
         public void processInBackground() {
@@ -5651,8 +5667,8 @@
      */
     private void scheduleRelaunchActivityIfPossible(@NonNull ActivityClientRecord r,
             boolean preserveWindow) {
-        if (r.activity.mFinished || r.token instanceof Binder) {
-            // Do not schedule relaunch if the activity is finishing or not a local object (e.g.
+        if ((r.activity != null && r.activity.mFinished) || r.token instanceof Binder) {
+            // Do not schedule relaunch if the activity is finishing or is a local object (e.g.
             // created by ActivtiyGroup that server side doesn't recognize it).
             return;
         }
@@ -6657,6 +6673,15 @@
             sendMessage(H.SET_CONTENT_CAPTURE_OPTIONS_CALLBACK, data.appInfo.packageName);
 
             mInitialApplication = app;
+            final boolean updateHttpProxy;
+            synchronized (this) {
+                updateHttpProxy = mUpdateHttpProxyOnBind;
+                // This synchronized block ensures that any subsequent call to updateHttpProxy()
+                // will see a non-null mInitialApplication.
+            }
+            if (updateHttpProxy) {
+                ActivityThread.updateHttpProxy(app);
+            }
 
             // don't bring up providers in restricted mode; they may depend on the
             // app's custom Application class
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 4ddb546..d932a29 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2849,14 +2849,14 @@
     private static final ThreadLocal<Integer> sBinderThreadCallingUid = new ThreadLocal<>();
 
     /**
-     * If a thread is currently executing a two-way binder transaction, this stores the
-     * ops that were noted blaming any app (the caller, the caller of the caller, etc).
+     * If a thread is currently executing a two-way binder transaction, this stores the op-codes of
+     * the app-ops that were noted during this transaction.
      *
      * @see #getNotedOpCollectionMode
      * @see #collectNotedOpSync
      */
-    private static final ThreadLocal<ArrayMap<String, ArrayMap<String, long[]>>>
-            sAppOpsNotedInThisBinderTransaction = new ThreadLocal<>();
+    private static final ThreadLocal<ArrayMap<String, long[]>> sAppOpsNotedInThisBinderTransaction =
+            new ThreadLocal<>();
 
     /** Whether noting for an appop should be collected */
     private static final @ShouldCollectNoteOp byte[] sAppOpsToNote = new byte[_NUM_OP];
@@ -7205,6 +7205,34 @@
      * @hide
      */
     public interface OnOpStartedListener {
+
+        /**
+         * Represents a start operation that was unsuccessful
+         * @hide
+         */
+        public int START_TYPE_FAILED = 0;
+
+        /**
+         * Represents a successful start operation
+         * @hide
+         */
+        public int START_TYPE_STARTED = 1;
+
+        /**
+         * Represents an operation where a restricted operation became unrestricted, and resumed.
+         * @hide
+         */
+        public int START_TYPE_RESUMED = 2;
+
+        /** @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+            START_TYPE_FAILED,
+            START_TYPE_STARTED,
+            START_TYPE_RESUMED
+        })
+        public @interface StartedType {}
+
         /**
          * Called when an op was started.
          *
@@ -7213,11 +7241,35 @@
          * @param uid The UID performing the operation.
          * @param packageName The package performing the operation.
          * @param attributionTag The attribution tag performing the operation.
-         * @param flags The flags of this op
+         * @param flags The flags of this op.
          * @param result The result of the start.
          */
         void onOpStarted(int op, int uid, String packageName, String attributionTag,
                 @OpFlags int flags, @Mode int result);
+
+        /**
+         * Called when an op was started.
+         *
+         * Note: This is only for op starts. It is not called when an op is noted or stopped.
+         * By default, unless this method is overridden, no code will be executed for resume
+         * events.
+         * @param op The op code.
+         * @param uid The UID performing the operation.
+         * @param packageName The package performing the operation.
+         * @param attributionTag The attribution tag performing the operation.
+         * @param flags The flags of this op.
+         * @param result The result of the start.
+         * @param startType The start type of this start event. Either failed, resumed, or started.
+         * @param attributionFlags The location of this started op in an attribution chain.
+         * @param attributionChainId The ID of the attribution chain of this op, if it is in one.
+         */
+        default void onOpStarted(int op, int uid, String packageName, String attributionTag,
+                @OpFlags int flags, @Mode int result, @StartedType int startType,
+                @AttributionFlags int attributionFlags, int attributionChainId) {
+            if (startType != START_TYPE_RESUMED) {
+                onOpStarted(op, uid, packageName, attributionTag, flags, result);
+            }
+        }
     }
 
     AppOpsManager(Context context, IAppOpsService service) {
@@ -7858,8 +7910,10 @@
              cb = new IAppOpsStartedCallback.Stub() {
                  @Override
                  public void opStarted(int op, int uid, String packageName, String attributionTag,
-                         int flags, int mode) {
-                     callback.onOpStarted(op, uid, packageName, attributionTag, flags, mode);
+                         int flags, int mode, int startType, int attributionFlags,
+                         int attributionChainId) {
+                     callback.onOpStarted(op, uid, packageName, attributionTag, flags, mode,
+                             startType, attributionFlags, attributionChainId);
                  }
              };
              mStartedWatchers.put(callback, cb);
@@ -9052,6 +9106,66 @@
     }
 
     /**
+     * State of a temporarily paused noted app-ops collection.
+     *
+     * @see #pauseNotedAppOpsCollection()
+     *
+     * @hide
+     */
+    public static class PausedNotedAppOpsCollection {
+        final int mUid;
+        final @Nullable ArrayMap<String, long[]> mCollectedNotedAppOps;
+
+        PausedNotedAppOpsCollection(int uid, @Nullable ArrayMap<String,
+                long[]> collectedNotedAppOps) {
+            mUid = uid;
+            mCollectedNotedAppOps = collectedNotedAppOps;
+        }
+    }
+
+    /**
+     * Temporarily suspend collection of noted app-ops when binder-thread calls into the other
+     * process. During such a call there might be call-backs coming back on the same thread which
+     * should not be accounted to the current collection.
+     *
+     * @return a state needed to resume the collection
+     *
+     * @hide
+     */
+    public static @Nullable PausedNotedAppOpsCollection pauseNotedAppOpsCollection() {
+        Integer previousUid = sBinderThreadCallingUid.get();
+        if (previousUid != null) {
+            ArrayMap<String, long[]> previousCollectedNotedAppOps =
+                    sAppOpsNotedInThisBinderTransaction.get();
+
+            sBinderThreadCallingUid.remove();
+            sAppOpsNotedInThisBinderTransaction.remove();
+
+            return new PausedNotedAppOpsCollection(previousUid, previousCollectedNotedAppOps);
+        }
+
+        return null;
+    }
+
+    /**
+     * Resume a collection paused via {@link #pauseNotedAppOpsCollection}.
+     *
+     * @param prevCollection The state of the previous collection
+     *
+     * @hide
+     */
+    public static void resumeNotedAppOpsCollection(
+            @Nullable PausedNotedAppOpsCollection prevCollection) {
+        if (prevCollection != null) {
+            sBinderThreadCallingUid.set(prevCollection.mUid);
+
+            if (prevCollection.mCollectedNotedAppOps != null) {
+                sAppOpsNotedInThisBinderTransaction.set(prevCollection.mCollectedNotedAppOps);
+            }
+        }
+    }
+
+    /**
      * Finish collection of noted appops on this thread.
      *
      * <p>Called at the end of a two way binder transaction.
@@ -9091,47 +9205,26 @@
      */
     @TestApi
     public static void collectNotedOpSync(@NonNull SyncNotedAppOp syncOp) {
-        collectNotedOpSync(sOpStrToOp.get(syncOp.getOp()), syncOp.getAttributionTag(),
-                syncOp.getPackageName());
-    }
-
-    /**
-     * Collect a noted op when inside of a two-way binder call.
-     *
-     * <p> Delivered to caller via {@link #prefixParcelWithAppOpsIfNeeded}
-     *
-     * @param code the op code to note for
-     * @param attributionTag the attribution tag to note for
-     * @param packageName the package to note for
-     */
-    private static void collectNotedOpSync(int code, @Nullable String attributionTag,
-            @NonNull String packageName) {
         // If this is inside of a two-way binder call:
         // We are inside of a two-way binder call. Delivered to caller via
         // {@link #prefixParcelWithAppOpsIfNeeded}
-        ArrayMap<String, ArrayMap<String, long[]>> appOpsNoted =
-                sAppOpsNotedInThisBinderTransaction.get();
+        int op = sOpStrToOp.get(syncOp.getOp());
+        ArrayMap<String, long[]> appOpsNoted = sAppOpsNotedInThisBinderTransaction.get();
         if (appOpsNoted == null) {
             appOpsNoted = new ArrayMap<>(1);
             sAppOpsNotedInThisBinderTransaction.set(appOpsNoted);
         }
 
-        ArrayMap<String, long[]> packageAppOpsNotedForAttribution = appOpsNoted.get(packageName);
-        if (packageAppOpsNotedForAttribution == null) {
-            packageAppOpsNotedForAttribution = new ArrayMap<>(1);
-            appOpsNoted.put(packageName, packageAppOpsNotedForAttribution);
-        }
-
-        long[] appOpsNotedForAttribution = packageAppOpsNotedForAttribution.get(attributionTag);
+        long[] appOpsNotedForAttribution = appOpsNoted.get(syncOp.getAttributionTag());
         if (appOpsNotedForAttribution == null) {
             appOpsNotedForAttribution = new long[2];
-            packageAppOpsNotedForAttribution.put(attributionTag, appOpsNotedForAttribution);
+            appOpsNoted.put(syncOp.getAttributionTag(), appOpsNotedForAttribution);
         }
 
-        if (code < 64) {
-            appOpsNotedForAttribution[0] |= 1L << code;
+        if (op < 64) {
+            appOpsNotedForAttribution[0] |= 1L << op;
         } else {
-            appOpsNotedForAttribution[1] |= 1L << (code - 64);
+            appOpsNotedForAttribution[1] |= 1L << (op - 64);
         }
     }
 
@@ -9185,7 +9278,9 @@
             }
         }
 
-        if (isListeningForOpNotedInBinderTransaction()) {
+        Integer binderUid = sBinderThreadCallingUid.get();
+
+        if (binderUid != null && binderUid == uid) {
             return COLLECT_SYNC;
         } else {
             return COLLECT_ASYNC;
@@ -9204,32 +9299,20 @@
      */
     // TODO (b/186872903) Refactor how sync noted ops are propagated.
     public static void prefixParcelWithAppOpsIfNeeded(@NonNull Parcel p) {
-        if (!isListeningForOpNotedInBinderTransaction()) {
-            return;
-        }
-        final ArrayMap<String, ArrayMap<String, long[]>> notedAppOps =
-                sAppOpsNotedInThisBinderTransaction.get();
+        ArrayMap<String, long[]> notedAppOps = sAppOpsNotedInThisBinderTransaction.get();
         if (notedAppOps == null) {
             return;
         }
 
         p.writeInt(Parcel.EX_HAS_NOTED_APPOPS_REPLY_HEADER);
 
-        final int packageCount = notedAppOps.size();
-        p.writeInt(packageCount);
+        int numAttributionWithNotesAppOps = notedAppOps.size();
+        p.writeInt(numAttributionWithNotesAppOps);
 
-        for (int i = 0; i < packageCount; i++) {
+        for (int i = 0; i < numAttributionWithNotesAppOps; i++) {
             p.writeString(notedAppOps.keyAt(i));
-
-            final ArrayMap<String, long[]> notedTagAppOps = notedAppOps.valueAt(i);
-            final int tagCount = notedTagAppOps.size();
-            p.writeInt(tagCount);
-
-            for (int j = 0; j < tagCount; j++) {
-                p.writeString(notedTagAppOps.keyAt(j));
-                p.writeLong(notedTagAppOps.valueAt(j)[0]);
-                p.writeLong(notedTagAppOps.valueAt(j)[1]);
-            }
+            p.writeLong(notedAppOps.valueAt(i)[0]);
+            p.writeLong(notedAppOps.valueAt(i)[1]);
         }
     }
 
@@ -9244,54 +9327,36 @@
      * @hide
      */
     public static void readAndLogNotedAppops(@NonNull Parcel p) {
-        final int packageCount = p.readInt();
-        if (packageCount <= 0) {
-            return;
-        }
+        int numAttributionsWithNotedAppOps = p.readInt();
 
-        final String myPackageName = ActivityThread.currentPackageName();
+        for (int i = 0; i < numAttributionsWithNotedAppOps; i++) {
+            String attributionTag = p.readString();
+            long[] rawNotedAppOps = new long[2];
+            rawNotedAppOps[0] = p.readLong();
+            rawNotedAppOps[1] = p.readLong();
 
-        synchronized (sLock) {
-            for (int i = 0; i < packageCount; i++) {
-                final String packageName = p.readString();
+            if (rawNotedAppOps[0] != 0 || rawNotedAppOps[1] != 0) {
+                BitSet notedAppOps = BitSet.valueOf(rawNotedAppOps);
 
-                final int tagCount = p.readInt();
-                for (int j = 0; j < tagCount; j++) {
-                    final String attributionTag = p.readString();
-                    final long[] rawNotedAppOps = new long[2];
-                    rawNotedAppOps[0] = p.readLong();
-                    rawNotedAppOps[1] = p.readLong();
-
-                    if (rawNotedAppOps[0] == 0 && rawNotedAppOps[1] == 0) {
-                        continue;
-                    }
-
-                    final BitSet notedAppOps = BitSet.valueOf(rawNotedAppOps);
+                synchronized (sLock) {
                     for (int code = notedAppOps.nextSetBit(0); code != -1;
                             code = notedAppOps.nextSetBit(code + 1)) {
-                        if (Objects.equals(myPackageName, packageName)) {
-                            if (sOnOpNotedCallback != null) {
-                                sOnOpNotedCallback.onNoted(new SyncNotedAppOp(code,
-                                        attributionTag, packageName));
-                            } else {
-                                String message = getFormattedStackTrace();
-                                sUnforwardedOps.add(new AsyncNotedAppOp(code, Process.myUid(),
-                                        attributionTag, message, System.currentTimeMillis()));
-                                if (sUnforwardedOps.size() > MAX_UNFORWARDED_OPS) {
-                                    sUnforwardedOps.remove(0);
-                                }
+                        if (sOnOpNotedCallback != null) {
+                            sOnOpNotedCallback.onNoted(new SyncNotedAppOp(code, attributionTag));
+                        } else {
+                            String message = getFormattedStackTrace();
+                            sUnforwardedOps.add(
+                                    new AsyncNotedAppOp(code, Process.myUid(), attributionTag,
+                                            message, System.currentTimeMillis()));
+                            if (sUnforwardedOps.size() > MAX_UNFORWARDED_OPS) {
+                                sUnforwardedOps.remove(0);
                             }
-                        } else if (isListeningForOpNotedInBinderTransaction()) {
-                            collectNotedOpSync(code, attributionTag, packageName);
                         }
                     }
-                    for (int code = notedAppOps.nextSetBit(0); code != -1;
-                            code = notedAppOps.nextSetBit(code + 1)) {
-                        if (Objects.equals(myPackageName, packageName)) {
-                            sMessageCollector.onNoted(new SyncNotedAppOp(code,
-                                    attributionTag, packageName));
-                        }
-                    }
+                }
+                for (int code = notedAppOps.nextSetBit(0); code != -1;
+                        code = notedAppOps.nextSetBit(code + 1)) {
+                    sMessageCollector.onNoted(new SyncNotedAppOp(code, attributionTag));
                 }
             }
         }
@@ -9398,15 +9463,7 @@
      * @hide
      */
     public static boolean isListeningForOpNoted() {
-        return sOnOpNotedCallback != null || isListeningForOpNotedInBinderTransaction()
-                || isCollectingStackTraces();
-    }
-
-    /**
-     * @return whether we are in a binder transaction and collecting appops.
-     */
-    private static boolean isListeningForOpNotedInBinderTransaction() {
-        return sBinderThreadCallingUid.get() != null;
+        return sOnOpNotedCallback != null || isCollectingStackTraces();
     }
 
     /**
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index b2184fe..fd6fa57 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -743,6 +743,18 @@
         }
 
         /**
+         * This overload is used for notifying the {@link android.window.TaskFragmentOrganizer}
+         * implementation internally about started activities.
+         *
+         * @see #onStartActivity(Intent)
+         * @hide
+         */
+        public ActivityResult onStartActivity(@NonNull Context who, @NonNull Intent intent,
+                @NonNull Bundle options) {
+            return onStartActivity(intent);
+        }
+
+        /**
          * Used for intercepting any started activity.
          *
          * <p> A non-null return value here will be considered a hit for this monitor.
@@ -1722,7 +1734,10 @@
                     final ActivityMonitor am = mActivityMonitors.get(i);
                     ActivityResult result = null;
                     if (am.ignoreMatchingSpecificIntents()) {
-                        result = am.onStartActivity(intent);
+                        if (options == null) {
+                            options = ActivityOptions.makeBasic().toBundle();
+                        }
+                        result = am.onStartActivity(who, intent, options);
                     }
                     if (result != null) {
                         am.mHits++;
@@ -1790,7 +1805,10 @@
                     final ActivityMonitor am = mActivityMonitors.get(i);
                     ActivityResult result = null;
                     if (am.ignoreMatchingSpecificIntents()) {
-                        result = am.onStartActivity(intents[0]);
+                        if (options == null) {
+                            options = ActivityOptions.makeBasic().toBundle();
+                        }
+                        result = am.onStartActivity(who, intents[0], options);
                     }
                     if (result != null) {
                         am.mHits++;
@@ -1861,7 +1879,10 @@
                     final ActivityMonitor am = mActivityMonitors.get(i);
                     ActivityResult result = null;
                     if (am.ignoreMatchingSpecificIntents()) {
-                        result = am.onStartActivity(intent);
+                        if (options == null) {
+                            options = ActivityOptions.makeBasic().toBundle();
+                        }
+                        result = am.onStartActivity(who, intent, options);
                     }
                     if (result != null) {
                         am.mHits++;
@@ -1928,7 +1949,10 @@
                     final ActivityMonitor am = mActivityMonitors.get(i);
                     ActivityResult result = null;
                     if (am.ignoreMatchingSpecificIntents()) {
-                        result = am.onStartActivity(intent);
+                        if (options == null) {
+                            options = ActivityOptions.makeBasic().toBundle();
+                        }
+                        result = am.onStartActivity(who, intent, options);
                     }
                     if (result != null) {
                         am.mHits++;
@@ -1974,7 +1998,10 @@
                     final ActivityMonitor am = mActivityMonitors.get(i);
                     ActivityResult result = null;
                     if (am.ignoreMatchingSpecificIntents()) {
-                        result = am.onStartActivity(intent);
+                        if (options == null) {
+                            options = ActivityOptions.makeBasic().toBundle();
+                        }
+                        result = am.onStartActivity(who, intent, options);
                     }
                     if (result != null) {
                         am.mHits++;
@@ -2021,7 +2048,10 @@
                     final ActivityMonitor am = mActivityMonitors.get(i);
                     ActivityResult result = null;
                     if (am.ignoreMatchingSpecificIntents()) {
-                        result = am.onStartActivity(intent);
+                        if (options == null) {
+                            options = ActivityOptions.makeBasic().toBundle();
+                        }
+                        result = am.onStartActivity(who, intent, options);
                     }
                     if (result != null) {
                         am.mHits++;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 9d149cf..e39636d 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5387,8 +5387,8 @@
             contentView.setInt(R.id.expand_button, "setDefaultPillColor", pillColor);
             // Use different highlighted colors for conversations' unread count
             if (p.mHighlightExpander) {
-                pillColor = Colors.flattenAlpha(getPrimaryAccentColor(p), bgColor);
-                textColor = Colors.flattenAlpha(bgColor, pillColor);
+                pillColor = Colors.flattenAlpha(getColors(p).getTertiaryAccentColor(), bgColor);
+                textColor = Colors.flattenAlpha(getColors(p).getOnAccentTextColor(), pillColor);
             }
             contentView.setInt(R.id.expand_button, "setHighlightTextColor", textColor);
             contentView.setInt(R.id.expand_button, "setHighlightPillColor", pillColor);
@@ -6122,28 +6122,29 @@
                 // change the background bgColor
                 CharSequence title = action.title;
                 ColorStateList[] outResultColor = new ColorStateList[1];
-                int background = getColors(p).getSecondaryAccentColor();
+                int buttonFillColor = getColors(p).getSecondaryAccentColor();
                 if (isLegacy()) {
                     title = ContrastColorUtil.clearColorSpans(title);
                 } else {
-                    title = ensureColorSpanContrast(title, background, outResultColor);
+                    int notifBackgroundColor = getColors(p).getBackgroundColor();
+                    title = ensureColorSpanContrast(title, notifBackgroundColor, outResultColor);
                 }
                 button.setTextViewText(R.id.action0, processTextSpans(title));
                 boolean hasColorOverride = outResultColor[0] != null;
                 if (hasColorOverride) {
                     // There's a span spanning the full text, let's take it and use it as the
                     // background color
-                    background = outResultColor[0].getDefaultColor();
+                    buttonFillColor = outResultColor[0].getDefaultColor();
                 }
                 final int textColor = ContrastColorUtil.resolvePrimaryColor(mContext,
-                        background, mInNightMode);
+                        buttonFillColor, mInNightMode);
                 button.setTextColor(R.id.action0, textColor);
                 // We only want about 20% alpha for the ripple
                 final int rippleColor = (textColor & 0x00ffffff) | 0x33000000;
                 button.setColorStateList(R.id.action0, "setRippleColor",
                         ColorStateList.valueOf(rippleColor));
                 button.setColorStateList(R.id.action0, "setButtonBackground",
-                        ColorStateList.valueOf(background));
+                        ColorStateList.valueOf(buttonFillColor));
                 if (p.mCallStyleActions) {
                     button.setImageViewIcon(R.id.action0, action.getIcon());
                     boolean priority = action.getExtras().getBoolean(CallStyle.KEY_ACTION_PRIORITY);
@@ -6176,8 +6177,8 @@
          *                    there exists a full length color span.
          * @return the contrasted charSequence
          */
-        private CharSequence ensureColorSpanContrast(CharSequence charSequence, int background,
-                ColorStateList[] outResultColor) {
+        private static CharSequence ensureColorSpanContrast(CharSequence charSequence,
+                int background, ColorStateList[] outResultColor) {
             if (charSequence instanceof Spanned) {
                 Spanned ss = (Spanned) charSequence;
                 Object[] spans = ss.getSpans(0, ss.length(), Object.class);
@@ -6197,8 +6198,9 @@
                             int[] colors = textColor.getColors();
                             int[] newColors = new int[colors.length];
                             for (int i = 0; i < newColors.length; i++) {
+                                boolean isBgDark = isColorDark(background);
                                 newColors[i] = ContrastColorUtil.ensureLargeTextContrast(
-                                        colors[i], background, mInNightMode);
+                                        colors[i], background, isBgDark);
                             }
                             textColor = new ColorStateList(textColor.getStates().clone(),
                                     newColors);
@@ -6217,8 +6219,9 @@
                     } else if (resultSpan instanceof ForegroundColorSpan) {
                         ForegroundColorSpan originalSpan = (ForegroundColorSpan) resultSpan;
                         int foregroundColor = originalSpan.getForegroundColor();
+                        boolean isBgDark = isColorDark(background);
                         foregroundColor = ContrastColorUtil.ensureLargeTextContrast(
-                                foregroundColor, background, mInNightMode);
+                                foregroundColor, background, isBgDark);
                         if (fullLength) {
                             outResultColor[0] = ColorStateList.valueOf(foregroundColor);
                             resultSpan = null;
@@ -6238,6 +6241,20 @@
         }
 
         /**
+         * Determines if the color is light or dark.  Specifically, this is using the same metric as
+         * {@link ContrastColorUtil#resolvePrimaryColor(Context, int, boolean)} and peers so that
+         * the direction of color shift is consistent.
+         *
+         * @param color the color to check
+         * @return true if the color has higher contrast with white than black
+         * @hide
+         */
+        public static boolean isColorDark(int color) {
+            // as per ContrastColorUtil.shouldUseDark, this uses the color contrast midpoint.
+            return ContrastColorUtil.calculateLuminance(color) <= 0.17912878474;
+        }
+
+        /**
          * @return Whether we are currently building a notification from a legacy (an app that
          *         doesn't create material notifications by itself) app.
          */
@@ -12289,6 +12306,8 @@
         private int mSecondaryTextColor = COLOR_INVALID;
         private int mPrimaryAccentColor = COLOR_INVALID;
         private int mSecondaryAccentColor = COLOR_INVALID;
+        private int mTertiaryAccentColor = COLOR_INVALID;
+        private int mOnAccentTextColor = COLOR_INVALID;
         private int mErrorColor = COLOR_INVALID;
         private int mContrastColor = COLOR_INVALID;
         private int mRippleAlpha = 0x33;
@@ -12346,7 +12365,7 @@
 
             if (isColorized) {
                 if (rawColor == COLOR_DEFAULT) {
-                    int[] attrs = {R.attr.colorAccentTertiary};
+                    int[] attrs = {R.attr.colorAccentSecondary};
                     try (TypedArray ta = obtainDayNightAttributes(ctx, attrs)) {
                         mBackgroundColor = getColor(ta, 0, Color.WHITE);
                     }
@@ -12363,6 +12382,8 @@
                 mContrastColor = mPrimaryTextColor;
                 mPrimaryAccentColor = mPrimaryTextColor;
                 mSecondaryAccentColor = mSecondaryTextColor;
+                mTertiaryAccentColor = flattenAlpha(mPrimaryTextColor, mBackgroundColor);
+                mOnAccentTextColor = mBackgroundColor;
                 mErrorColor = mPrimaryTextColor;
                 mRippleAlpha = 0x33;
             } else {
@@ -12373,6 +12394,8 @@
                         R.attr.textColorSecondary,
                         R.attr.colorAccent,
                         R.attr.colorAccentSecondary,
+                        R.attr.colorAccentTertiary,
+                        R.attr.textColorOnAccent,
                         R.attr.colorError,
                         R.attr.colorControlHighlight
                 };
@@ -12383,8 +12406,10 @@
                     mSecondaryTextColor = getColor(ta, 3, COLOR_INVALID);
                     mPrimaryAccentColor = getColor(ta, 4, COLOR_INVALID);
                     mSecondaryAccentColor = getColor(ta, 5, COLOR_INVALID);
-                    mErrorColor = getColor(ta, 6, COLOR_INVALID);
-                    mRippleAlpha = Color.alpha(getColor(ta, 7, 0x33ffffff));
+                    mTertiaryAccentColor = getColor(ta, 6, COLOR_INVALID);
+                    mOnAccentTextColor = getColor(ta, 7, COLOR_INVALID);
+                    mErrorColor = getColor(ta, 8, COLOR_INVALID);
+                    mRippleAlpha = Color.alpha(getColor(ta, 9, 0x33ffffff));
                 }
                 mContrastColor = calculateContrastColor(ctx, rawColor, mPrimaryAccentColor,
                         mBackgroundColor, nightMode);
@@ -12404,6 +12429,14 @@
                 if (mSecondaryAccentColor == COLOR_INVALID) {
                     mSecondaryAccentColor = mContrastColor;
                 }
+                if (mTertiaryAccentColor == COLOR_INVALID) {
+                    mTertiaryAccentColor = mContrastColor;
+                }
+                if (mOnAccentTextColor == COLOR_INVALID) {
+                    mOnAccentTextColor = ColorUtils.setAlphaComponent(
+                            ContrastColorUtil.resolvePrimaryColor(
+                                    ctx, mTertiaryAccentColor, nightMode), 0xFF);
+                }
                 if (mErrorColor == COLOR_INVALID) {
                     mErrorColor = mPrimaryTextColor;
                 }
@@ -12469,6 +12502,16 @@
             return mSecondaryAccentColor;
         }
 
+        /** @return the theme's tertiary accent color for colored UI elements. */
+        public @ColorInt int getTertiaryAccentColor() {
+            return mTertiaryAccentColor;
+        }
+
+        /** @return the theme's text color to be used on the tertiary accent color. */
+        public @ColorInt int getOnAccentTextColor() {
+            return mOnAccentTextColor;
+        }
+
         /**
          * @return the contrast-adjusted version of the color provided by the app, or the
          * primary text color when colorized.
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 7cb1d89..3363872 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -387,6 +387,13 @@
      * <p>This mode makes sense for things that will be explicitly started
      * and stopped to run for arbitrary periods of time, such as a service
      * performing background music playback.
+     *
+     * <p>Since Android version {@link Build.VERSION_CODES#S}, apps
+     * targeting {@link Build.VERSION_CODES#S} or above are disallowed
+     * to start a foreground service from the background, but the restriction
+     * doesn't impact <em>restarts</em> of a sticky foreground service. However,
+     * when apps start a sticky foreground service from the background,
+     * the same restriction still applies.
      */
     public static final int START_STICKY = 1;
     
diff --git a/core/java/android/app/SyncNotedAppOp.java b/core/java/android/app/SyncNotedAppOp.java
index 32d889e..7c0c08a 100644
--- a/core/java/android/app/SyncNotedAppOp.java
+++ b/core/java/android/app/SyncNotedAppOp.java
@@ -21,7 +21,6 @@
 import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.os.Parcelable;
-import android.os.Process;
 
 import com.android.internal.annotations.Immutable;
 import com.android.internal.util.DataClass;
@@ -29,6 +28,8 @@
 /**
  * Description of an app-op that was noted for the current process.
  *
+ * Note: package name is currently unused in the system.
+ *
  * <p>This is either delivered after a
  * {@link AppOpsManager.OnOpNotedCallback#onNoted(SyncNotedAppOp) two way binder call} or
  * when the app
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 8d332ab..edfbf1a 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -221,6 +221,20 @@
     public static final String COMMAND_REAPPLY = "android.wallpaper.reapply";
 
     /**
+     * Command for {@link #sendWallpaperCommand}: reported when the live wallpaper needs to be
+     * frozen.
+     * @hide
+     */
+    public static final String COMMAND_FREEZE = "android.wallpaper.freeze";
+
+    /**
+     * Command for {@link #sendWallpaperCommand}: reported when the live wallapper doesn't need
+     * to be frozen anymore.
+     * @hide
+     */
+    public static final String COMMAND_UNFREEZE = "android.wallpaper.unfreeze";
+
+    /**
      * Extra passed back from setWallpaper() giving the new wallpaper's assigned ID.
      * @hide
      */
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index dae565e..67f631f 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -281,6 +281,32 @@
     }
 
     /**
+     * Convenience method for callers who need to indicate that some other package or
+     * some other user needs a backup pass. This can be useful in the case of groups of
+     * packages that share a uid and/or have user-specific data.
+     * <p>
+     * This method requires that the application hold the "android.permission.BACKUP"
+     * permission if the package named in the package argument does not run under the
+     * same uid as the caller. This method also requires that the application hold the
+     * "android.permission.INTERACT_ACROSS_USERS_FULL" if the user argument is not the
+     * same as the user the caller is running under.
+     * @param userId The user to back up
+     * @param packageName The package name identifying the application to back up.
+     *
+     * @hide
+     */
+    public static void dataChangedForUser(int userId, String packageName) {
+        checkServiceBinder();
+        if (sService != null) {
+            try {
+                sService.dataChangedForUser(userId, packageName);
+            } catch (RemoteException e) {
+                Log.e(TAG, "dataChanged(userId,pkg) couldn't connect");
+            }
+        }
+    }
+
+    /**
      * @deprecated Applications shouldn't request a restore operation using this method. In Android
      * P and later, this method is a no-op.
      *
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index 1b03f2f..bf9a9b0 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -116,7 +116,7 @@
         ConfigSection.CLOUD_BACKUP,
         ConfigSection.DEVICE_TRANSFER
     })
-    private @interface ConfigSection {
+    @interface ConfigSection {
         String CLOUD_BACKUP = "cloud-backup";
         String DEVICE_TRANSFER = "device-transfer";
     }
@@ -528,7 +528,8 @@
             return mExcludes;
         }
 
-        private synchronized int getRequiredTransportFlags()
+        @VisibleForTesting
+        public synchronized int getRequiredTransportFlags()
                 throws IOException, XmlPullParserException {
             if (mRequiredTransportFlags == null) {
                 maybeParseBackupSchemeLocked();
@@ -587,11 +588,13 @@
             if (mDataExtractionRules != 0) {
                 // New config is present. Use it if it has configuration for this operation
                 // type.
+                boolean isSectionPresent;
                 try (XmlResourceParser parser = getParserForResource(mDataExtractionRules)) {
-                    parseNewBackupSchemeFromXmlLocked(parser, configSection, mExcludes, mIncludes);
+                    isSectionPresent = parseNewBackupSchemeFromXmlLocked(parser, configSection,
+                            mExcludes, mIncludes);
                 }
-                if (!mExcludes.isEmpty() || !mIncludes.isEmpty()) {
-                    // Found configuration in the new config, we will use it.
+                if (isSectionPresent) {
+                    // Found the relevant section in the new config, we will use it.
                     mIsUsingNewScheme = true;
                     return;
                 }
@@ -630,24 +633,31 @@
                     .getXml(resourceId);
         }
 
-        private void parseNewBackupSchemeFromXmlLocked(XmlPullParser parser,
+        @VisibleForTesting
+        public boolean parseNewBackupSchemeFromXmlLocked(XmlPullParser parser,
                 @ConfigSection  String configSection,
                 Set<PathWithRequiredFlags> excludes,
                 Map<String, Set<PathWithRequiredFlags>> includes)
                 throws IOException, XmlPullParserException {
             verifyTopLevelTag(parser, "data-extraction-rules");
 
+            boolean isSectionPresent = false;
+
             int event;
             while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
                 if (event != XmlPullParser.START_TAG || !configSection.equals(parser.getName())) {
                     continue;
                 }
 
+                isSectionPresent = true;
+
                 parseRequiredTransportFlags(parser, configSection);
                 parseRules(parser, excludes, includes, Optional.of(0), configSection);
             }
 
             logParsingResults(excludes, includes);
+
+            return isSectionPresent;
         }
 
         private void parseRequiredTransportFlags(XmlPullParser parser,
diff --git a/core/java/android/app/servertransaction/ActivityTransactionItem.java b/core/java/android/app/servertransaction/ActivityTransactionItem.java
index a539812..186f25d 100644
--- a/core/java/android/app/servertransaction/ActivityTransactionItem.java
+++ b/core/java/android/app/servertransaction/ActivityTransactionItem.java
@@ -59,36 +59,37 @@
     }
 
     /**
-     * Get the {@link ActivityClientRecord} instance that corresponds to the provided token.
+     * Gets the {@link ActivityClientRecord} instance that corresponds to the provided token.
      * @param client Target client handler.
      * @param token Target activity token.
-     * @param includeLaunching Indicate to also find the {@link ActivityClientRecord} in launching
-     *                         activity list. It should be noted that there is no activity in
+     * @param includeLaunching Indicate to find the {@link ActivityClientRecord} in launching
+     *                         activity list.
+     *                         <p>Note that there is no {@link android.app.Activity} instance in
      *                         {@link ActivityClientRecord} from the launching activity list.
      * @return The {@link ActivityClientRecord} instance that corresponds to the provided token.
      */
     @NonNull ActivityClientRecord getActivityClientRecord(
             @NonNull ClientTransactionHandler client, IBinder token, boolean includeLaunching) {
-        ActivityClientRecord r = client.getActivityClient(token);
-        if (r != null) {
-            if (client.getActivity(token) == null) {
+        ActivityClientRecord r = null;
+        // Check launching Activity first to prevent race condition that activity instance has not
+        // yet set to ActivityClientRecord.
+        if (includeLaunching) {
+            r = client.getLaunchingActivity(token);
+        }
+        // Then if we don't want to find launching Activity or the ActivityClientRecord doesn't
+        // exist in launching Activity list. The ActivityClientRecord should have been initialized
+        // and put in the Activity list.
+        if (r == null) {
+            r = client.getActivityClient(token);
+            if (r != null && client.getActivity(token) == null) {
                 throw new IllegalArgumentException("Activity must not be null to execute "
                         + "transaction item");
             }
-            return r;
-        }
-        // The activity may not be launched yet. Fallback to check launching activity.
-        if (includeLaunching) {
-            r = client.getLaunchingActivity(token);
         }
         if (r == null) {
             throw new IllegalArgumentException("Activity client record must not be null to execute "
                     + "transaction item");
         }
-
-        // We don't need to check the activity of launching activity client records because they
-        // have not been launched yet.
-
         return r;
     }
 }
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 34e4fcd..37cbccb 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -73,6 +73,7 @@
     private IBinder mAssistToken;
     private IBinder mShareableActivityToken;
     private boolean mLaunchedFromBubble;
+    private IBinder mTaskFragmentToken;
     /**
      * It is only non-null if the process is the first time to launch activity. It is only an
      * optimization for quick look up of the interface so the field is ignored for comparison.
@@ -86,7 +87,7 @@
                 mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
                 mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,
                 client, mAssistToken, mFixedRotationAdjustments, mShareableActivityToken,
-                mLaunchedFromBubble);
+                mLaunchedFromBubble, mTaskFragmentToken);
         client.addLaunchingActivity(token, r);
         client.updateProcessState(mProcState, false);
         client.updatePendingConfiguration(mCurConfig);
@@ -124,7 +125,7 @@
             boolean isForward, ProfilerInfo profilerInfo, IBinder assistToken,
             IActivityClientController activityClientController,
             FixedRotationAdjustments fixedRotationAdjustments, IBinder shareableActivityToken,
-            boolean launchedFromBubble) {
+            boolean launchedFromBubble, IBinder taskFragmentToken) {
         LaunchActivityItem instance = ObjectPool.obtain(LaunchActivityItem.class);
         if (instance == null) {
             instance = new LaunchActivityItem();
@@ -133,7 +134,7 @@
                 voiceInteractor, procState, state, persistentState, pendingResults,
                 pendingNewIntents, activityOptions, isForward, profilerInfo, assistToken,
                 activityClientController, fixedRotationAdjustments, shareableActivityToken,
-                launchedFromBubble);
+                launchedFromBubble, taskFragmentToken);
 
         return instance;
     }
@@ -141,7 +142,7 @@
     @Override
     public void recycle() {
         setValues(this, null, 0, null, null, null, null, null, null, 0, null, null, null, null,
-                null, false, null, null, null, null, null, false);
+                null, false, null, null, null, null, null, false, null);
         ObjectPool.recycle(this);
     }
 
@@ -172,6 +173,7 @@
         dest.writeTypedObject(mFixedRotationAdjustments, flags);
         dest.writeStrongBinder(mShareableActivityToken);
         dest.writeBoolean(mLaunchedFromBubble);
+        dest.writeStrongBinder(mTaskFragmentToken);
     }
 
     /** Read from Parcel. */
@@ -190,7 +192,8 @@
                 in.readStrongBinder(),
                 IActivityClientController.Stub.asInterface(in.readStrongBinder()),
                 in.readTypedObject(FixedRotationAdjustments.CREATOR), in.readStrongBinder(),
-                in.readBoolean());
+                in.readBoolean(),
+                in.readStrongBinder());
     }
 
     public static final @NonNull Creator<LaunchActivityItem> CREATOR =
@@ -229,7 +232,8 @@
                 && Objects.equals(mProfilerInfo, other.mProfilerInfo)
                 && Objects.equals(mAssistToken, other.mAssistToken)
                 && Objects.equals(mFixedRotationAdjustments, other.mFixedRotationAdjustments)
-                && Objects.equals(mShareableActivityToken, other.mShareableActivityToken);
+                && Objects.equals(mShareableActivityToken, other.mShareableActivityToken)
+                && Objects.equals(mTaskFragmentToken, other.mTaskFragmentToken);
     }
 
     @Override
@@ -252,6 +256,7 @@
         result = 31 * result + Objects.hashCode(mAssistToken);
         result = 31 * result + Objects.hashCode(mFixedRotationAdjustments);
         result = 31 * result + Objects.hashCode(mShareableActivityToken);
+        result = 31 * result + Objects.hashCode(mTaskFragmentToken);
         return result;
     }
 
@@ -301,7 +306,7 @@
             ActivityOptions activityOptions, boolean isForward, ProfilerInfo profilerInfo,
             IBinder assistToken, IActivityClientController activityClientController,
             FixedRotationAdjustments fixedRotationAdjustments, IBinder shareableActivityToken,
-            boolean launchedFromBubble) {
+            boolean launchedFromBubble, IBinder taskFragmentToken) {
         instance.mIntent = intent;
         instance.mIdent = ident;
         instance.mInfo = info;
@@ -323,5 +328,6 @@
         instance.mFixedRotationAdjustments = fixedRotationAdjustments;
         instance.mShareableActivityToken = shareableActivityToken;
         instance.mLaunchedFromBubble = launchedFromBubble;
+        instance.mTaskFragmentToken = taskFragmentToken;
     }
 }
diff --git a/core/java/android/appwidget/AppWidgetManagerInternal.java b/core/java/android/appwidget/AppWidgetManagerInternal.java
index 5694ca8..266e33a 100644
--- a/core/java/android/appwidget/AppWidgetManagerInternal.java
+++ b/core/java/android/appwidget/AppWidgetManagerInternal.java
@@ -19,6 +19,8 @@
 import android.annotation.Nullable;
 import android.util.ArraySet;
 
+import java.util.Set;
+
 /**
  * App widget manager local system service interface.
  *
@@ -42,4 +44,16 @@
      * @param userId The user that is being unlocked.
      */
     public abstract void unlockUser(int userId);
+
+    /**
+     * Updates all widgets, applying changes to Runtime Resource Overlay affecting the specified
+     * target packages.
+     *
+     * @param packageNames The names of all target packages for which an overlay was modified
+     * @param userId The user for which overlay modifications occurred.
+     * @param updateFrameworkRes Whether or not an overlay affected the values of framework
+     *                           resources.
+     */
+    public abstract void applyResourceOverlaysToWidgets(Set<String> packageNames, int userId,
+            boolean updateFrameworkRes);
 }
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 65cdca9..1dd32fe 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -1025,6 +1025,10 @@
     public boolean setBufferLengthMillis(@BluetoothCodecConfig.SourceCodecType int codec,
             int value) {
         if (VDBG) log("setBufferLengthMillis(" + codec + ", " + value + ")");
+        if (value < 0) {
+            Log.e(TAG, "Trying to set audio buffer length to a negative value: " + value);
+            return false;
+        }
         try {
             final IBluetoothA2dp service = getService();
             if (service != null && isEnabled()) {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 8398be1..5094498 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -64,6 +64,8 @@
 import android.util.Log;
 import android.util.Pair;
 
+import com.android.internal.annotations.GuardedBy;
+
 import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -78,6 +80,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.UUID;
+import java.util.WeakHashMap;
 import java.util.concurrent.Executor;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -715,10 +718,21 @@
     private final IBluetoothManager mManagerService;
     private final AttributionSource mAttributionSource;
 
+    // Yeah, keeping both mService and sService isn't pretty, but it's too late
+    // in the current release for a major refactoring, so we leave them both
+    // intact until this can be cleaned up in a future release
+
     @UnsupportedAppUsage
+    @GuardedBy("mServiceLock")
     private IBluetooth mService;
     private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
 
+    @GuardedBy("sServiceLock")
+    private static boolean sServiceRegistered;
+    @GuardedBy("sServiceLock")
+    private static IBluetooth sService;
+    private static final Object sServiceLock = new Object();
+
     private final Object mLock = new Object();
     private final Map<LeScanCallback, ScanCallback> mLeScanClients;
     private final Map<BluetoothDevice, List<Pair<OnMetadataChangedListener, Executor>>>
@@ -792,19 +806,11 @@
      * Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance.
      */
     BluetoothAdapter(IBluetoothManager managerService, AttributionSource attributionSource) {
-        if (managerService == null) {
-            throw new IllegalArgumentException("bluetooth manager service is null");
-        }
-        try {
-            mServiceLock.writeLock().lock();
-            mService = managerService.registerAdapter(mManagerCallback);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.writeLock().unlock();
-        }
         mManagerService = Objects.requireNonNull(managerService);
         mAttributionSource = Objects.requireNonNull(attributionSource);
+        synchronized (mServiceLock.writeLock()) {
+            mService = getBluetoothService(mManagerCallback);
+        }
         mLeScanClients = new HashMap<LeScanCallback, ScanCallback>();
         mToken = new Binder(DESCRIPTOR);
     }
@@ -3154,21 +3160,16 @@
         }
     }
 
-    @SuppressLint("AndroidFrameworkBluetoothPermission")
-    private final IBluetoothManagerCallback mManagerCallback =
+    private static final IBluetoothManagerCallback sManagerCallback =
             new IBluetoothManagerCallback.Stub() {
-                @SuppressLint("AndroidFrameworkRequiresPermission")
                 public void onBluetoothServiceUp(IBluetooth bluetoothService) {
                     if (DBG) {
                         Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService);
                     }
 
-                    mServiceLock.writeLock().lock();
-                    mService = bluetoothService;
-                    mServiceLock.writeLock().unlock();
-
-                    synchronized (mProxyServiceStateCallbacks) {
-                        for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks) {
+                    synchronized (sServiceLock) {
+                        sService = bluetoothService;
+                        for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) {
                             try {
                                 if (cb != null) {
                                     cb.onBluetoothServiceUp(bluetoothService);
@@ -3180,6 +3181,56 @@
                             }
                         }
                     }
+                }
+
+                public void onBluetoothServiceDown() {
+                    if (DBG) {
+                        Log.d(TAG, "onBluetoothServiceDown");
+                    }
+
+                    synchronized (sServiceLock) {
+                        sService = null;
+                        for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) {
+                            try {
+                                if (cb != null) {
+                                    cb.onBluetoothServiceDown();
+                                } else {
+                                    Log.d(TAG, "onBluetoothServiceDown: cb is null!");
+                                }
+                            } catch (Exception e) {
+                                Log.e(TAG, "", e);
+                            }
+                        }
+                    }
+                }
+
+                public void onBrEdrDown() {
+                    if (VDBG) {
+                        Log.i(TAG, "onBrEdrDown");
+                    }
+
+                    synchronized (sServiceLock) {
+                        for (IBluetoothManagerCallback cb : sProxyServiceStateCallbacks.keySet()) {
+                            try {
+                                if (cb != null) {
+                                    cb.onBrEdrDown();
+                                } else {
+                                    Log.d(TAG, "onBrEdrDown: cb is null!");
+                                }
+                            } catch (Exception e) {
+                                Log.e(TAG, "", e);
+                            }
+                        }
+                    }
+                }
+            };
+
+    private final IBluetoothManagerCallback mManagerCallback =
+            new IBluetoothManagerCallback.Stub() {
+                public void onBluetoothServiceUp(IBluetooth bluetoothService) {
+                    synchronized (mServiceLock.writeLock()) {
+                        mService = bluetoothService;
+                    }
                     synchronized (mMetadataListeners) {
                         mMetadataListeners.forEach((device, pair) -> {
                             try {
@@ -3204,12 +3255,7 @@
                 }
 
                 public void onBluetoothServiceDown() {
-                    if (DBG) {
-                        Log.d(TAG, "onBluetoothServiceDown: " + mService);
-                    }
-
-                    try {
-                        mServiceLock.writeLock().lock();
+                    synchronized (mServiceLock.writeLock()) {
                         mService = null;
                         if (mLeScanClients != null) {
                             mLeScanClients.clear();
@@ -3220,29 +3266,10 @@
                         if (mBluetoothLeScanner != null) {
                             mBluetoothLeScanner.cleanup();
                         }
-                    } finally {
-                        mServiceLock.writeLock().unlock();
-                    }
-
-                    synchronized (mProxyServiceStateCallbacks) {
-                        for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks) {
-                            try {
-                                if (cb != null) {
-                                    cb.onBluetoothServiceDown();
-                                } else {
-                                    Log.d(TAG, "onBluetoothServiceDown: cb is null!");
-                                }
-                            } catch (Exception e) {
-                                Log.e(TAG, "", e);
-                            }
-                        }
                     }
                 }
 
                 public void onBrEdrDown() {
-                    if (VDBG) {
-                        Log.i(TAG, "onBrEdrDown: " + mService);
-                    }
                 }
             };
 
@@ -3477,15 +3504,12 @@
 
     protected void finalize() throws Throwable {
         try {
-            mManagerService.unregisterAdapter(mManagerCallback);
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
+            removeServiceStateCallback(mManagerCallback);
         } finally {
             super.finalize();
         }
     }
 
-
     /**
      * Validate a String Bluetooth address, such as "00:43:A8:23:10:F0"
      * <p>Alphabetic characters must be uppercase to be valid.
@@ -3549,24 +3573,64 @@
         return mAttributionSource;
     }
 
-    private final ArrayList<IBluetoothManagerCallback> mProxyServiceStateCallbacks =
-            new ArrayList<IBluetoothManagerCallback>();
+    @GuardedBy("sServiceLock")
+    private static final WeakHashMap<IBluetoothManagerCallback, Void> sProxyServiceStateCallbacks =
+            new WeakHashMap<>();
+
+    /*package*/ IBluetooth getBluetoothService() {
+        synchronized (sServiceLock) {
+            if (sProxyServiceStateCallbacks.isEmpty()) {
+                throw new IllegalStateException(
+                        "Anonymous service access requires at least one lifecycle in process");
+            }
+            return sService;
+        }
+    }
 
     @UnsupportedAppUsage
     /*package*/ IBluetooth getBluetoothService(IBluetoothManagerCallback cb) {
-        synchronized (mProxyServiceStateCallbacks) {
-            if (cb == null) {
-                Log.w(TAG, "getBluetoothService() called with no BluetoothManagerCallback");
-            } else if (!mProxyServiceStateCallbacks.contains(cb)) {
-                mProxyServiceStateCallbacks.add(cb);
-            }
+        Objects.requireNonNull(cb);
+        synchronized (sServiceLock) {
+            sProxyServiceStateCallbacks.put(cb, null);
+            registerOrUnregisterAdapterLocked();
+            return sService;
         }
-        return mService;
     }
 
     /*package*/ void removeServiceStateCallback(IBluetoothManagerCallback cb) {
-        synchronized (mProxyServiceStateCallbacks) {
-            mProxyServiceStateCallbacks.remove(cb);
+        Objects.requireNonNull(cb);
+        synchronized (sServiceLock) {
+            sProxyServiceStateCallbacks.remove(cb);
+            registerOrUnregisterAdapterLocked();
+        }
+    }
+
+    /**
+     * Handle registering (or unregistering) a single process-wide
+     * {@link IBluetoothManagerCallback} based on the presence of local
+     * {@link #sProxyServiceStateCallbacks} clients.
+     */
+    @GuardedBy("sServiceLock")
+    private void registerOrUnregisterAdapterLocked() {
+        final boolean isRegistered = sServiceRegistered;
+        final boolean wantRegistered = !sProxyServiceStateCallbacks.isEmpty();
+
+        if (isRegistered != wantRegistered) {
+            if (wantRegistered) {
+                try {
+                    sService = mManagerService.registerAdapter(sManagerCallback);
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            } else {
+                try {
+                    mManagerService.unregisterAdapter(sManagerCallback);
+                    sService = null;
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
+            sServiceRegistered = wantRegistered;
         }
     }
 
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index bb409d5..1655b62 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -399,7 +399,7 @@
         try {
             if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
             IBluetooth bluetoothProxy =
-                    BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
+                    BluetoothAdapter.getDefaultAdapter().getBluetoothService();
             if (bluetoothProxy == null) throw new IOException("Bluetooth is off");
             mPfd = bluetoothProxy.getSocketManager().connectSocket(mDevice, mType,
                     mUuid, mPort, getSecurityFlags());
@@ -438,7 +438,7 @@
     /*package*/ int bindListen() {
         int ret;
         if (mSocketState == SocketState.CLOSED) return EBADFD;
-        IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
+        IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService();
         if (bluetoothProxy == null) {
             Log.e(TAG, "bindListen fail, reason: bluetooth is off");
             return -1;
@@ -706,7 +706,7 @@
                 throw new IOException("socket closed");
             }
             IBluetooth bluetoothProxy =
-                    BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
+                    BluetoothAdapter.getDefaultAdapter().getBluetoothService();
             if (bluetoothProxy == null) {
                 throw new IOException("Bluetooth is off");
             }
diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java
index cb3bf29..8ff0181 100644
--- a/core/java/android/bluetooth/le/ScanFilter.java
+++ b/core/java/android/bluetooth/le/ScanFilter.java
@@ -168,6 +168,15 @@
                 dest.writeByteArray(mManufacturerDataMask);
             }
         }
+
+        // IRK
+        if (mDeviceAddress != null) {
+            dest.writeInt(mAddressType);
+            dest.writeInt(mIrk == null ? 0 : 1);
+            if (mIrk != null) {
+                dest.writeByteArray(mIrk);
+            }
+        }
     }
 
     /**
@@ -187,8 +196,10 @@
             if (in.readInt() == 1) {
                 builder.setDeviceName(in.readString());
             }
+            String address = null;
+            // If we have a non-null address
             if (in.readInt() == 1) {
-                builder.setDeviceAddress(in.readString());
+                address = in.readString();
             }
             if (in.readInt() == 1) {
                 ParcelUuid uuid = in.readParcelable(ParcelUuid.class.getClassLoader());
@@ -245,6 +256,17 @@
                 }
             }
 
+            // IRK
+            if (address != null) {
+                final int addressType = in.readInt();
+                if (in.readInt() == 1) {
+                    final byte[] irk = new byte[16];
+                    in.readByteArray(irk);
+                    builder.setDeviceAddress(address, addressType, irk);
+                } else {
+                    builder.setDeviceAddress(address, addressType);
+                }
+            }
             return builder.build();
         }
     };
diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java
index f49362e..2ecd71b 100644
--- a/core/java/android/content/ClipDescription.java
+++ b/core/java/android/content/ClipDescription.java
@@ -124,6 +124,16 @@
      */
     public static final String EXTRA_ACTIVITY_OPTIONS = "android.intent.extra.ACTIVITY_OPTIONS";
 
+    /**
+     * An instance id used for logging.
+     * <p>
+     * Type: {@link com.android.internal.logging.InstanceId}
+     * </p>
+     * @hide
+     */
+    public static final String EXTRA_LOGGING_INSTANCE_ID =
+            "android.intent.extra.LOGGING_INSTANCE_ID";
+
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(value =
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index a741f96..c714f507 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -745,9 +745,6 @@
         if (Binder.getCallingPid() == Process.myPid()) {
             return PermissionChecker.PERMISSION_GRANTED;
         }
-        if (!attributionSource.checkCallingUid()) {
-            return PermissionChecker.PERMISSION_HARD_DENIED;
-        }
         return PermissionChecker.checkPermissionForDataDeliveryFromDataSource(getContext(),
                 permission, -1, new AttributionSource(getContext().getAttributionSource(),
                         attributionSource), /*message*/ null);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c02dcfd..2702772 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4464,11 +4464,11 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a {@link
-     * android.media.MediaTranscodeManager} for transcoding media.
+     * android.media.MediaTranscodingManager} for transcoding media.
      *
      * @hide
      * @see #getSystemService(String)
-     * @see android.media.MediaTranscodeManager
+     * @see android.media.MediaTranscodingManager
      */
     @SystemApi
     public static final String MEDIA_TRANSCODING_SERVICE = "media_transcoding";
diff --git a/core/java/android/content/pm/PackageParserCacheHelper.java b/core/java/android/content/pm/PackageParserCacheHelper.java
index 8212224..e03c3ee 100644
--- a/core/java/android/content/pm/PackageParserCacheHelper.java
+++ b/core/java/android/content/pm/PackageParserCacheHelper.java
@@ -56,6 +56,9 @@
             mStrings.clear();
 
             final int poolPosition = mParcel.readInt();
+            if (poolPosition < 0) {
+                throw new IllegalStateException("Invalid string pool position: " + poolPosition);
+            }
             final int startPosition = mParcel.dataPosition();
 
             // The pool is at the end of the parcel.
diff --git a/core/java/android/hardware/ISensorPrivacyManager.aidl b/core/java/android/hardware/ISensorPrivacyManager.aidl
index d69a9e1..5571165 100644
--- a/core/java/android/hardware/ISensorPrivacyManager.aidl
+++ b/core/java/android/hardware/ISensorPrivacyManager.aidl
@@ -52,4 +52,6 @@
     void addUserGlobalIndividualSensorPrivacyListener(int sensor, in ISensorPrivacyListener listener);
 
     void removeUserGlobalIndividualSensorPrivacyListener(int sensor, in ISensorPrivacyListener listener);
+
+    void showSensorUseDialog(int sensor);
 }
\ No newline at end of file
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index 4526ab77..3c524b2 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -24,15 +24,16 @@
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.annotation.UserIdInt;
-import android.app.ActivityManager;
 import android.content.Context;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.service.SensorPrivacyIndividualEnabledSensorProto;
 import android.service.SensorPrivacyToggleSourceProto;
 import android.util.ArrayMap;
+import android.util.Log;
 import android.util.Pair;
 import android.util.SparseArray;
 
@@ -48,6 +49,8 @@
 @SystemService(Context.SENSOR_PRIVACY_SERVICE)
 public final class SensorPrivacyManager {
 
+    private static final String LOG_TAG = SensorPrivacyManager.class.getSimpleName();
+
     /**
      * Unique Id of this manager to identify to the service
      * @hide
@@ -379,7 +382,7 @@
     @SystemApi
     @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
     public boolean isSensorPrivacyEnabled(@Sensors.Sensor int sensor) {
-        return isSensorPrivacyEnabled(sensor, getCurrentUserId());
+        return isSensorPrivacyEnabled(sensor, UserHandle.USER_CURRENT);
     }
 
     /**
@@ -410,7 +413,7 @@
     @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
     public void setSensorPrivacy(@Sources.Source int source, @Sensors.Sensor int sensor,
             boolean enable) {
-        setSensorPrivacy(source, sensor, enable, getCurrentUserId());
+        setSensorPrivacy(source, sensor, enable, UserHandle.USER_CURRENT);
     }
 
     /**
@@ -446,7 +449,7 @@
     @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
     public void setSensorPrivacyForProfileGroup(@Sources.Source int source,
             @Sensors.Sensor int sensor, boolean enable) {
-        setSensorPrivacyForProfileGroup(source , sensor, enable, getCurrentUserId());
+        setSensorPrivacyForProfileGroup(source , sensor, enable, UserHandle.USER_CURRENT);
     }
 
     /**
@@ -481,7 +484,7 @@
     @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
     public void suppressSensorPrivacyReminders(int sensor,
             boolean suppress) {
-        suppressSensorPrivacyReminders(sensor, suppress, getCurrentUserId());
+        suppressSensorPrivacyReminders(sensor, suppress, UserHandle.USER_CURRENT);
     }
 
     /**
@@ -505,6 +508,25 @@
     }
 
     /**
+     * If sensor privacy for the provided sensor is enabled then this call will show the user the
+     * dialog which is shown when an application attempts to use that sensor. If privacy isn't
+     * enabled then this does nothing.
+     *
+     * This call can only be made by the system uid.
+     *
+     * @throws SecurityException when called by someone other than system uid.
+     *
+     * @hide
+     */
+    public void showSensorUseDialog(int sensor) {
+        try {
+            mService.showSensorUseDialog(sensor);
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "Received exception while trying to show sensor use dialog", e);
+        }
+    }
+
+    /**
      * A class implementing this interface can register with the {@link
      * android.hardware.SensorPrivacyManager} to receive notification when the all-sensor privacy
      * state changes.
@@ -609,12 +631,4 @@
         }
     }
 
-    private int getCurrentUserId() {
-        try {
-            return ActivityManager.getService().getCurrentUserId();
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-        }
-        return 0;
-    }
 }
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index af48b71..3c1ec3e 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -807,6 +807,23 @@
      * The same logic applies to other hardware levels and capabilities.
      * </p>
      *
+     * <p> Devices with the ULTRA_HIGH_RESOLUTION_SENSOR capability have some additional guarantees
+     * which clients can take advantage of : </p>
+     * <table>
+     * <tr><th colspan="10">Additional guaranteed combinations for ULTRA_HIGH_RESOLUTION sensors</th></tr>
+     * <tr> <th colspan="3" id="rb">Target 1</th> <th colspan="3" id="rb">Target 2</th>  <th colspan="3" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr>
+     * <tr> <th>Type</th><th id="rb"> SC Map</th><th id="rb">Max size</th> <th>Type</th><th id="rb"> SC Map</th><th id="rb">Max size</th> <th>Type</th><th id="rb"> SC Map</th><th id="rb">Max size</th></tr>
+     * <tr> <td>{@code YUV / JPEG / RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td id="rb">{@code PRIV / YUV}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code PREVIEW}</td><td colspan="3" id="rb"></td> <td>Ultra high res still image capture with preview</td> </tr>
+     * <tr> <td>{@code YUV / JPEG / RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td id="rb">{@code PRIV}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PRIV / YUV}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code RECORD}</td> <td>Ultra high res still capture with preview + app based RECORD size analysis</td> </tr>
+     * <tr> <td>{@code YUV / JPEG / RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td id="rb">{@code PRIV}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code JPEG / YUV / RAW}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code MAX}</td> <td>Ultra high res still image capture with preview + default sensor pixel mode analysis stream</td> </tr>
+     * </table><br>
+     *
+     * <p> Here, SC Map, refers to the {@link StreamConfigurationMap}, the target stream sizes must
+     * be chosen from. {@code DEFAULT} refers to the default sensor pixel mode {@link
+     * StreamConfigurationMap} and {@code MAX_RES} refers to the maximum resolution {@link
+     * StreamConfigurationMap}. The same capture request must not mix targets from
+     * {@link StreamConfigurationMap}s corresponding to different sensor pixel modes.
+     *
      * <p>Since the capabilities of camera devices vary greatly, a given camera device may support
      * target combinations with sizes outside of these guarantees, but this can only be tested for
      * by calling {@link #isSessionConfigurationSupported} or attempting to create a session with
@@ -989,6 +1006,16 @@
      * <tr> <td>{@code PRIV}</td><td id="rb">{@code MULTI_RES}</td> <td>{@code PRIV}</td><td id="rb">{@code MULTI_RES}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td></td><td id="rb"></td> <td>Maximum-resolution two-input ZSL in-app processing.</td> </tr>
      * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MULTI_RES}</td> <td>Same as input</td><td id="rb">{@code MULTI_RES}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MULTI_RES}</td> <td>ZSL still capture and in-app processing.</td> </tr>
      * </table><br>
+     * <p> Devices with the ULTRA_HIGH_RESOLUTION_SENSOR capability have some additional guarantees
+     * which clients can take advantage of : </p>
+     * <table>
+     * <tr><th colspan="13">Additional guaranteed combinations for ULTRA_HIGH_RESOLUTION sensors (YUV / PRIV inputs are guaranteed only if YUV / PRIVATE reprocessing are supported)</th></tr>
+     * <tr> <th colspan="3" id="rb">Input</th> <th colspan="3" id="rb">Target 1</th> <th colspan="3" id="rb">Target 2</th>  <th colspan="3" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr>
+     * <tr> <th>Type</th><th id="rb"> SC Map</th><th id="rb">Max size</th><th>Type</th><th id="rb"> SC Map</th><th id="rb">Max size</th> <th>Type</th><th id="rb"> SC Map</th><th id="rb">Max size</th> <th>Type</th><th id="rb"> SC Map</th><th id="rb">Max size</th></tr>
+     * <tr> <td>{@code RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td>{@code RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td id="rb">{@code PRIV / YUV}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code PREVIEW}</td><td colspan="3" id="rb"></td> <td>RAW remosaic reprocessing with seperate preview</td> </tr>
+     * <tr> <td>{@code RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td>{@code RAW}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td id="rb">{@code PRIV / YUV}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code JPEG / YUV}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td> <td>Ultra high res RAW -> JPEG / YUV with seperate preview</td> </tr>
+     * <tr> <td>{@code YUV / PRIV}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td> <td>{@code YUV / PRIV}</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td><td id="rb">{@code YUV / PRIV}</td><td id="rb">{@code DEFAULT}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code JPEG }</td><td id="rb">{@code MAX_RES}</td><td id="rb">{@code MAX}</td> <td> Ultra high res PRIV / YUV -> YUV / JPEG reprocessing with seperate preview</td> </tr>
+     * </table><br>
      * No additional mandatory stream combinations for RAW capability and LEVEL-3 hardware level.
      * </p>
      *
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index d9fa56e..4b35294 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -1138,10 +1138,14 @@
      * <code>{@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}</code> describes the streams supported in 'default'
      * mode.
      * The stream configurations supported in 'max resolution' mode are described by
-     * <code>{@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION android.scaler.streamConfigurationMapMaximumResolution}</code>.</p>
+     * <code>{@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION android.scaler.streamConfigurationMapMaximumResolution}</code>.
+     * The maximum resolution mode pixel array size of a camera device
+     * (<code>{@link CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE android.sensor.info.pixelArraySize}</code>) with this capability,
+     * will be at least 24 megapixels.</p>
      *
      * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
      * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION
+     * @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE
      * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
      */
     public static final int REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR = 16;
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index c78dd53..b55a1cb 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -2318,6 +2318,51 @@
             new Key<Float>("android.control.zoomRatio", float.class);
 
     /**
+     * <p>Framework-only private key which informs camera fwk that the AF regions has been set
+     * by the client and those regions need not be corrected when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is
+     * set to MAXIMUM_RESOLUTION.</p>
+     * <p>This must be set to TRUE by the camera2 java fwk when the camera client sets
+     * {@link CaptureRequest#CONTROL_AF_REGIONS android.control.afRegions}.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#CONTROL_AF_REGIONS
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     * @hide
+     */
+    public static final Key<Boolean> CONTROL_AF_REGIONS_SET =
+            new Key<Boolean>("android.control.afRegionsSet", boolean.class);
+
+    /**
+     * <p>Framework-only private key which informs camera fwk that the AE regions has been set
+     * by the client and those regions need not be corrected when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is
+     * set to MAXIMUM_RESOLUTION.</p>
+     * <p>This must be set to TRUE by the camera2 java fwk when the camera client sets
+     * {@link CaptureRequest#CONTROL_AE_REGIONS android.control.aeRegions}.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#CONTROL_AE_REGIONS
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     * @hide
+     */
+    public static final Key<Boolean> CONTROL_AE_REGIONS_SET =
+            new Key<Boolean>("android.control.aeRegionsSet", boolean.class);
+
+    /**
+     * <p>Framework-only private key which informs camera fwk that the AF regions has been set
+     * by the client and those regions need not be corrected when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is
+     * set to MAXIMUM_RESOLUTION.</p>
+     * <p>This must be set to TRUE by the camera2 java fwk when the camera client sets
+     * {@link CaptureRequest#CONTROL_AWB_REGIONS android.control.awbRegions}.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#CONTROL_AWB_REGIONS
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     * @hide
+     */
+    public static final Key<Boolean> CONTROL_AWB_REGIONS_SET =
+            new Key<Boolean>("android.control.awbRegionsSet", boolean.class);
+
+    /**
      * <p>Operation mode for edge
      * enhancement.</p>
      * <p>Edge enhancement improves sharpness and details in the captured image. OFF means
@@ -3057,6 +3102,21 @@
             new Key<Integer>("android.scaler.rotateAndCrop", int.class);
 
     /**
+     * <p>Framework-only private key which informs camera fwk that the scaler crop region
+     * ({@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}) has been set by the client and it need
+     * not be corrected when {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode} is set to MAXIMUM_RESOLUTION.</p>
+     * <p>This must be set to TRUE by the camera2 java fwk when the camera client sets
+     * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}.</p>
+     * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#SCALER_CROP_REGION
+     * @see CaptureRequest#SENSOR_PIXEL_MODE
+     * @hide
+     */
+    public static final Key<Boolean> SCALER_CROP_REGION_SET =
+            new Key<Boolean>("android.scaler.cropRegionSet", boolean.class);
+
+    /**
      * <p>Duration each pixel is exposed to
      * light.</p>
      * <p>If the sensor can't expose this exact duration, it will shorten the
diff --git a/core/java/android/hardware/camera2/MultiResolutionImageReader.java b/core/java/android/hardware/camera2/MultiResolutionImageReader.java
index 3af1b5b..0dbf29d 100644
--- a/core/java/android/hardware/camera2/MultiResolutionImageReader.java
+++ b/core/java/android/hardware/camera2/MultiResolutionImageReader.java
@@ -91,7 +91,7 @@
      * </p>
      * <p>
      * The {@code maxImages} parameter determines the maximum number of
-     * {@link Image} objects that can be be acquired from each of the {@code ImageReader}
+     * {@link Image} objects that can be acquired from each of the {@code ImageReader}
      * within the {@code MultiResolutionImageReader}. However, requesting more buffers will
      * use up more memory, so it is important to use only the minimum number necessary. The
      * application is strongly recommended to acquire no more than {@code maxImages} images
diff --git a/core/java/android/hardware/camera2/extension/IRequestProcessorImpl.aidl b/core/java/android/hardware/camera2/extension/IRequestProcessorImpl.aidl
index 52595a8..88b03a47 100644
--- a/core/java/android/hardware/camera2/extension/IRequestProcessorImpl.aidl
+++ b/core/java/android/hardware/camera2/extension/IRequestProcessorImpl.aidl
@@ -24,9 +24,9 @@
 interface IRequestProcessorImpl
 {
     void setImageProcessor(in OutputConfigId outputConfigId, in IImageProcessorImpl imageProcessor);
-    boolean submit(in Request request, in IRequestCallback callback);
-    boolean submitBurst(in List<Request> requests, in IRequestCallback callback);
-    boolean setRepeating(in Request request, in IRequestCallback callback);
+    int submit(in Request request, in IRequestCallback callback);
+    int submitBurst(in List<Request> requests, in IRequestCallback callback);
+    int setRepeating(in Request request, in IRequestCallback callback);
     void abortCaptures();
     void stopRepeating();
 }
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
index bfc1f27..8da6551 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -446,7 +446,9 @@
         }
     }
 
-    public void release() {
+    public void release(boolean skipCloseNotification) {
+        boolean notifyClose = false;
+
         synchronized (mInterfaceLock) {
             mHandlerThread.quitSafely();
 
@@ -463,6 +465,7 @@
             if (mExtensionClientId >= 0) {
                 CameraExtensionCharacteristics.unregisterClient(mExtensionClientId);
                 if (mInitialized) {
+                    notifyClose = true;
                     CameraExtensionCharacteristics.releaseSession();
                 }
             }
@@ -476,6 +479,16 @@
             mClientRepeatingRequestSurface = null;
             mClientCaptureSurface = null;
         }
+
+        if (notifyClose && !skipCloseNotification) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallbacks.onClosed(
+                        CameraAdvancedExtensionSessionImpl.this));
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
     }
 
     private void notifyConfigurationFailure() {
@@ -485,7 +498,7 @@
             }
         }
 
-        release();
+        release(true /*skipCloseNotification*/);
 
         final long ident = Binder.clearCallingIdentity();
         try {
@@ -501,15 +514,7 @@
             android.hardware.camera2.CameraCaptureSession.StateCallback {
         @Override
         public void onClosed(@NonNull CameraCaptureSession session) {
-            release();
-
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallbacks.onClosed(
-                       CameraAdvancedExtensionSessionImpl.this));
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
+            release(false /*skipCloseNotification*/);
         }
 
         @Override
@@ -812,6 +817,8 @@
                     Log.e(TAG,"Failed to parcel buffer fence!");
                 }
             }
+            parcelImage.width = img.getWidth();
+            parcelImage.height = img.getHeight();
             parcelImage.format = img.getFormat();
             parcelImage.timestamp = img.getTimestamp();
             parcelImage.transform = img.getTransform();
@@ -857,14 +864,15 @@
         }
 
         @Override
-        public boolean submit(Request request, IRequestCallback callback) {
+        public int submit(Request request, IRequestCallback callback) {
             ArrayList<Request> captureList = new ArrayList<>();
             captureList.add(request);
             return submitBurst(captureList, callback);
         }
 
         @Override
-        public boolean submitBurst(List<Request> requests, IRequestCallback callback) {
+        public int submitBurst(List<Request> requests, IRequestCallback callback) {
+            int seqId = -1;
             synchronized (mInterfaceLock) {
                 try {
                     CaptureCallbackHandler captureCallback = new CaptureCallbackHandler(callback);
@@ -873,37 +881,36 @@
                         captureRequests.add(initializeCaptureRequest(mCameraDevice, request,
                                 mCameraConfigMap));
                     }
-                    mCaptureSession.captureBurstRequests(captureRequests,
+                    seqId = mCaptureSession.captureBurstRequests(captureRequests,
                             new CameraExtensionUtils.HandlerExecutor(mHandler), captureCallback);
                 } catch (CameraAccessException e) {
                     Log.e(TAG, "Failed to submit capture requests!");
-                    return false;
                 } catch (IllegalStateException e) {
                     Log.e(TAG, "Capture session closed!");
                 }
             }
 
-            return true;
+            return seqId;
         }
 
         @Override
-        public boolean setRepeating(Request request, IRequestCallback callback) {
+        public int setRepeating(Request request, IRequestCallback callback) {
+            int seqId = -1;
             synchronized (mInterfaceLock) {
                 try {
                     CaptureRequest repeatingRequest = initializeCaptureRequest(mCameraDevice,
                                 request, mCameraConfigMap);
                     CaptureCallbackHandler captureCallback = new CaptureCallbackHandler(callback);
-                    mCaptureSession.setSingleRepeatingRequest(repeatingRequest,
+                    seqId = mCaptureSession.setSingleRepeatingRequest(repeatingRequest,
                             new CameraExtensionUtils.HandlerExecutor(mHandler), captureCallback);
                 } catch (CameraAccessException e) {
                     Log.e(TAG, "Failed to enable repeating request!");
-                    return false;
                 } catch (IllegalStateException e) {
                     Log.e(TAG, "Capture session closed!");
                 }
             }
 
-            return true;
+            return seqId;
         }
 
         @Override
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 11b137ca..fc728a2 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -696,6 +696,16 @@
                 mCurrentSession.replaceSessionClose();
             }
 
+            if (mCurrentExtensionSession != null) {
+                mCurrentExtensionSession.release(false /*skipCloseNotification*/);
+                mCurrentExtensionSession = null;
+            }
+
+            if (mCurrentAdvancedExtensionSession != null) {
+                mCurrentAdvancedExtensionSession.release(false /*skipCloseNotification*/);
+                mCurrentAdvancedExtensionSession = null;
+            }
+
             // TODO: dont block for this
             boolean configureSuccess = true;
             CameraAccessException pendingException = null;
@@ -1342,12 +1352,12 @@
             }
 
             if (mCurrentExtensionSession != null) {
-                mCurrentExtensionSession.release();
+                mCurrentExtensionSession.release(true /*skipCloseNotification*/);
                 mCurrentExtensionSession = null;
             }
 
             if (mCurrentAdvancedExtensionSession != null) {
-                mCurrentAdvancedExtensionSession.release();
+                mCurrentAdvancedExtensionSession.release(true /*skipCloseNotification*/);
                 mCurrentAdvancedExtensionSession = null;
             }
 
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
index 537b894..71047af 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
@@ -49,8 +49,6 @@
 import android.os.Binder;
 import android.os.Handler;
 import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.IInterface;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.annotation.NonNull;
@@ -253,6 +251,7 @@
                     CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT);
             mPreviewImageProcessor.onResolutionUpdate(new Size(repeatingSurfaceInfo.mWidth,
                     repeatingSurfaceInfo.mHeight));
+            mPreviewImageProcessor.onOutputSurface(null, -1);
             mRepeatingRequestImageReader = ImageReader.newInstance(repeatingSurfaceInfo.mWidth,
                     repeatingSurfaceInfo.mHeight,
                     CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT, PREVIEW_QUEUE_SIZE,
@@ -265,13 +264,6 @@
             } catch (ClassCastException e) {
                 throw new UnsupportedOperationException("Failed casting preview processor!");
             }
-            if (mClientRepeatingRequestSurface != null) {
-                mPreviewRequestUpdateProcessor.onOutputSurface(mClientRepeatingRequestSurface,
-                        nativeGetSurfaceFormat(mClientRepeatingRequestSurface));
-                mRepeatingRequestImageWriter = ImageWriter.newInstance(
-                        mClientRepeatingRequestSurface, PREVIEW_QUEUE_SIZE,
-                        CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT);
-            }
             mRepeatingRequestImageReader = ImageReader.newInstance(repeatingSurfaceInfo.mWidth,
                     repeatingSurfaceInfo.mHeight,
                     CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT,
@@ -285,11 +277,6 @@
             mPreviewRequestUpdateProcessor.onImageFormatUpdate(
                     CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT);
         } else {
-            if (mClientRepeatingRequestSurface != null) {
-                mRepeatingRequestImageWriter = ImageWriter.newInstance(
-                        mClientRepeatingRequestSurface, PREVIEW_QUEUE_SIZE,
-                        CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT);
-            }
             mRepeatingRequestImageReader = ImageReader.newInstance(repeatingSurfaceInfo.mWidth,
                     repeatingSurfaceInfo.mHeight,
                     CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT,
@@ -320,7 +307,6 @@
                 mBurstCaptureImageReader = ImageReader.newInstance(surfaceInfo.mWidth,
                         surfaceInfo.mHeight, CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT,
                         mImageExtender.getMaxCaptureStage());
-                mImageProcessor.onOutputSurface(mClientCaptureSurface, surfaceInfo.mFormat);
             } else {
                 // The client doesn't intend to trigger multi-frame capture, however the
                 // image extender still needs to get initialized and the camera still capture
@@ -367,6 +353,29 @@
         }
     }
 
+    private void finishPipelineInitialization() throws RemoteException {
+        if (mClientRepeatingRequestSurface != null) {
+            if (mPreviewProcessorType == IPreviewExtenderImpl.PROCESSOR_TYPE_REQUEST_UPDATE_ONLY) {
+                mPreviewRequestUpdateProcessor.onOutputSurface(mClientRepeatingRequestSurface,
+                        nativeGetSurfaceFormat(mClientRepeatingRequestSurface));
+                mRepeatingRequestImageWriter = ImageWriter.newInstance(
+                        mClientRepeatingRequestSurface,
+                        PREVIEW_QUEUE_SIZE,
+                        CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT);
+            } else if (mPreviewProcessorType == IPreviewExtenderImpl.PROCESSOR_TYPE_NONE) {
+                mRepeatingRequestImageWriter = ImageWriter.newInstance(
+                        mClientRepeatingRequestSurface,
+                        PREVIEW_QUEUE_SIZE,
+                        CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT);
+            }
+        }
+        if ((mImageProcessor != null) && (mClientCaptureSurface != null)) {
+            CameraExtensionUtils.SurfaceInfo surfaceInfo = CameraExtensionUtils.querySurface(
+                    mClientCaptureSurface);
+            mImageProcessor.onOutputSurface(mClientCaptureSurface, surfaceInfo.mFormat);
+        }
+    }
+
     /**
      * @hide
      */
@@ -623,7 +632,9 @@
     }
 
     /** @hide */
-    public void release() {
+    public void release(boolean skipCloseNotification) {
+        boolean notifyClose = false;
+
         synchronized (mInterfaceLock) {
             mInternalRepeatingRequestEnabled = false;
             mHandlerThread.quitSafely();
@@ -639,6 +650,7 @@
             if (mExtensionClientId >= 0) {
                 CameraExtensionCharacteristics.unregisterClient(mExtensionClientId);
                 if (mInitialized) {
+                    notifyClose = true;
                     CameraExtensionCharacteristics.releaseSession();
                 }
             }
@@ -689,6 +701,15 @@
             mCameraRepeatingSurface = mClientRepeatingRequestSurface = null;
             mCameraBurstSurface = mClientCaptureSurface = null;
         }
+
+        if (notifyClose && !skipCloseNotification) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallbacks.onClosed(CameraExtensionSessionImpl.this));
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
     }
 
     private void notifyConfigurationFailure() {
@@ -698,7 +719,7 @@
             }
         }
 
-        release();
+        release(true /*skipCloseNotification*/);
 
         final long ident = Binder.clearCallingIdentity();
         try {
@@ -730,14 +751,7 @@
             android.hardware.camera2.CameraCaptureSession.StateCallback {
         @Override
         public void onClosed(@NonNull CameraCaptureSession session) {
-            release();
-
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                mExecutor.execute(() -> mCallbacks.onClosed(CameraExtensionSessionImpl.this));
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
+            release(false /*skipCloseNotification*/);
         }
 
         @Override
@@ -750,6 +764,7 @@
             synchronized (mInterfaceLock) {
                 mCaptureSession = session;
                 try {
+                    finishPipelineInitialization();
                     CameraExtensionCharacteristics.initializeSession(mInitializeHandler);
                 } catch (RemoteException e) {
                     Log.e(TAG, "Failed to initialize session! Extension service does"
@@ -1640,6 +1655,8 @@
                 Log.e(TAG,"Failed to parcel buffer fence!");
             }
         }
+        parcelImage.width = img.getWidth();
+        parcelImage.height = img.getHeight();
         parcelImage.format = img.getFormat();
         parcelImage.timestamp = img.getTimestamp();
         parcelImage.transform = img.getTransform();
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java b/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java
index 950d716b..afefcbe 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java
@@ -20,7 +20,6 @@
 import android.annotation.Nullable;
 import android.graphics.ImageFormat;
 import android.graphics.PixelFormat;
-import android.hardware.HardwareBuffer;
 import android.hardware.camera2.CameraExtensionCharacteristics;
 import android.hardware.camera2.params.OutputConfiguration;
 import android.hardware.camera2.params.StreamConfigurationMap;
@@ -76,43 +75,22 @@
         ImageWriter writer = null;
         Image img = null;
         SurfaceInfo surfaceInfo = new SurfaceInfo();
-        int nativeFormat = SurfaceUtils.getSurfaceFormat(s);
+        int nativeFormat = SurfaceUtils.detectSurfaceFormat(s);
         int dataspace = SurfaceUtils.getSurfaceDataspace(s);
+        Size surfaceSize = SurfaceUtils.getSurfaceSize(s);
+        surfaceInfo.mFormat = nativeFormat;
+        surfaceInfo.mWidth = surfaceSize.getWidth();
+        surfaceInfo.mHeight = surfaceSize.getHeight();
+        surfaceInfo.mUsage = SurfaceUtils.getSurfaceUsage(s);
         // Jpeg surfaces cannot be queried for their usage and other parameters
         // in the usual way below. A buffer can only be de-queued after the
         // producer overrides the surface dimensions to (width*height) x 1.
         if ((nativeFormat == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB) &&
                 (dataspace == StreamConfigurationMap.HAL_DATASPACE_V0_JFIF)) {
             surfaceInfo.mFormat = ImageFormat.JPEG;
-            Size surfaceSize = SurfaceUtils.getSurfaceSize(s);
-            surfaceInfo.mWidth = surfaceSize.getWidth();
-            surfaceInfo.mHeight = surfaceSize.getHeight();
             return surfaceInfo;
         }
 
-        HardwareBuffer buffer = null;
-        try {
-            writer = ImageWriter.newInstance(s, 1);
-            img = writer.dequeueInputImage();
-            buffer = img.getHardwareBuffer();
-            surfaceInfo.mFormat = buffer.getFormat();
-            surfaceInfo.mWidth = buffer.getWidth();
-            surfaceInfo.mHeight = buffer.getHeight();
-            surfaceInfo.mUsage = buffer.getUsage();
-        } catch (Exception e) {
-            Log.e(TAG, "Failed to query surface, returning defaults!");
-        } finally {
-            if (buffer != null) {
-                buffer.close();
-            }
-            if (img != null) {
-                img.close();
-            }
-            if (writer != null) {
-                writer.close();
-            }
-        }
-
         return surfaceInfo;
     }
 
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 6cbe107..196134b 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -53,6 +53,7 @@
 import android.hardware.camera2.params.Face;
 import android.hardware.camera2.params.HighSpeedVideoConfiguration;
 import android.hardware.camera2.params.LensShadingMap;
+import android.hardware.camera2.params.MeteringRectangle;
 import android.hardware.camera2.params.MandatoryStreamCombination;
 import android.hardware.camera2.params.MultiResolutionStreamConfigurationMap;
 import android.hardware.camera2.params.OisSample;
@@ -1708,6 +1709,34 @@
                 metadata.setGpsLocation((Location) value);
             }
         });
+        sSetCommandMap.put(CaptureRequest.SCALER_CROP_REGION.getNativeKey(),
+                new SetCommand() {
+            @Override
+            public <T> void setValue(CameraMetadataNative metadata, T value) {
+                metadata.setScalerCropRegion((Rect) value);
+            }
+        });
+        sSetCommandMap.put(CaptureRequest.CONTROL_AWB_REGIONS.getNativeKey(),
+                new SetCommand() {
+            @Override
+            public <T> void setValue(CameraMetadataNative metadata, T value) {
+                metadata.setAWBRegions(value);
+            }
+        });
+        sSetCommandMap.put(CaptureRequest.CONTROL_AF_REGIONS.getNativeKey(),
+                new SetCommand() {
+            @Override
+            public <T> void setValue(CameraMetadataNative metadata, T value) {
+                metadata.setAFRegions(value);
+            }
+        });
+        sSetCommandMap.put(CaptureRequest.CONTROL_AE_REGIONS.getNativeKey(),
+                new SetCommand() {
+            @Override
+            public <T> void setValue(CameraMetadataNative metadata, T value) {
+                metadata.setAERegions(value);
+            }
+        });
     }
 
     private boolean setAvailableFormats(int[] value) {
@@ -1777,6 +1806,45 @@
         return true;
     }
 
+    private <T> boolean setScalerCropRegion(Rect cropRegion) {
+        if (cropRegion == null) {
+            return false;
+        }
+        setBase(CaptureRequest.SCALER_CROP_REGION_SET, true);
+        setBase(CaptureRequest.SCALER_CROP_REGION, cropRegion);
+        return true;
+    }
+
+    private <T> boolean setAFRegions(T afRegions) {
+        if (afRegions == null) {
+            return false;
+        }
+        setBase(CaptureRequest.CONTROL_AF_REGIONS_SET, true);
+        // The cast to CaptureRequest.Key is needed since java does not support template
+        // specialization and we need to route this method to
+        // setBase(CaptureRequest.Key<T> key, T value)
+        setBase((CaptureRequest.Key)CaptureRequest.CONTROL_AF_REGIONS, afRegions);
+        return true;
+    }
+
+    private <T> boolean setAERegions(T aeRegions) {
+        if (aeRegions == null) {
+            return false;
+        }
+        setBase(CaptureRequest.CONTROL_AE_REGIONS_SET, true);
+        setBase((CaptureRequest.Key)CaptureRequest.CONTROL_AE_REGIONS, aeRegions);
+        return true;
+    }
+
+    private <T> boolean setAWBRegions(T awbRegions) {
+        if (awbRegions == null) {
+            return false;
+        }
+        setBase(CaptureRequest.CONTROL_AWB_REGIONS_SET, true);
+        setBase((CaptureRequest.Key)CaptureRequest.CONTROL_AWB_REGIONS, awbRegions);
+        return true;
+    }
+
     private void updateNativeAllocation() {
         long currentBufferSize = nativeGetBufferSize(mMetadataPtr);
 
diff --git a/core/java/android/hardware/camera2/params/InputConfiguration.java b/core/java/android/hardware/camera2/params/InputConfiguration.java
index 8dfc0a7b..70c85a1 100644
--- a/core/java/android/hardware/camera2/params/InputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/InputConfiguration.java
@@ -42,7 +42,7 @@
     private final boolean mIsMultiResolution;
 
     /**
-     * Create an input configration with the width, height, and user-defined format.
+     * Create an input configuration with the width, height, and user-defined format.
      *
      * <p>Images of a user-defined format are accessible by applications. Use
      * {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP}
@@ -64,7 +64,7 @@
     }
 
     /**
-     * Create an input configration with the format and a list of multi-resolution input stream
+     * Create an input configuration with the format and a list of multi-resolution input stream
      * info.
      *
      * <p>Use {@link
@@ -108,7 +108,7 @@
     }
 
     /**
-     * Get the width of this input configration.
+     * Get the width of this input configuration.
      *
      * @return width of this input configuration.
      */
@@ -117,7 +117,7 @@
     }
 
     /**
-     * Get the height of this input configration.
+     * Get the height of this input configuration.
      *
      * @return height of this input configuration.
      */
@@ -126,7 +126,7 @@
     }
 
     /**
-     * Get the format of this input configration.
+     * Get the format of this input configuration.
      *
      * @return format of this input configuration.
      */
diff --git a/core/java/android/hardware/camera2/utils/SurfaceUtils.java b/core/java/android/hardware/camera2/utils/SurfaceUtils.java
index 35b5c15..fd1a331 100644
--- a/core/java/android/hardware/camera2/utils/SurfaceUtils.java
+++ b/core/java/android/hardware/camera2/utils/SurfaceUtils.java
@@ -105,6 +105,20 @@
     }
 
     /**
+     * Get the surface usage bits.
+     *
+     * @param surface The surface to be queried for usage.
+     * @return the native object id of the surface, 0 if surface is not backed by a native object.
+     */
+    public static long getSurfaceUsage(Surface surface) {
+        checkNotNull(surface);
+        try {
+            return nativeDetectSurfaceUsageFlags(surface);
+        } catch (IllegalArgumentException e) {
+            return 0;
+        }
+    }
+    /**
      * Get the Surface size.
      *
      * @param surface The surface to be queried for size.
@@ -146,6 +160,23 @@
     }
 
     /**
+     * Detect and retrieve the Surface format without any
+     * additional overrides.
+     *
+     * @param surface The surface to be queried for format.
+     * @return format of the surface.
+     *
+     * @throws IllegalArgumentException if the surface is already abandoned.
+     */
+    public static int detectSurfaceFormat(Surface surface) {
+        checkNotNull(surface);
+        int surfaceType = nativeDetectSurfaceType(surface);
+        if (surfaceType == BAD_VALUE) throw new IllegalArgumentException("Surface was abandoned");
+
+        return surfaceType;
+    }
+
+    /**
      * Get the Surface dataspace.
      *
      * @param surface The surface to be queried for dataspace.
diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java
index 52dad3e..95892aa 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManager.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManager.java
@@ -75,23 +75,24 @@
     /**
      * Submits a {@link DeviceStateRequest request} to modify the device state.
      * <p>
-     * By default, the request is kept active until a call to
-     * {@link #cancelRequest(DeviceStateRequest)} or until one of the following occurs:
+     * By default, the request is kept active until one of the following occurs:
      * <ul>
+     *     <li>The system deems the request can no longer be honored, for example if the requested
+     *     state becomes unsupported.
+     *     <li>A call to {@link #cancelRequest(DeviceStateRequest)}.
      *     <li>Another processes submits a request succeeding this request in which case the request
      *     will be suspended until the interrupting request is canceled.
-     *     <li>The requested state has become unsupported.
-     *     <li>The process submitting the request dies.
      * </ul>
      * However, this behavior can be changed by setting flags on the {@link DeviceStateRequest}.
      *
      * @throws IllegalArgumentException if the requested state is unsupported.
-     * @throws SecurityException if the {@link android.Manifest.permission#CONTROL_DEVICE_STATE}
-     * permission is not held.
+     * @throws SecurityException if the caller is neither the current top-focused activity nor if
+     * the {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission is held.
      *
      * @see DeviceStateRequest
      */
-    @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
+    @RequiresPermission(value = android.Manifest.permission.CONTROL_DEVICE_STATE,
+            conditional = true)
     public void requestState(@NonNull DeviceStateRequest request,
             @Nullable @CallbackExecutor Executor executor,
             @Nullable DeviceStateRequest.Callback callback) {
@@ -105,10 +106,11 @@
      * This method is noop if the {@code request} has not been submitted with a call to
      * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
      *
-     * @throws SecurityException if the {@link android.Manifest.permission#CONTROL_DEVICE_STATE}
-     * permission is not held.
+     * @throws SecurityException if the caller is neither the current top-focused activity nor if
+     * the {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission is held.
      */
-    @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
+    @RequiresPermission(value = android.Manifest.permission.CONTROL_DEVICE_STATE,
+            conditional = true)
     public void cancelRequest(@NonNull DeviceStateRequest request) {
         mGlobal.cancelRequest(request);
     }
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index e13a7b6..5034ef1d 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -940,6 +940,34 @@
     }
 
     /**
+     * Sets the brightness configuration for the specified display.
+     * If the specified display doesn't exist, then this will return and do nothing.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)
+    public void setBrightnessConfigurationForDisplay(@NonNull BrightnessConfiguration c,
+            @NonNull String uniqueId) {
+        mGlobal.setBrightnessConfigurationForDisplay(c, uniqueId, mContext.getUserId(),
+                mContext.getPackageName());
+    }
+
+    /**
+     * Gets the brightness configuration for the specified display and default user.
+     * Returns the default configuration if unset or display is invalid.
+     *
+     * @hide
+     */
+    @Nullable
+    @SystemApi
+    @RequiresPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)
+    public BrightnessConfiguration getBrightnessConfigurationForDisplay(
+            @NonNull String uniqueId) {
+        return mGlobal.getBrightnessConfigurationForDisplay(uniqueId, mContext.getUserId());
+    }
+
+    /**
      * Sets the global display brightness configuration for a specific user.
      *
      * Note this requires the INTERACT_ACROSS_USERS permission if setting the configuration for a
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index a9b95fc..3e15c0ee 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -705,6 +705,34 @@
     }
 
     /**
+     * Sets the brightness configuration for a given display.
+     *
+     * @hide
+     */
+    public void setBrightnessConfigurationForDisplay(BrightnessConfiguration c,
+            String uniqueDisplayId, int userId, String packageName) {
+        try {
+            mDm.setBrightnessConfigurationForDisplay(c, uniqueDisplayId, userId, packageName);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Gets the brightness configuration for a given display or null if one hasn't been set.
+     *
+     * @hide
+     */
+    public BrightnessConfiguration getBrightnessConfigurationForDisplay(String uniqueDisplayId,
+            int userId) {
+        try {
+            return mDm.getBrightnessConfigurationForDisplay(uniqueDisplayId, userId);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Gets the global brightness configuration for a given user or null if one hasn't been set.
      *
      * @hide
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index abcc33c..4f20553 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -21,6 +21,7 @@
 import android.graphics.Point;
 import android.hardware.SensorManager;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.PowerManager;
 import android.util.IntArray;
 import android.util.Slog;
@@ -340,6 +341,28 @@
     public abstract List<RefreshRateLimitation> getRefreshRateLimitations(int displayId);
 
     /**
+     * Returns the window token of the level of the WindowManager hierarchy to mirror. Returns null
+     * if layer mirroring by SurfaceFlinger should not be performed for the given displayId.
+     * For now, only used for mirroring started from MediaProjection.
+     */
+    public abstract IBinder getWindowTokenClientToMirror(int displayId);
+
+    /**
+     * For the given displayId, updates the window token of the level of the WindowManager hierarchy
+     * to mirror. If windowToken is null, then SurfaceFlinger performs no layer mirroring to the
+     * given display.
+     * For now, only used for mirroring started from MediaProjection.
+     */
+    public abstract void setWindowTokenClientToMirror(int displayId, IBinder windowToken);
+
+    /**
+     * Returns the default size of the surface associated with the display, or null if the surface
+     * is not provided for layer mirroring by SurfaceFlinger.
+     * For now, only used for mirroring started from MediaProjection.
+     */
+    public abstract Point getDisplaySurfaceDefaultSize(int displayId);
+
+    /**
      * Describes the requested power state of the display.
      *
      * This object is intended to describe the general characteristics of the
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 2303353..1162146 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -118,6 +118,16 @@
     void setBrightnessConfigurationForUser(in BrightnessConfiguration c, int userId,
             String packageName);
 
+    // Sets the global brightness configuration for a given display. Requires
+    // CONFIGURE_DISPLAY_BRIGHTNESS.
+    void setBrightnessConfigurationForDisplay(in BrightnessConfiguration c, String uniqueDisplayId,
+            int userId, String packageName);
+
+    // Gets the brightness configuration for a given display. Requires
+    // CONFIGURE_DISPLAY_BRIGHTNESS.
+    BrightnessConfiguration getBrightnessConfigurationForDisplay(String uniqueDisplayId,
+            int userId);
+
     // Gets the global brightness configuration for a given user. Requires
     // CONFIGURE_DISPLAY_BRIGHTNESS, and INTERACT_ACROSS_USER if the user is not
     // the same as the calling user.
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java
index 71688c7c..0e86f43 100644
--- a/core/java/android/hardware/display/VirtualDisplayConfig.java
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -23,6 +23,7 @@
 import android.annotation.Nullable;
 import android.media.projection.MediaProjection;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.view.Surface;
@@ -91,9 +92,16 @@
      */
     private int mDisplayIdToMirror = DEFAULT_DISPLAY;
 
+    /**
+     * The window token of the level of the WindowManager hierarchy to mirror, or null if mirroring
+     * should not be performed.
+     */
+    @Nullable
+    private IBinder mWindowTokenClientToMirror = null;
 
 
-    // Code below generated by codegen v1.0.20.
+
+    // Code below generated by codegen v1.0.23.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -115,7 +123,8 @@
             int flags,
             @Nullable Surface surface,
             @Nullable String uniqueId,
-            int displayIdToMirror) {
+            int displayIdToMirror,
+            @Nullable IBinder windowTokenClientToMirror) {
         this.mName = name;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mName);
@@ -135,6 +144,7 @@
         this.mSurface = surface;
         this.mUniqueId = uniqueId;
         this.mDisplayIdToMirror = displayIdToMirror;
+        this.mWindowTokenClientToMirror = windowTokenClientToMirror;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -212,6 +222,15 @@
         return mDisplayIdToMirror;
     }
 
+    /**
+     * The window token of the level of the WindowManager hierarchy to mirror, or null if mirroring
+     * should not be performed.
+     */
+    @DataClass.Generated.Member
+    public @Nullable IBinder getWindowTokenClientToMirror() {
+        return mWindowTokenClientToMirror;
+    }
+
     @Override
     @DataClass.Generated.Member
     public void writeToParcel(@NonNull Parcel dest, int flags) {
@@ -221,6 +240,7 @@
         int flg = 0;
         if (mSurface != null) flg |= 0x20;
         if (mUniqueId != null) flg |= 0x40;
+        if (mWindowTokenClientToMirror != null) flg |= 0x100;
         dest.writeInt(flg);
         dest.writeString(mName);
         dest.writeInt(mWidth);
@@ -230,6 +250,7 @@
         if (mSurface != null) dest.writeTypedObject(mSurface, flags);
         if (mUniqueId != null) dest.writeString(mUniqueId);
         dest.writeInt(mDisplayIdToMirror);
+        if (mWindowTokenClientToMirror != null) dest.writeStrongBinder(mWindowTokenClientToMirror);
     }
 
     @Override
@@ -252,6 +273,7 @@
         Surface surface = (flg & 0x20) == 0 ? null : (Surface) in.readTypedObject(Surface.CREATOR);
         String uniqueId = (flg & 0x40) == 0 ? null : in.readString();
         int displayIdToMirror = in.readInt();
+        IBinder windowTokenClientToMirror = (flg & 0x100) == 0 ? null : (IBinder) in.readStrongBinder();
 
         this.mName = name;
         com.android.internal.util.AnnotationValidations.validate(
@@ -272,6 +294,7 @@
         this.mSurface = surface;
         this.mUniqueId = uniqueId;
         this.mDisplayIdToMirror = displayIdToMirror;
+        this.mWindowTokenClientToMirror = windowTokenClientToMirror;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -305,6 +328,7 @@
         private @Nullable Surface mSurface;
         private @Nullable String mUniqueId;
         private int mDisplayIdToMirror;
+        private @Nullable IBinder mWindowTokenClientToMirror;
 
         private long mBuilderFieldsSet = 0L;
 
@@ -439,10 +463,22 @@
             return this;
         }
 
+        /**
+         * The window token of the level of the WindowManager hierarchy to mirror, or null if mirroring
+         * should not be performed.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setWindowTokenClientToMirror(@NonNull IBinder value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x100;
+            mWindowTokenClientToMirror = value;
+            return this;
+        }
+
         /** Builds the instance. This builder should not be touched after calling this! */
         public @NonNull VirtualDisplayConfig build() {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x100; // Mark builder used
+            mBuilderFieldsSet |= 0x200; // Mark builder used
 
             if ((mBuilderFieldsSet & 0x10) == 0) {
                 mFlags = 0;
@@ -456,6 +492,9 @@
             if ((mBuilderFieldsSet & 0x80) == 0) {
                 mDisplayIdToMirror = DEFAULT_DISPLAY;
             }
+            if ((mBuilderFieldsSet & 0x100) == 0) {
+                mWindowTokenClientToMirror = null;
+            }
             VirtualDisplayConfig o = new VirtualDisplayConfig(
                     mName,
                     mWidth,
@@ -464,12 +503,13 @@
                     mFlags,
                     mSurface,
                     mUniqueId,
-                    mDisplayIdToMirror);
+                    mDisplayIdToMirror,
+                    mWindowTokenClientToMirror);
             return o;
         }
 
         private void checkNotUsed() {
-            if ((mBuilderFieldsSet & 0x100) != 0) {
+            if ((mBuilderFieldsSet & 0x200) != 0) {
                 throw new IllegalStateException(
                         "This Builder should not be reused. Use a new Builder instance instead");
             }
@@ -477,10 +517,10 @@
     }
 
     @DataClass.Generated(
-            time = 1604456298440L,
-            codegenVersion = "1.0.20",
+            time = 1620657851981L,
+            codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/hardware/display/VirtualDisplayConfig.java",
-            inputSignatures = "private @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.IntRange int mWidth\nprivate @android.annotation.IntRange int mHeight\nprivate @android.annotation.IntRange int mDensityDpi\nprivate  int mFlags\nprivate @android.annotation.Nullable android.view.Surface mSurface\nprivate @android.annotation.Nullable java.lang.String mUniqueId\nprivate  int mDisplayIdToMirror\nclass VirtualDisplayConfig extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true)")
+            inputSignatures = "private @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.IntRange int mWidth\nprivate @android.annotation.IntRange int mHeight\nprivate @android.annotation.IntRange int mDensityDpi\nprivate  int mFlags\nprivate @android.annotation.Nullable android.view.Surface mSurface\nprivate @android.annotation.Nullable java.lang.String mUniqueId\nprivate  int mDisplayIdToMirror\nprivate @android.annotation.Nullable android.os.IBinder mWindowTokenClientToMirror\nclass VirtualDisplayConfig extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 3c3ba59..385ad2d 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -790,6 +790,11 @@
                 }
             }
         }
+
+        // This is used as a last resort in case a vendor string is missing
+        // It should not happen for anything other than FACE_ERROR_VENDOR, but
+        // warn and use the default if all else fails.
+        // TODO(b/196639965): update string
         Slog.w(TAG, "Invalid error message: " + errMsg + ", " + vendorCode);
         return "";
     }
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index dc1a50f..87d45b9 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -1386,8 +1386,13 @@
                 }
             }
         }
+
+        // This is used as a last resort in case a vendor string is missing
+        // It should not happen for anything other than FINGERPRINT_ERROR_VENDOR, but
+        // warn and use the default if all else fails.
+        // TODO(b/196639965): update string
         Slog.w(TAG, "Invalid error message: " + errMsg + ", " + vendorCode);
-        return null;
+        return "";
     }
 
     /**
diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java
index 17d4ae6..f8f0970 100644
--- a/core/java/android/inputmethodservice/AbstractInputMethodService.java
+++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java
@@ -309,7 +309,7 @@
     @Override
     @Nullable
     public final Bundle getWindowContextOptions() {
-        return null;
+        return super.getWindowContextOptions();
     }
 
     /** @hide */
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 42fa9fb..c0f0081 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1732,12 +1732,12 @@
         if (config.orientation != Configuration.ORIENTATION_LANDSCAPE) {
             return false;
         }
-        if ((mInputEditorInfo != null
-                && (mInputEditorInfo.imeOptions & EditorInfo.IME_FLAG_NO_FULLSCREEN) != 0)
+        if (mInputEditorInfo != null
+                && ((mInputEditorInfo.imeOptions & EditorInfo.IME_FLAG_NO_FULLSCREEN) != 0
                 // If app window has portrait orientation, regardless of what display orientation
                 // is, IME shouldn't use fullscreen-mode.
                 || (mInputEditorInfo.internalImeOptions
-                        & EditorInfo.IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT) != 0) {
+                        & EditorInfo.IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT) != 0)) {
             return false;
         }
         return true;
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index d44b016..3d466a0 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -560,6 +560,9 @@
             }
         }
 
+        final AppOpsManager.PausedNotedAppOpsCollection prevCollection =
+                AppOpsManager.pauseNotedAppOpsCollection();
+
         if ((flags & FLAG_ONEWAY) == 0 && AppOpsManager.isListeningForOpNoted()) {
             flags |= FLAG_COLLECT_NOTED_APP_OPS;
         }
@@ -567,6 +570,8 @@
         try {
             return transactNative(code, data, reply, flags);
         } finally {
+            AppOpsManager.resumeNotedAppOpsCollection(prevCollection);
+
             if (transactListener != null) {
                 transactListener.onTransactEnded(session);
             }
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index f3e0ce9..edfcb3d 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -1460,17 +1460,17 @@
     /** {@hide} */
     @VisibleForTesting
     public static ParcelFileDescriptor convertToModernFd(FileDescriptor fd) {
-        try {
-            Context context = AppGlobals.getInitialApplication();
-            if (UserHandle.getAppId(Process.myUid()) == getMediaProviderAppId(context)) {
-                // Never convert modern fd for MediaProvider, because this requires
-                // MediaStore#scanFile and can cause infinite loops when MediaProvider scans
-                return null;
-            }
-            return MediaStore.getOriginalMediaFormatFileDescriptor(context,
-                    ParcelFileDescriptor.dup(fd));
+        Context context = AppGlobals.getInitialApplication();
+        if (UserHandle.getAppId(Process.myUid()) == getMediaProviderAppId(context)) {
+            // Never convert modern fd for MediaProvider, because this requires
+            // MediaStore#scanFile and can cause infinite loops when MediaProvider scans
+            return null;
+        }
+
+        try (ParcelFileDescriptor dupFd = ParcelFileDescriptor.dup(fd)) {
+            return MediaStore.getOriginalMediaFormatFileDescriptor(context, dupFd);
         } catch (Exception e) {
-            Log.d(TAG, "Failed to convert to modern format file descriptor", e);
+            // Ignore error
             return null;
         }
     }
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index be21fea..1651bfc 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -26,8 +26,6 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.content.res.AssetFileDescriptor;
-import android.content.res.AssetManager;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
@@ -37,9 +35,6 @@
 
 import java.io.BufferedReader;
 import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.util.ArrayList;
@@ -88,9 +83,6 @@
     private static final String UPDATABLE_DRIVER_ALLOWLIST_ALL = "*";
     private static final String UPDATABLE_DRIVER_SPHAL_LIBRARIES_FILENAME = "sphal_libraries.txt";
 
-    // ANGLE related properties.
-    private static final String ANGLE_RULES_FILE = "a4a_rules.json";
-    private static final String ANGLE_TEMP_RULES = "debug.angle.rules";
     private static final String ACTION_ANGLE_FOR_ANDROID = "android.app.action.ANGLE_FOR_ANDROID";
     private static final String ACTION_ANGLE_FOR_ANDROID_TOAST_MESSAGE =
             "android.app.action.ANGLE_FOR_ANDROID_TOAST_MESSAGE";
@@ -164,21 +156,14 @@
         Log.v(TAG, "ANGLE Developer option for '" + packageName + "' "
                 + "set to: '" + devOptIn + "'");
 
-        // We only want to use ANGLE if the app is in the allowlist, or the developer has
-        // explicitly chosen something other than default driver.
-        // The allowlist will be generated by the ANGLE APK at both boot time and
-        // ANGLE update time. It will only include apps mentioned in the rules file.
-        final boolean allowed = checkAngleAllowlist(context, coreSettings, packageName);
+        // We only want to use ANGLE if the developer has explicitly chosen something other than
+        // default driver.
         final boolean requested = devOptIn.equals(ANGLE_GL_DRIVER_CHOICE_ANGLE);
-
-        if (allowed) {
-            Log.v(TAG, "ANGLE allowlist includes " + packageName);
-        }
         if (requested) {
             Log.v(TAG, "ANGLE developer option for " + packageName + ": " + devOptIn);
         }
 
-        return allowed || requested;
+        return requested;
     }
 
     private int getVulkanVersion(PackageManager pm) {
@@ -475,117 +460,6 @@
     }
 
     /**
-     * Attempt to setup ANGLE with a temporary rules file.
-     * True: Temporary rules file was loaded.
-     * False: Temporary rules file was *not* loaded.
-     */
-    private boolean setupAngleWithTempRulesFile(Context context,
-                                                String packageName,
-                                                String paths,
-                                                String devOptIn) {
-        /**
-         * We only want to load a temp rules file for:
-         *  - apps that are marked 'debuggable' in their manifest
-         *  - devices that are running a userdebug build (ro.debuggable) or can inject libraries for
-         *    debugging (PR_SET_DUMPABLE).
-         */
-        if (!isDebuggable()) {
-            Log.v(TAG, "Skipping loading temporary rules file");
-            return false;
-        }
-
-        final String angleTempRules = SystemProperties.get(ANGLE_TEMP_RULES);
-
-        if (TextUtils.isEmpty(angleTempRules)) {
-            Log.v(TAG, "System property '" + ANGLE_TEMP_RULES + "' is not set or is empty");
-            return false;
-        }
-
-        Log.i(TAG, "Detected system property " + ANGLE_TEMP_RULES + ": " + angleTempRules);
-
-        final File tempRulesFile = new File(angleTempRules);
-        if (tempRulesFile.exists()) {
-            Log.i(TAG, angleTempRules + " exists, loading file.");
-            try {
-                final FileInputStream stream = new FileInputStream(angleTempRules);
-
-                try {
-                    final FileDescriptor rulesFd = stream.getFD();
-                    final long rulesOffset = 0;
-                    final long rulesLength = stream.getChannel().size();
-                    Log.i(TAG, "Loaded temporary ANGLE rules from " + angleTempRules);
-
-                    setAngleInfo(paths, packageName, devOptIn, null,
-                            rulesFd, rulesOffset, rulesLength);
-
-                    stream.close();
-
-                    // We successfully setup ANGLE, so return with good status
-                    return true;
-                } catch (IOException e) {
-                    Log.w(TAG, "Hit IOException thrown by FileInputStream: " + e);
-                }
-            } catch (FileNotFoundException e) {
-                Log.w(TAG, "Temp ANGLE rules file not found: " + e);
-            } catch (SecurityException e) {
-                Log.w(TAG, "Temp ANGLE rules file not accessible: " + e);
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Attempt to setup ANGLE with a rules file loaded from the ANGLE APK.
-     * True: APK rules file was loaded.
-     * False: APK rules file was *not* loaded.
-     */
-    private boolean setupAngleRulesApk(String anglePkgName,
-            ApplicationInfo angleInfo,
-            PackageManager pm,
-            String packageName,
-            String paths,
-            String devOptIn,
-            String[] features) {
-        // Pass the rules file to loader for ANGLE decisions
-        try {
-            final AssetManager angleAssets = pm.getResourcesForApplication(angleInfo).getAssets();
-
-            try {
-                final AssetFileDescriptor assetsFd = angleAssets.openFd(ANGLE_RULES_FILE);
-
-                setAngleInfo(paths, packageName, devOptIn, features, assetsFd.getFileDescriptor(),
-                        assetsFd.getStartOffset(), assetsFd.getLength());
-
-                assetsFd.close();
-
-                return true;
-            } catch (IOException e) {
-                Log.w(TAG, "Failed to get AssetFileDescriptor for " + ANGLE_RULES_FILE
-                        + " from '" + anglePkgName + "': " + e);
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            Log.w(TAG, "Failed to get AssetManager for '" + anglePkgName + "': " + e);
-        }
-
-        return false;
-    }
-
-    /**
-     * Pull ANGLE allowlist from GlobalSettings and compare against current package
-     */
-    private boolean checkAngleAllowlist(Context context, Bundle bundle, String packageName) {
-        final ContentResolver contentResolver = context.getContentResolver();
-        final List<String> angleAllowlist =
-                getGlobalSettingsString(contentResolver, bundle,
-                    Settings.Global.ANGLE_ALLOWLIST);
-
-        if (DEBUG) Log.v(TAG, "ANGLE allowlist: " + angleAllowlist);
-
-        return angleAllowlist.contains(packageName);
-    }
-
-    /**
      * Pass ANGLE details down to trigger enable logic
      *
      * @param context
@@ -648,27 +522,16 @@
         if (DEBUG) Log.v(TAG, "ANGLE package libs: " + paths);
 
         // If the user has set the developer option to something other than default,
-        // we need to call setupAngleRulesApk() with the package name and the developer
+        // we need to call setAngleInfo() with the package name and the developer
         // option value (native/angle/other). Then later when we are actually trying to
         // load a driver, GraphicsEnv::getShouldUseAngle() has seen the package name before
         // and can confidently answer yes/no based on the previously set developer
         // option value.
         final String devOptIn = getDriverForPackage(context, bundle, packageName);
+        final String[] features = getAngleEglFeatures(context, bundle);
 
-        if (setupAngleWithTempRulesFile(context, packageName, paths, devOptIn)) {
-            // We setup ANGLE with a temp rules file, so we're done here.
-            return true;
-        }
-
-        String[] features = getAngleEglFeatures(context, bundle);
-
-        if (setupAngleRulesApk(
-                anglePkgName, angleInfo, pm, packageName, paths, devOptIn, features)) {
-            // ANGLE with rules is set up from the APK, hence return.
-            return true;
-        }
-
-        return false;
+        setAngleInfo(paths, packageName, devOptIn, features);
+        return true;
     }
 
     /**
@@ -956,7 +819,7 @@
     private static native void setGpuStats(String driverPackageName, String driverVersionName,
             long driverVersionCode, long driverBuildTime, String appPackageName, int vulkanVersion);
     private static native void setAngleInfo(String path, String appPackage, String devOptIn,
-            String[] features, FileDescriptor rulesFd, long rulesOffset, long rulesLength);
+            String[] features);
     private static native boolean getShouldUseAngle(String packageName);
     private static native boolean setInjectLayersPrSetDumpable();
 
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 6bca336..9f37c48 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -34,12 +34,9 @@
 
 import libcore.io.IoUtils;
 
-import java.io.BufferedReader;
 import java.io.FileDescriptor;
-import java.io.FileReader;
 import java.io.IOException;
 import java.util.Map;
-import java.util.StringTokenizer;
 import java.util.concurrent.TimeoutException;
 
 /**
@@ -1472,43 +1469,4 @@
     }
 
     private static native int nativePidFdOpen(int pid, int flags) throws ErrnoException;
-
-    /**
-     * Checks if a process corresponding to a specific pid owns any file locks.
-     * @param pid The process ID for which we want to know the existence of file locks.
-     * @return true If the process holds any file locks, false otherwise.
-     * @throws IOException if /proc/locks can't be accessed.
-     *
-     * @hide
-     */
-    public static boolean hasFileLocks(int pid) throws Exception {
-        BufferedReader br = null;
-
-        try {
-            br = new BufferedReader(new FileReader("/proc/locks"));
-            String line;
-
-            while ((line = br.readLine()) != null) {
-                StringTokenizer st = new StringTokenizer(line);
-
-                for (int i = 0; i < 5 && st.hasMoreTokens(); i++) {
-                    String str = st.nextToken();
-                    try {
-                        if (i == 4 && Integer.parseInt(str) == pid) {
-                            return true;
-                        }
-                    } catch (NumberFormatException nfe) {
-                        throw new Exception("Exception parsing /proc/locks at \" "
-                                + line +  " \", token #" + i);
-                    }
-                }
-            }
-
-            return false;
-        } finally {
-            if (br != null) {
-                br.close();
-            }
-        }
-    }
 }
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 9ab6955..4a94c32 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -16,7 +16,7 @@
 
 package android.permission;
 
-import android.content.AttributionSource;
+import android.content.AttributionSourceState;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
@@ -87,7 +87,7 @@
 
     boolean isAutoRevokeExempted(String packageName, int userId);
 
-    void registerAttributionSource(in AttributionSource source);
+    void registerAttributionSource(in AttributionSourceState source);
 
-    boolean isRegisteredAttributionSource(in AttributionSource source);
+    boolean isRegisteredAttributionSource(in AttributionSourceState source);
 }
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index a52ede8..63bcc9c 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -1177,7 +1177,7 @@
         // enforcement we need to replace the binder with a unique one.
         final AttributionSource registeredSource = source.withToken(new Binder());
         try {
-            mPermissionManager.registerAttributionSource(registeredSource);
+            mPermissionManager.registerAttributionSource(registeredSource.asState());
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
         }
@@ -1196,7 +1196,7 @@
      */
     public boolean isRegisteredAttributionSource(@NonNull AttributionSource source) {
         try {
-            return mPermissionManager.isRegisteredAttributionSource(source);
+            return mPermissionManager.isRegisteredAttributionSource(source.asState());
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index 03f94c5..19f204b 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -31,7 +31,9 @@
 import static android.app.AppOpsManager.OPSTR_PHONE_CALL_CAMERA;
 import static android.app.AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE;
 import static android.app.AppOpsManager.OPSTR_RECORD_AUDIO;
+import static android.app.AppOpsManager.OP_CAMERA;
 import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED;
+import static android.app.AppOpsManager.OP_RECORD_AUDIO;
 import static android.media.AudioSystem.MODE_IN_COMMUNICATION;
 import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
 
@@ -63,7 +65,8 @@
  *
  * @hide
  */
-public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedListener {
+public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedListener,
+        AppOpsManager.OnOpStartedListener {
 
     /** Whether to show the mic and camera icons.  */
     private static final String PROPERTY_CAMERA_MIC_ICONS_ENABLED = "camera_mic_icons_enabled";
@@ -160,9 +163,10 @@
         mUserContexts = new ArrayMap<>();
         mUserContexts.put(Process.myUserHandle(), mContext);
         // TODO ntmyren: make this listen for flag enable/disable changes
-        String[] ops = { OPSTR_CAMERA, OPSTR_RECORD_AUDIO };
-        mContext.getSystemService(AppOpsManager.class).startWatchingActive(ops,
-                context.getMainExecutor(), this);
+        String[] opStrs = { OPSTR_CAMERA, OPSTR_RECORD_AUDIO };
+        mAppOpsManager.startWatchingActive(opStrs, context.getMainExecutor(), this);
+        int[] ops = { OP_CAMERA, OP_RECORD_AUDIO };
+        mAppOpsManager.startWatchingStarted(ops, this);
     }
 
     private Context getUserContext(UserHandle user) {
@@ -182,25 +186,65 @@
     public void onOpActiveChanged(@NonNull String op, int uid, @NonNull String packageName,
             @Nullable String attributionTag, boolean active, @AttributionFlags int attributionFlags,
             int attributionChainId) {
-        if (attributionChainId == ATTRIBUTION_CHAIN_ID_NONE
-                || attributionFlags == ATTRIBUTION_FLAGS_NONE
-                || (attributionFlags & ATTRIBUTION_FLAG_TRUSTED) == 0) {
-            // If this is not a chain, or it is untrusted, return
+        if (active) {
+            // Started callback handles these
             return;
         }
 
-        if (!active) {
-            // if any link in the chain is finished, remove the chain.
-            // TODO ntmyren: be smarter about this
-            mAttributionChains.remove(attributionChainId);
+        // if any link in the chain is finished, remove the chain. Then, find any other chains that
+        // contain this op/package/uid/tag combination, and remove them, as well.
+        // TODO ntmyren: be smarter about this
+        mAttributionChains.remove(attributionChainId);
+        int numChains = mAttributionChains.size();
+        ArrayList<Integer> toRemove = new ArrayList<>();
+        for (int i = 0; i < numChains; i++) {
+            int chainId = mAttributionChains.keyAt(i);
+            ArrayList<AccessChainLink> chain = mAttributionChains.valueAt(i);
+            int chainSize = chain.size();
+            for (int j = 0; j < chainSize; j++) {
+                AccessChainLink link = chain.get(j);
+                if (link.packageAndOpEquals(op, packageName, attributionTag, uid)) {
+                    toRemove.add(chainId);
+                    break;
+                }
+            }
+        }
+        mAttributionChains.removeAll(toRemove);
+    }
+
+    @Override
+    public void onOpStarted(int op, int uid, String packageName, String attributionTag,
+                @AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result) {
+       // not part of an attribution chain. Do nothing
+    }
+
+    @Override
+    public void onOpStarted(int op, int uid, String packageName, String attributionTag,
+            @AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result,
+            @StartedType int startedType, @AttributionFlags int attributionFlags,
+            int attributionChainId) {
+        if (startedType == START_TYPE_FAILED || attributionChainId == ATTRIBUTION_CHAIN_ID_NONE
+                || attributionFlags == ATTRIBUTION_FLAGS_NONE
+                || (attributionFlags & ATTRIBUTION_FLAG_TRUSTED) == 0) {
+            // If this is not a successful start, or it is not a chain, or it is untrusted, return
             return;
         }
+        addLinkToChainIfNotPresent(AppOpsManager.opToPublicName(op), packageName, uid,
+                attributionTag, attributionFlags, attributionChainId);
+    }
+
+    private void addLinkToChainIfNotPresent(String op, String packageName, int uid,
+            String attributionTag, int attributionFlags, int attributionChainId) {
 
         ArrayList<AccessChainLink> currentChain = mAttributionChains.computeIfAbsent(
                 attributionChainId, k -> new ArrayList<>());
         AccessChainLink link = new AccessChainLink(op, packageName, attributionTag, uid,
                 attributionFlags);
 
+        if (currentChain.contains(link)) {
+            return;
+        }
+
         int currSize = currentChain.size();
         if (currSize == 0 || link.isEnd() || !currentChain.get(currSize - 1).isEnd()) {
             // if the list is empty, this link is the end, or the last link in the current chain
@@ -613,5 +657,21 @@
         public boolean isStart() {
             return (flags & ATTRIBUTION_FLAG_RECEIVER) != 0;
         }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof AccessChainLink)) {
+                return false;
+            }
+            AccessChainLink other = (AccessChainLink) obj;
+            return other.flags == flags && packageAndOpEquals(other.usage.op,
+                    other.usage.packageName, other.usage.attributionTag, other.usage.uid);
+        }
+
+        public boolean packageAndOpEquals(String op, String packageName, String attributionTag,
+                int uid) {
+            return Objects.equals(op, usage.op) && Objects.equals(packageName, usage.packageName)
+                    && Objects.equals(attributionTag, usage.attributionTag) && uid == usage.uid;
+        }
     }
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 589ecc0..99cb4c5 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10119,6 +10119,61 @@
         @Readable
         public static final String GAME_DASHBOARD_ALWAYS_ON = "game_dashboard_always_on";
 
+
+        /**
+         * For this device state, no specific auto-rotation lock setting should be applied.
+         * If the user toggles the auto-rotate lock in this state, the setting will apply to the
+         * previously valid device state.
+         * @hide
+         */
+        public static final int DEVICE_STATE_ROTATION_LOCK_IGNORED = 0;
+        /**
+         * For this device state, the setting for auto-rotation is locked.
+         * @hide
+         */
+        public static final int DEVICE_STATE_ROTATION_LOCK_LOCKED = 1;
+        /**
+         * For this device state, the setting for auto-rotation is unlocked.
+         * @hide
+         */
+        public static final int DEVICE_STATE_ROTATION_LOCK_UNLOCKED = 2;
+
+        /**
+         * The different settings that can be used as values with
+         * {@link #DEVICE_STATE_ROTATION_LOCK}.
+         * @hide
+         */
+        @IntDef(prefix = {"DEVICE_STATE_ROTATION_LOCK_"}, value = {
+                DEVICE_STATE_ROTATION_LOCK_IGNORED,
+                DEVICE_STATE_ROTATION_LOCK_LOCKED,
+                DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        @interface DeviceStateRotationLockSetting {
+        }
+
+        /**
+         * Rotation lock setting keyed on device state.
+         *
+         * This holds a serialized map using int keys that represent Device States and value of
+         * {@link DeviceStateRotationLockSetting} representing the rotation lock setting for that
+         * device state.
+         *
+         * Serialized as key0:value0:key1:value1:...:keyN:valueN.
+         *
+         * Example: "0:1:1:2:2:1"
+         * This example represents a map of:
+         * <ul>
+         *     <li>0 -> DEVICE_STATE_ROTATION_LOCK_LOCKED</li>
+         *     <li>1 -> DEVICE_STATE_ROTATION_LOCK_UNLOCKED</li>
+         *     <li>2 -> DEVICE_STATE_ROTATION_LOCK_IGNORED</li>
+         * </ul>
+         *
+         * @hide
+         */
+        public static final String DEVICE_STATE_ROTATION_LOCK =
+                "device_state_rotation_lock";
+
         /**
          * These entries are considered common between the personal and the managed profile,
          * since the managed profile doesn't get to change them.
@@ -13681,13 +13736,6 @@
                 "angle_gl_driver_selection_values";
 
         /**
-         * List of package names that should check ANGLE rules
-         * @hide
-         */
-        @Readable
-        public static final String ANGLE_ALLOWLIST = "angle_allowlist";
-
-        /**
          * Lists of ANGLE EGL features for debugging.
          * Each list of features is separated by a comma, each feature in each list is separated by
          * a colon.
@@ -14926,6 +14974,16 @@
                 "power_button_long_press";
 
         /**
+         * Override internal R.integer.config_longPressOnPowerDurationMs. It determines the length
+         * of power button press to be considered a long press in milliseconds.
+         * Used by PhoneWindowManager.
+         * @hide
+         */
+        @Readable
+        public static final String POWER_BUTTON_LONG_PRESS_DURATION_MS =
+                "power_button_long_press_duration_ms";
+
+        /**
          * Overrides internal R.integer.config_veryLongPressOnPowerBehavior.
          * Allowable values detailed in frameworks/base/core/res/res/values/config.xml.
          * Used by PhoneWindowManager.
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 4137416..face870 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -783,6 +783,9 @@
      *         This may happen if another detector has been instantiated or the
      *         {@link VoiceInteractionService} hosting this detector has been shut down.
      */
+    // TODO: Remove this RequiresPermission since it isn't actually enforced. Also fix the javadoc
+    // about permissions enforcement (when it throws vs when it just returns false) for other
+    // methods in this class.
     @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
     @Override
     public boolean stopRecognition() {
diff --git a/core/java/android/service/voice/SoftwareHotwordDetector.java b/core/java/android/service/voice/SoftwareHotwordDetector.java
index 02294e5..f7a3415 100644
--- a/core/java/android/service/voice/SoftwareHotwordDetector.java
+++ b/core/java/android/service/voice/SoftwareHotwordDetector.java
@@ -82,6 +82,9 @@
         try {
             mManagerService.startListeningFromMic(
                     mAudioFormat, new BinderCallback(mHandler, mCallback));
+        } catch (SecurityException e) {
+            Slog.e(TAG, "startRecognition failed: " + e);
+            return false;
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 6a0fec7..f52c9ff 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -520,6 +520,12 @@
             // Ignore.
         }
 
+        try {
+            mSystemService.shutdownHotwordDetectionService();
+        } catch (Exception ex) {
+            // Ignore.
+        }
+
         mSoftwareHotwordDetector = null;
     }
 
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 1111c28..4bf6049 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -16,6 +16,8 @@
 
 package android.service.wallpaper;
 
+import static android.app.WallpaperManager.COMMAND_FREEZE;
+import static android.app.WallpaperManager.COMMAND_UNFREEZE;
 import static android.graphics.Matrix.MSCALE_X;
 import static android.graphics.Matrix.MSCALE_Y;
 import static android.graphics.Matrix.MSKEW_X;
@@ -42,12 +44,14 @@
 import android.graphics.BLASTBufferQueue;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.GraphicBuffer;
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
+import android.hardware.HardwareBuffer;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManager.DisplayListener;
 import android.os.Build;
@@ -56,6 +60,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.SystemProperties;
@@ -200,6 +205,12 @@
         boolean mVisible;
         boolean mReportedVisible;
         boolean mDestroyed;
+        // Set to true after receiving WallpaperManager#COMMAND_FREEZE. It's reset back to false
+        // after receiving WallpaperManager#COMMAND_UNFREEZE. COMMAND_FREEZE is fully applied once
+        // mScreenshotSurfaceControl isn't null. When this happens, then Engine is notified through
+        // doVisibilityChanged that main wallpaper surface is no longer visible and the wallpaper
+        // host receives onVisibilityChanged(false) callback.
+        private boolean mFrozenRequested = false;
 
         // Current window state.
         boolean mCreated;
@@ -264,6 +275,8 @@
         SurfaceControl mSurfaceControl = new SurfaceControl();
         SurfaceControl mBbqSurfaceControl;
         BLASTBufferQueue mBlastBufferQueue;
+        private SurfaceControl mScreenshotSurfaceControl;
+        private Point mScreenshotSize = new Point();
 
         final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
             {
@@ -776,8 +789,10 @@
                 WallpaperColors color = colors.get(i);
                 RectF area = regions.get(i);
                 if (color == null || area == null) {
-                    Log.wtf(TAG, "notifyLocalColorsChanged null values. color: "
-                            + color + " area " + area);
+                    if (DEBUG) {
+                        Log.e(TAG, "notifyLocalColorsChanged null values. color: "
+                                + color + " area " + area);
+                    }
                     continue;
                 }
                 try {
@@ -1347,11 +1362,15 @@
             if (!mDestroyed) {
                 mVisible = visible;
                 reportVisibility();
-                if (visible) processLocalColors(mPendingXOffset, mPendingXOffsetStep);
+                if (mReportedVisible) processLocalColors(mPendingXOffset, mPendingXOffsetStep);
             }
         }
 
         void reportVisibility() {
+            if (mScreenshotSurfaceControl != null && mVisible) {
+                if (DEBUG) Log.v(TAG, "Frozen so don't report visibility change");
+                return;
+            }
             if (!mDestroyed) {
                 mDisplayState = mDisplay == null ? Display.STATE_UNKNOWN : mDisplay.getState();
                 boolean visible = mVisible && mDisplayState != Display.STATE_OFF;
@@ -1368,6 +1387,10 @@
                         updateSurface(true, false, false);
                     }
                     onVisibilityChanged(visible);
+                    if (mReportedVisible && mFrozenRequested) {
+                        if (DEBUG) Log.v(TAG, "Freezing wallpaper after visibility update");
+                        freeze();
+                    }
                 }
             }
         }
@@ -1828,6 +1851,9 @@
         void doCommand(WallpaperCommand cmd) {
             Bundle result;
             if (!mDestroyed) {
+                if (COMMAND_FREEZE.equals(cmd.action) || COMMAND_UNFREEZE.equals(cmd.action)) {
+                    updateFrozenState(/* frozenRequested= */ !COMMAND_UNFREEZE.equals(cmd.action));
+                }
                 result = onCommand(cmd.action, cmd.x, cmd.y, cmd.z,
                         cmd.extras, cmd.sync);
             } else {
@@ -1842,6 +1868,159 @@
             }
         }
 
+        private void updateFrozenState(boolean frozenRequested) {
+            if (mIWallpaperEngine.mWallpaperManager.getWallpaperInfo() == null
+                    // Procees the unfreeze command in case the wallaper became static while
+                    // being paused.
+                    && frozenRequested) {
+                if (DEBUG) Log.v(TAG, "Ignoring the freeze command for static wallpapers");
+                return;
+            }
+            mFrozenRequested = frozenRequested;
+            boolean isFrozen = mScreenshotSurfaceControl != null;
+            if (mFrozenRequested == isFrozen) {
+                return;
+            }
+            if (mFrozenRequested) {
+                freeze();
+            } else {
+                unfreeze();
+            }
+        }
+
+        private void freeze() {
+            if (!mReportedVisible || mDestroyed) {
+                // Screenshot can't be taken until visibility is reported to the wallpaper host.
+                return;
+            }
+            if (!showScreenshotOfWallpaper()) {
+                return;
+            }
+            // Prevent a wallpaper host from rendering wallpaper behind a screeshot.
+            doVisibilityChanged(false);
+            // Remember that visibility is requested since it's not guaranteed that
+            // mWindow#dispatchAppVisibility will be called when letterboxed application with
+            // wallpaper background transitions to the Home screen.
+            mVisible = true;
+        }
+
+        private void unfreeze() {
+            cleanUpScreenshotSurfaceControl();
+            if (mVisible) {
+                doVisibilityChanged(true);
+            }
+        }
+
+        private void cleanUpScreenshotSurfaceControl() {
+            // TODO(b/194399558): Add crossfade transition.
+            if (mScreenshotSurfaceControl != null) {
+                new SurfaceControl.Transaction()
+                        .remove(mScreenshotSurfaceControl)
+                        .show(mBbqSurfaceControl)
+                        .apply();
+                mScreenshotSurfaceControl = null;
+            }
+        }
+
+        void scaleAndCropScreenshot() {
+            if (mScreenshotSurfaceControl == null) {
+                return;
+            }
+            if (mScreenshotSize.x <= 0 || mScreenshotSize.y <= 0) {
+                Log.w(TAG, "Unexpected screenshot size: " + mScreenshotSize);
+                return;
+            }
+            // Don't scale down and using the same scaling factor for both dimensions to
+            // avoid stretching wallpaper image.
+            float scaleFactor = Math.max(1, Math.max(
+                    ((float) mSurfaceSize.x) / mScreenshotSize.x,
+                    ((float) mSurfaceSize.y) / mScreenshotSize.y));
+            int diffX =  ((int) (mScreenshotSize.x * scaleFactor)) - mSurfaceSize.x;
+            int diffY =  ((int) (mScreenshotSize.y * scaleFactor)) - mSurfaceSize.y;
+            if (DEBUG) {
+                Log.v(TAG, "Adjusting screenshot: scaleFactor=" + scaleFactor
+                        + " diffX=" + diffX + " diffY=" + diffY + " mSurfaceSize=" + mSurfaceSize
+                        + " mScreenshotSize=" + mScreenshotSize);
+            }
+            new SurfaceControl.Transaction()
+                        .setMatrix(
+                                mScreenshotSurfaceControl,
+                                /* dsdx= */ scaleFactor, /* dtdx= */ 0,
+                                /* dtdy= */ 0, /* dsdy= */ scaleFactor)
+                        .setWindowCrop(
+                                mScreenshotSurfaceControl,
+                                new Rect(
+                                        /* left= */ diffX / 2,
+                                        /* top= */ diffY / 2,
+                                        /* right= */ diffX / 2 + mScreenshotSize.x,
+                                        /* bottom= */ diffY / 2 + mScreenshotSize.y))
+                        .setPosition(mScreenshotSurfaceControl, -diffX / 2, -diffY / 2)
+                        .apply();
+        }
+
+        private boolean showScreenshotOfWallpaper() {
+            if (mDestroyed || mSurfaceControl == null || !mSurfaceControl.isValid()) {
+                if (DEBUG) Log.v(TAG, "Failed to screenshot wallpaper: surface isn't valid");
+                return false;
+            }
+
+            final Rect bounds = new Rect(0, 0, mSurfaceSize.x,  mSurfaceSize.y);
+            if (bounds.isEmpty()) {
+                Log.w(TAG, "Failed to screenshot wallpaper: surface bounds are empty");
+                return false;
+            }
+
+            if (mScreenshotSurfaceControl != null) {
+                Log.e(TAG, "Screenshot is unexpectedly not null");
+                // Destroying previous screenshot since it can have different size.
+                cleanUpScreenshotSurfaceControl();
+            }
+
+            SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
+                    SurfaceControl.captureLayers(
+                            new SurfaceControl.LayerCaptureArgs.Builder(mSurfaceControl)
+                                    // Needed because SurfaceFlinger#validateScreenshotPermissions
+                                    // uses this parameter to check whether a caller only attempts
+                                    // to screenshot itself when call doesn't come from the system.
+                                    .setUid(Process.myUid())
+                                    .setChildrenOnly(false)
+                                    .setSourceCrop(bounds)
+                                    .build());
+
+            if (screenshotBuffer == null) {
+                Log.w(TAG, "Failed to screenshot wallpaper: screenshotBuffer is null");
+                return false;
+            }
+
+            final HardwareBuffer hardwareBuffer = screenshotBuffer.getHardwareBuffer();
+
+            SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+
+            // TODO(b/194399558): Add crossfade transition.
+            mScreenshotSurfaceControl = new SurfaceControl.Builder()
+                    .setName("Wallpaper snapshot for engine " + this)
+                    .setFormat(hardwareBuffer.getFormat())
+                    .setParent(mSurfaceControl)
+                    .setSecure(screenshotBuffer.containsSecureLayers())
+                    .setCallsite("WallpaperService.Engine.showScreenshotOfWallpaper")
+                    .setBLASTLayer()
+                    .build();
+
+            mScreenshotSize.set(mSurfaceSize.x, mSurfaceSize.y);
+
+            GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(hardwareBuffer);
+
+            t.setBuffer(mScreenshotSurfaceControl, graphicBuffer);
+            t.setColorSpace(mScreenshotSurfaceControl, screenshotBuffer.getColorSpace());
+            // Place on top everything else.
+            t.setLayer(mScreenshotSurfaceControl, Integer.MAX_VALUE);
+            t.show(mScreenshotSurfaceControl);
+            t.hide(mBbqSurfaceControl);
+            t.apply();
+
+            return true;
+        }
+
         void reportSurfaceDestroyed() {
             if (mSurfaceCreated) {
                 mSurfaceCreated = false;
@@ -2164,6 +2343,7 @@
                     final boolean reportDraw = message.arg1 != 0;
                     mEngine.updateSurface(true, false, reportDraw);
                     mEngine.doOffsetsChanged(true);
+                    mEngine.scaleAndCropScreenshot();
                 } break;
                 case MSG_WINDOW_MOVED: {
                     // Do nothing. What does it mean for a Wallpaper to move?
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 6c3c383..3d39fbe 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -51,6 +51,9 @@
     /** @hide */
     public static final String SETTINGS_ENABLE_SECURITY_HUB = "settings_enable_security_hub";
 
+    /** @hide */
+    public static final String SETTINGS_SUPPORT_LARGE_SCREEN = "settings_support_large_screen";
+
     private static final Map<String, String> DEFAULT_FLAGS;
 
     static {
@@ -72,12 +75,14 @@
         DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "true");
         DEFAULT_FLAGS.put(SETTINGS_USE_NEW_BACKUP_ELIGIBILITY_RULES, "true");
         DEFAULT_FLAGS.put(SETTINGS_ENABLE_SECURITY_HUB, "true");
+        DEFAULT_FLAGS.put(SETTINGS_SUPPORT_LARGE_SCREEN, "false");
     }
 
     private static final Set<String> PERSISTENT_FLAGS;
     static {
         PERSISTENT_FLAGS = new HashSet<>();
         PERSISTENT_FLAGS.add(SETTINGS_PROVIDER_MODEL);
+        PERSISTENT_FLAGS.add(SETTINGS_SUPPORT_LARGE_SCREEN);
     }
 
     /**
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 9cb0d1f..e7ff978 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -1463,10 +1463,10 @@
             return false;
         }
         final Configuration config = mResources.getConfiguration();
-        // TODO(b/179308296) Temporarily exclude Launcher from being given max bounds, by checking
-        // if the caller is the recents component.
+        // TODO(b/179308296) Temporarily - never report max bounds to only Launcher if the feature
+        // is disabled.
         return config != null && !config.windowConfiguration.getMaxBounds().isEmpty()
-                && !isRecentsComponent();
+                && (mDisplayInfo.shouldConstrainMetricsForLauncher || !isRecentsComponent());
     }
 
     /**
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 8e5f905..6572510 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -306,6 +306,13 @@
     public float brightnessDefault;
 
     /**
+     * @hide
+     * True if Display#getRealSize and getRealMetrics should be constrained for Launcher, false
+     * otherwise.
+     */
+    public boolean shouldConstrainMetricsForLauncher = false;
+
+    /**
      * The {@link RoundedCorners} if present, otherwise {@code null}.
      */
     @Nullable
@@ -381,7 +388,8 @@
                 && brightnessMinimum == other.brightnessMinimum
                 && brightnessMaximum == other.brightnessMaximum
                 && brightnessDefault == other.brightnessDefault
-                && Objects.equals(roundedCorners, other.roundedCorners);
+                && Objects.equals(roundedCorners, other.roundedCorners)
+                && shouldConstrainMetricsForLauncher == other.shouldConstrainMetricsForLauncher;
     }
 
     @Override
@@ -432,6 +440,7 @@
         brightnessMaximum = other.brightnessMaximum;
         brightnessDefault = other.brightnessDefault;
         roundedCorners = other.roundedCorners;
+        shouldConstrainMetricsForLauncher = other.shouldConstrainMetricsForLauncher;
     }
 
     public void readFromParcel(Parcel source) {
@@ -488,6 +497,7 @@
         for (int i = 0; i < numUserDisabledFormats; i++) {
             userDisabledHdrTypes[i] = source.readInt();
         }
+        shouldConstrainMetricsForLauncher = source.readBoolean();
     }
 
     @Override
@@ -542,6 +552,7 @@
         for (int i = 0; i < userDisabledHdrTypes.length; i++) {
             dest.writeInt(userDisabledHdrTypes[i]);
         }
+        dest.writeBoolean(shouldConstrainMetricsForLauncher);
     }
 
     @Override
@@ -796,6 +807,8 @@
         sb.append(brightnessMaximum);
         sb.append(", brightnessDefault ");
         sb.append(brightnessDefault);
+        sb.append(", shouldConstrainMetricsForLauncher ");
+        sb.append(shouldConstrainMetricsForLauncher);
         sb.append("}");
         return sb.toString();
     }
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 1cb1439..139bff4b 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -19,6 +19,7 @@
 import static android.view.Display.INVALID_DISPLAY;
 
 import android.annotation.Nullable;
+import android.graphics.Matrix;
 import android.graphics.Region;
 import android.gui.TouchOcclusionMode;
 import android.os.IBinder;
@@ -43,6 +44,17 @@
     // channel and the server input channel will both contain this token.
     public IBinder token;
 
+    /**
+     * The {@link IWindow} handle if InputWindowHandle is associated with a window, null otherwise.
+     */
+    @Nullable
+    private IBinder windowToken;
+    /**
+     * Used to cache IWindow from the windowToken so we don't need to convert every time getWindow
+     * is called.
+     */
+    private IWindow window;
+
     // The window name.
     public String name;
 
@@ -122,6 +134,12 @@
      */
     public boolean replaceTouchableRegionWithCrop;
 
+    /**
+     * The transform that should be applied to the Window to get it from screen coordinates to
+     * window coordinates
+     */
+    public Matrix transform;
+
     private native void nativeDispose();
 
     public InputWindowHandle(InputApplicationHandle inputApplicationHandle, int displayId) {
@@ -136,6 +154,9 @@
                         .append(frameRight).append(",").append(frameBottom).append("]")
                 .append(", touchableRegion=").append(touchableRegion)
                 .append(", visible=").append(visible)
+                .append(", scaleFactor=").append(scaleFactor)
+                .append(", transform=").append(transform)
+                .append(", windowToken=").append(windowToken)
                 .toString();
 
     }
@@ -167,4 +188,17 @@
     public void setTouchableRegionCrop(@Nullable SurfaceControl bounds) {
         touchableRegionSurfaceControl = new WeakReference<>(bounds);
     }
+
+    public void setWindowToken(IWindow iwindow) {
+        windowToken = iwindow.asBinder();
+        window = iwindow;
+    }
+
+    public IWindow getWindow() {
+        if (window != null) {
+            return window;
+        }
+        window = IWindow.Stub.asInterface(windowToken);
+        return window;
+    }
 }
diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
index cdc099b..bd68401 100644
--- a/core/java/android/view/RemoteAnimationTarget.java
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -197,6 +197,12 @@
     public ActivityManager.RunningTaskInfo taskInfo;
 
     /**
+     * {@code true} if picture-in-picture permission is granted in {@link android.app.AppOpsManager}
+     */
+    @UnsupportedAppUsage
+    public boolean allowEnterPip;
+
+    /**
      * The {@link android.view.WindowManager.LayoutParams.WindowType} of this window. It's only used
      * for non-app window.
      */
@@ -206,10 +212,11 @@
             Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position,
             Rect localBounds, Rect screenSpaceBounds,
             WindowConfiguration windowConfig, boolean isNotInRecents,
-            SurfaceControl startLeash, Rect startBounds, ActivityManager.RunningTaskInfo taskInfo) {
+            SurfaceControl startLeash, Rect startBounds, ActivityManager.RunningTaskInfo taskInfo,
+            boolean allowEnterPip) {
         this(taskId, mode, leash, isTranslucent, clipRect, contentInsets, prefixOrderIndex,
                 position, localBounds, screenSpaceBounds, windowConfig, isNotInRecents, startLeash,
-                startBounds, taskInfo, INVALID_WINDOW_TYPE);
+                startBounds, taskInfo, allowEnterPip, INVALID_WINDOW_TYPE);
     }
 
     public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent,
@@ -217,7 +224,7 @@
             Rect localBounds, Rect screenSpaceBounds,
             WindowConfiguration windowConfig, boolean isNotInRecents,
             SurfaceControl startLeash, Rect startBounds,
-            ActivityManager.RunningTaskInfo taskInfo,
+            ActivityManager.RunningTaskInfo taskInfo, boolean allowEnterPip,
             @WindowManager.LayoutParams.WindowType int windowType) {
         this.mode = mode;
         this.taskId = taskId;
@@ -235,6 +242,7 @@
         this.startLeash = startLeash;
         this.startBounds = startBounds == null ? null : new Rect(startBounds);
         this.taskInfo = taskInfo;
+        this.allowEnterPip = allowEnterPip;
         this.windowType = windowType;
     }
 
@@ -255,6 +263,7 @@
         startLeash = in.readTypedObject(SurfaceControl.CREATOR);
         startBounds = in.readTypedObject(Rect.CREATOR);
         taskInfo = in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
+        allowEnterPip = in.readBoolean();
         windowType = in.readInt();
     }
 
@@ -281,6 +290,7 @@
         dest.writeTypedObject(startLeash, 0 /* flags */);
         dest.writeTypedObject(startBounds, 0 /* flags */);
         dest.writeTypedObject(taskInfo, 0 /* flags */);
+        dest.writeBoolean(allowEnterPip);
         dest.writeInt(windowType);
     }
 
@@ -299,6 +309,7 @@
         pw.print(prefix); pw.print("windowConfiguration="); pw.println(windowConfiguration);
         pw.print(prefix); pw.print("leash="); pw.println(leash);
         pw.print(prefix); pw.print("taskInfo="); pw.println(taskInfo);
+        pw.print(prefix); pw.print("allowEnterPip="); pw.println(allowEnterPip);
         pw.print(prefix); pw.print("windowType="); pw.print(windowType);
     }
 
diff --git a/core/java/android/view/ScrollCaptureResponse.java b/core/java/android/view/ScrollCaptureResponse.java
index 8808827..758f9ab 100644
--- a/core/java/android/view/ScrollCaptureResponse.java
+++ b/core/java/android/view/ScrollCaptureResponse.java
@@ -53,6 +53,10 @@
     @Nullable
     private String mWindowTitle = null;
 
+    /** The package which owns the window. */
+    @Nullable
+    private String mPackageName = null;
+
     /** Carries additional logging and debugging information when enabled. */
     @NonNull
     @DataClass.PluralOf("message")
@@ -77,7 +81,7 @@
 
 
 
-    // Code below generated by codegen v1.0.22.
+    // Code below generated by codegen v1.0.23.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -97,6 +101,7 @@
             @Nullable Rect windowBounds,
             @Nullable Rect boundsInWindow,
             @Nullable String windowTitle,
+            @Nullable String packageName,
             @NonNull ArrayList<String> messages) {
         this.mDescription = description;
         com.android.internal.util.AnnotationValidations.validate(
@@ -105,6 +110,7 @@
         this.mWindowBounds = windowBounds;
         this.mBoundsInWindow = boundsInWindow;
         this.mWindowTitle = windowTitle;
+        this.mPackageName = packageName;
         this.mMessages = messages;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mMessages);
@@ -153,6 +159,14 @@
     }
 
     /**
+     * The package name of the process the window is owned by.
+     */
+    @DataClass.Generated.Member
+    public @Nullable String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
      * Carries additional logging and debugging information when enabled.
      */
     @DataClass.Generated.Member
@@ -172,6 +186,7 @@
                 "windowBounds = " + mWindowBounds + ", " +
                 "boundsInWindow = " + mBoundsInWindow + ", " +
                 "windowTitle = " + mWindowTitle + ", " +
+                "packageName = " + mPackageName + ", " +
                 "messages = " + mMessages +
         " }";
     }
@@ -187,12 +202,14 @@
         if (mWindowBounds != null) flg |= 0x4;
         if (mBoundsInWindow != null) flg |= 0x8;
         if (mWindowTitle != null) flg |= 0x10;
+        if (mPackageName != null) flg |= 0x20;
         dest.writeByte(flg);
         dest.writeString(mDescription);
         if (mConnection != null) dest.writeStrongInterface(mConnection);
         if (mWindowBounds != null) dest.writeTypedObject(mWindowBounds, flags);
         if (mBoundsInWindow != null) dest.writeTypedObject(mBoundsInWindow, flags);
         if (mWindowTitle != null) dest.writeString(mWindowTitle);
+        if (mPackageName != null) dest.writeString(mPackageName);
         dest.writeStringList(mMessages);
     }
 
@@ -213,6 +230,7 @@
         Rect windowBounds = (flg & 0x4) == 0 ? null : (Rect) in.readTypedObject(Rect.CREATOR);
         Rect boundsInWindow = (flg & 0x8) == 0 ? null : (Rect) in.readTypedObject(Rect.CREATOR);
         String windowTitle = (flg & 0x10) == 0 ? null : in.readString();
+        String packageName = (flg & 0x20) == 0 ? null : in.readString();
         ArrayList<String> messages = new ArrayList<>();
         in.readStringList(messages);
 
@@ -223,6 +241,7 @@
         this.mWindowBounds = windowBounds;
         this.mBoundsInWindow = boundsInWindow;
         this.mWindowTitle = windowTitle;
+        this.mPackageName = packageName;
         this.mMessages = messages;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mMessages);
@@ -256,6 +275,7 @@
         private @Nullable Rect mWindowBounds;
         private @Nullable Rect mBoundsInWindow;
         private @Nullable String mWindowTitle;
+        private @Nullable String mPackageName;
         private @NonNull ArrayList<String> mMessages;
 
         private long mBuilderFieldsSet = 0L;
@@ -319,12 +339,23 @@
         }
 
         /**
+         * The package name of the process the window is owned by.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setPackageName(@NonNull String value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x20;
+            mPackageName = value;
+            return this;
+        }
+
+        /**
          * Carries additional logging and debugging information when enabled.
          */
         @DataClass.Generated.Member
         public @NonNull Builder setMessages(@NonNull ArrayList<String> value) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x20;
+            mBuilderFieldsSet |= 0x40;
             mMessages = value;
             return this;
         }
@@ -340,7 +371,7 @@
         /** Builds the instance. This builder should not be touched after calling this! */
         public @NonNull ScrollCaptureResponse build() {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x40; // Mark builder used
+            mBuilderFieldsSet |= 0x80; // Mark builder used
 
             if ((mBuilderFieldsSet & 0x1) == 0) {
                 mDescription = "";
@@ -358,6 +389,9 @@
                 mWindowTitle = null;
             }
             if ((mBuilderFieldsSet & 0x20) == 0) {
+                mPackageName = null;
+            }
+            if ((mBuilderFieldsSet & 0x40) == 0) {
                 mMessages = new ArrayList<>();
             }
             ScrollCaptureResponse o = new ScrollCaptureResponse(
@@ -366,12 +400,13 @@
                     mWindowBounds,
                     mBoundsInWindow,
                     mWindowTitle,
+                    mPackageName,
                     mMessages);
             return o;
         }
 
         private void checkNotUsed() {
-            if ((mBuilderFieldsSet & 0x40) != 0) {
+            if ((mBuilderFieldsSet & 0x80) != 0) {
                 throw new IllegalStateException(
                         "This Builder should not be reused. Use a new Builder instance instead");
             }
@@ -379,10 +414,10 @@
     }
 
     @DataClass.Generated(
-            time = 1614833185795L,
-            codegenVersion = "1.0.22",
+            time = 1628630366187L,
+            codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/view/ScrollCaptureResponse.java",
-            inputSignatures = "private @android.annotation.NonNull java.lang.String mDescription\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.MaySetToNull android.view.IScrollCaptureConnection mConnection\nprivate @android.annotation.Nullable android.graphics.Rect mWindowBounds\nprivate @android.annotation.Nullable android.graphics.Rect mBoundsInWindow\nprivate @android.annotation.Nullable java.lang.String mWindowTitle\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"message\") java.util.ArrayList<java.lang.String> mMessages\npublic  boolean isConnected()\npublic  void close()\nclass ScrollCaptureResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genGetters=true)")
+            inputSignatures = "private @android.annotation.NonNull java.lang.String mDescription\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.MaySetToNull android.view.IScrollCaptureConnection mConnection\nprivate @android.annotation.Nullable android.graphics.Rect mWindowBounds\nprivate @android.annotation.Nullable android.graphics.Rect mBoundsInWindow\nprivate @android.annotation.Nullable java.lang.String mWindowTitle\nprivate @android.annotation.Nullable java.lang.String mPackageName\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"message\") java.util.ArrayList<java.lang.String> mMessages\npublic  boolean isConnected()\npublic  void close()\nclass ScrollCaptureResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genGetters=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index ff2d2eb..aaf53ee 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -29,6 +29,7 @@
 import android.graphics.ColorSpace;
 import android.graphics.HardwareRenderer;
 import android.graphics.Matrix;
+import android.graphics.Point;
 import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
 import android.graphics.RenderNode;
@@ -408,6 +409,20 @@
     }
 
     /**
+     * Returns the default size of this Surface provided by the consumer of the surface.
+     * Should only be used by the producer of the surface.
+     *
+     * @hide
+     */
+    @NonNull
+    public Point getDefaultSize() {
+        synchronized (mLock) {
+            checkNotReleasedLocked();
+            return new Point(nativeGetWidth(mNativeObject), nativeGetHeight(mNativeObject));
+        }
+    }
+
+    /**
      * Gets a {@link Canvas} for drawing into this surface.
      *
      * After drawing into the provided {@link Canvas}, the caller must
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index c39426d..a4e7a43 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -33,6 +33,7 @@
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.RenderNode;
@@ -237,15 +238,6 @@
             new SurfaceControl.Transaction();
 
     /**
-     * Transaction that should be used for
-     * {@link RenderNode.PositionUpdateListener#positionChanged(long, int, int, int, int)}
-     * The callback is invoked from a thread pool so it's not thread safe with other render thread
-     * transactions. Keep the transactions for position changed callbacks on its own transaction.
-     */
-    private final SurfaceControl.Transaction mPositionChangedTransaction =
-            new SurfaceControl.Transaction();
-
-    /**
      * A temporary transaction holder that should only be used when applying right away. There
      * should be no assumption about thread safety for this transaction.
      */
@@ -295,7 +287,6 @@
             int defStyleRes, boolean disableBackgroundLayer) {
         super(context, attrs, defStyleAttr, defStyleRes);
         mUseBlastAdapter = useBlastAdapter(context);
-        mRenderNode.addPositionUpdateListener(mPositionListener);
 
         setWillNotDraw(true);
         mDisableBackgroundLayer = disableBackgroundLayer;
@@ -943,6 +934,24 @@
         }
     }
 
+
+    // The position update listener is used to safely share the surface size between render thread
+    // workers and the UI thread. Both threads need to know the surface size to determine the scale.
+    // The parent layer scales the surface size to view size. The child (BBQ) layer scales
+    // the buffer to the surface size. Both scales along with the window crop must be applied
+    // synchronously otherwise we may see flickers.
+    // When the listener is updated, we will get at least a single position update call so we can
+    // guarantee any changes we post will be applied.
+    private void replacePositionUpdateListener(int surfaceWidth, int surfaceHeight,
+            @Nullable Transaction geometryTransaction) {
+        if (mPositionListener != null) {
+            mRenderNode.removePositionUpdateListener(mPositionListener);
+        }
+        mPositionListener = new SurfaceViewPositionUpdateListener(surfaceWidth, surfaceHeight,
+                geometryTransaction);
+        mRenderNode.addPositionUpdateListener(mPositionListener);
+    }
+
     private boolean performSurfaceTransaction(ViewRootImpl viewRoot, Translator translator,
             boolean creating, boolean sizeChanged, boolean hintChanged) {
         boolean realSizeChanged = false;
@@ -985,13 +994,13 @@
             // While creating the surface, we will set it's initial
             // geometry. Outside of that though, we should generally
             // leave it to the RenderThread.
-            //
-            // There is one more case when the buffer size changes we aren't yet
-            // prepared to sync (as even following the transaction applying
-            // we still need to latch a buffer).
-            // b/28866173
-            if (sizeChanged || creating || !mRtHandlingPositionUpdates) {
-                onSetSurfacePositionAndScaleRT(mTmpTransaction, mSurfaceControl,
+            Transaction geometryTransaction = new Transaction();
+            geometryTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
+            if ((sizeChanged || hintChanged) && !creating) {
+                setBufferSize(geometryTransaction);
+            }
+            if (sizeChanged || creating || !isHardwareAccelerated()) {
+                onSetSurfacePositionAndScaleRT(geometryTransaction, mSurfaceControl,
                         mScreenRect.left, /*positionLeft*/
                         mScreenRect.top /*positionTop*/ ,
                         mScreenRect.width() / (float) mSurfaceWidth /*postScaleX*/,
@@ -1002,17 +1011,31 @@
                 // use SCALING_MODE_SCALE and submit a larger size than the surface
                 // size.
                 if (mClipSurfaceToBounds && mClipBounds != null) {
-                    mTmpTransaction.setWindowCrop(mSurfaceControl, mClipBounds);
+                    geometryTransaction.setWindowCrop(mSurfaceControl, mClipBounds);
                 } else {
-                    mTmpTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth,
+                    geometryTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth,
                             mSurfaceHeight);
                 }
-            }
-            mTmpTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
-            if ((sizeChanged || hintChanged) && !creating) {
-                setBufferSize(mTmpTransaction);
-            }
 
+                boolean applyChangesOnRenderThread =
+                        sizeChanged && !creating && isHardwareAccelerated();
+                if (isHardwareAccelerated()) {
+                    // This will consume the passed in transaction and the transaction will be
+                    // applied on a render worker thread.
+                    replacePositionUpdateListener(mSurfaceWidth, mSurfaceHeight,
+                            applyChangesOnRenderThread ? geometryTransaction : null);
+                }
+                if (DEBUG_POSITION) {
+                    Log.d(TAG, String.format(
+                            "%d updateSurfacePosition %s"
+                                + "position = [%d, %d, %d, %d] surfaceSize = %dx%d",
+                            System.identityHashCode(this),
+                            applyChangesOnRenderThread ? "RenderWorker" : "UiThread",
+                            mScreenRect.left, mScreenRect.top, mScreenRect.right,
+                            mScreenRect.bottom, mSurfaceWidth, mSurfaceHeight));
+                }
+            }
+            mTmpTransaction.merge(geometryTransaction);
             mTmpTransaction.apply();
             updateEmbeddedAccessibilityMatrix();
 
@@ -1242,7 +1265,7 @@
             mBlastSurfaceControl.setTransformHint(mTransformHint);
             if (mBlastBufferQueue != null) {
                 mBlastBufferQueue.update(mBlastSurfaceControl, mSurfaceWidth, mSurfaceHeight,
-                        mFormat);
+                        mFormat, transaction);
             }
         } else {
             transaction.setBufferSize(mSurfaceControl, mSurfaceWidth, mSurfaceHeight);
@@ -1399,19 +1422,6 @@
         mTmpTransaction.apply();
     }
 
-    private void applySurfaceTransforms(SurfaceControl surface, SurfaceControl.Transaction t,
-            Rect position) {
-        onSetSurfacePositionAndScaleRT(t, surface,
-                position.left /*positionLeft*/,
-                position.top /*positionTop*/,
-                position.width() / (float) mSurfaceWidth /*postScaleX*/,
-                position.height() / (float) mSurfaceHeight /*postScaleY*/);
-
-        if (mViewVisibility) {
-            t.show(surface);
-        }
-    }
-
     /**
      * @return The last render position of the backing surface or an empty rect.
      *
@@ -1421,13 +1431,6 @@
         return mRTLastReportedPosition;
     }
 
-    private void setParentSpaceRectangle(Rect position, long frameNumber, Transaction t) {
-        final ViewRootImpl viewRoot = getViewRootImpl();
-        applySurfaceTransforms(mSurfaceControl, t, position);
-        applyChildSurfaceTransaction_renderWorker(t, viewRoot.mSurface, frameNumber);
-        applyOrMergeTransaction(t, frameNumber);
-    }
-
     private void applyOrMergeTransaction(Transaction t, long frameNumber) {
         final ViewRootImpl viewRoot = getViewRootImpl();
         boolean useBLAST = viewRoot != null && useBLASTSync(viewRoot);
@@ -1440,9 +1443,24 @@
     }
 
     private Rect mRTLastReportedPosition = new Rect();
+    private Point mRTLastReportedSurfaceSize = new Point();
 
-    private RenderNode.PositionUpdateListener mPositionListener =
-            new RenderNode.PositionUpdateListener() {
+    private class SurfaceViewPositionUpdateListener implements RenderNode.PositionUpdateListener {
+        int mRtSurfaceWidth = -1;
+        int mRtSurfaceHeight = -1;
+        private final SurfaceControl.Transaction mPositionChangedTransaction =
+                new SurfaceControl.Transaction();
+        boolean mPendingTransaction = false;
+
+        SurfaceViewPositionUpdateListener(int surfaceWidth, int surfaceHeight,
+                @Nullable Transaction t) {
+            mRtSurfaceWidth = surfaceWidth;
+            mRtSurfaceHeight = surfaceHeight;
+            if (t != null) {
+                mPositionChangedTransaction.merge(t);
+                mPendingTransaction = true;
+            }
+        }
 
         @Override
         public void positionChanged(long frameNumber, int left, int top, int right, int bottom) {
@@ -1464,21 +1482,34 @@
             if (mRTLastReportedPosition.left == left
                     && mRTLastReportedPosition.top == top
                     && mRTLastReportedPosition.right == right
-                    && mRTLastReportedPosition.bottom == bottom) {
+                    && mRTLastReportedPosition.bottom == bottom
+                    && mRTLastReportedSurfaceSize.x == mRtSurfaceWidth
+                    && mRTLastReportedSurfaceSize.y == mRtSurfaceHeight
+                    && !mPendingTransaction) {
                 return;
             }
             try {
                 if (DEBUG_POSITION) {
                     Log.d(TAG, String.format(
                             "%d updateSurfacePosition RenderWorker, frameNr = %d, "
-                                    + "position = [%d, %d, %d, %d]",
-                            System.identityHashCode(this), frameNumber,
-                            left, top, right, bottom));
+                                    + "position = [%d, %d, %d, %d] surfaceSize = %dx%d",
+                            System.identityHashCode(SurfaceView.this), frameNumber,
+                            left, top, right, bottom, mRtSurfaceWidth, mRtSurfaceHeight));
                 }
                 mRTLastReportedPosition.set(left, top, right, bottom);
-                setParentSpaceRectangle(mRTLastReportedPosition, frameNumber,
-                        mPositionChangedTransaction);
-                // Now overwrite mRTLastReportedPosition with our values
+                mRTLastReportedSurfaceSize.set(mRtSurfaceWidth, mRtSurfaceHeight);
+                onSetSurfacePositionAndScaleRT(mPositionChangedTransaction, mSurfaceControl,
+                        mRTLastReportedPosition.left /*positionLeft*/,
+                        mRTLastReportedPosition.top /*positionTop*/,
+                        mRTLastReportedPosition.width() / (float) mRtSurfaceWidth /*postScaleX*/,
+                        mRTLastReportedPosition.height() / (float) mRtSurfaceHeight /*postScaleY*/);
+                if (mViewVisibility) {
+                    mPositionChangedTransaction.show(mSurfaceControl);
+                }
+                applyChildSurfaceTransaction_renderWorker(mPositionChangedTransaction,
+                        getViewRootImpl().mSurface, frameNumber);
+                applyOrMergeTransaction(mPositionChangedTransaction, frameNumber);
+                mPendingTransaction = false;
             } catch (Exception ex) {
                 Log.e(TAG, "Exception from repositionChild", ex);
             }
@@ -1502,7 +1533,13 @@
                         System.identityHashCode(this), frameNumber));
             }
             mRTLastReportedPosition.setEmpty();
-
+            mRTLastReportedSurfaceSize.set(-1, -1);
+            if (mPendingTransaction) {
+                Log.w(TAG, System.identityHashCode(SurfaceView.this)
+                        + "Pending transaction cleared.");
+                mPositionChangedTransaction.clear();
+                mPendingTransaction = false;
+            }
             if (mSurfaceControl == null) {
                 return;
             }
@@ -1521,7 +1558,9 @@
                 mRtHandlingPositionUpdates = false;
             }
         }
-    };
+    }
+
+    private SurfaceViewPositionUpdateListener mPositionListener = null;
 
     private SurfaceHolder.Callback[] getSurfaceCallbacks() {
         SurfaceHolder.Callback[] callbacks;
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 0a3d0da..4b18d3a 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -433,9 +433,8 @@
         mAmbiguousGestureMultiplier = Math.max(1.0f, multiplierValue.getFloat());
 
         // Size of the screen in bytes, in ARGB_8888 format
-        final WindowManager windowManager = context.getSystemService(WindowManager.class);
-        final Rect maxWindowBounds = windowManager.getMaximumWindowMetrics().getBounds();
-        mMaximumDrawingCacheSize = 4 * maxWindowBounds.width() * maxWindowBounds.height();
+        final Rect maxBounds = config.windowConfiguration.getMaxBounds();
+        mMaximumDrawingCacheSize = 4 * maxBounds.width() * maxBounds.height();
 
         mOverscrollDistance = (int) (sizeAndDensity * OVERSCROLL_DISTANCE + 0.5f);
         mOverflingDistance = (int) (sizeAndDensity * OVERFLING_DISTANCE + 0.5f);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 27eb2a5..04d65c0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -9517,6 +9517,7 @@
 
         ScrollCaptureResponse.Builder response = new ScrollCaptureResponse.Builder();
         response.setWindowTitle(getTitle().toString());
+        response.setPackageName(mContext.getPackageName());
 
         StringWriter writer =  new StringWriter();
         IndentingPrintWriter pw = new IndentingPrintWriter(writer);
@@ -9931,7 +9932,10 @@
         if (!mUseMTRenderer) {
             return;
         }
-        mWindowDrawCountDown = new CountDownLatch(mWindowCallbacks.size());
+        // Only wait if it will report next draw.
+        if (mReportNextDraw) {
+            mWindowDrawCountDown = new CountDownLatch(mWindowCallbacks.size());
+        }
         for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
             mWindowCallbacks.get(i).onRequestDraw(mReportNextDraw);
         }
diff --git a/core/java/android/view/WindowInfo.java b/core/java/android/view/WindowInfo.java
index 57dfc62..1edbbba 100644
--- a/core/java/android/view/WindowInfo.java
+++ b/core/java/android/view/WindowInfo.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.app.ActivityTaskManager;
 import android.graphics.Region;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -51,6 +52,7 @@
     public boolean inPictureInPicture;
     public boolean hasFlagWatchOutsideTouch;
     public int displayId = Display.INVALID_DISPLAY;
+    public int taskId = ActivityTaskManager.INVALID_TASK_ID;
 
     private WindowInfo() {
         /* do nothing - hide constructor */
@@ -67,6 +69,7 @@
     public static WindowInfo obtain(WindowInfo other) {
         WindowInfo window = obtain();
         window.displayId = other.displayId;
+        window.taskId = other.taskId;
         window.type = other.type;
         window.layer = other.layer;
         window.token = other.token;
@@ -103,6 +106,7 @@
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeInt(displayId);
+        parcel.writeInt(taskId);
         parcel.writeInt(type);
         parcel.writeInt(layer);
         parcel.writeStrongBinder(token);
@@ -129,6 +133,7 @@
         builder.append("WindowInfo[");
         builder.append("title=").append(title);
         builder.append(", displayId=").append(displayId);
+        builder.append(", taskId=").append(taskId);
         builder.append(", type=").append(type);
         builder.append(", layer=").append(layer);
         builder.append(", token=").append(token);
@@ -146,6 +151,7 @@
 
     private void initFromParcel(Parcel parcel) {
         displayId = parcel.readInt();
+        taskId = parcel.readInt();
         type = parcel.readInt();
         layer = parcel.readInt();
         token = parcel.readStrongBinder();
@@ -169,6 +175,7 @@
 
     private void clear() {
         displayId = Display.INVALID_DISPLAY;
+        taskId = ActivityTaskManager.INVALID_TASK_ID;
         type = 0;
         layer = 0;
         token = null;
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 8cc8866..f57e520 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -320,6 +320,25 @@
     int TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE = 27;
 
     /**
+     * A window in a new task fragment is being opened.
+     * @hide
+     */
+    int TRANSIT_OLD_TASK_FRAGMENT_OPEN = 28;
+
+    /**
+     * A window in the top-most activity of task fragment is being closed to reveal the activity
+     * below.
+     * @hide
+     */
+    int TRANSIT_OLD_TASK_FRAGMENT_CLOSE = 29;
+
+    /**
+     * A window of task fragment is changing bounds.
+     * @hide
+     */
+    int TRANSIT_OLD_TASK_FRAGMENT_CHANGE = 30;
+
+    /**
      * @hide
      */
     @IntDef(prefix = { "TRANSIT_OLD_" }, value = {
@@ -344,7 +363,10 @@
             TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN,
             TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE,
             TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE,
-            TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE
+            TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE,
+            TRANSIT_OLD_TASK_FRAGMENT_OPEN,
+            TRANSIT_OLD_TASK_FRAGMENT_CLOSE,
+            TRANSIT_OLD_TASK_FRAGMENT_CHANGE
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface TransitionOldType {}
@@ -381,8 +403,11 @@
     int TRANSIT_CHANGE = 6;
     /**
      * The keyguard was visible and has been dismissed.
+     * @deprecated use {@link #TRANSIT_TO_BACK} + {@link #TRANSIT_FLAG_KEYGUARD_GOING_AWAY} for
+     *             keyguard going away with Shell transition.
      * @hide
      */
+    @Deprecated
     int TRANSIT_KEYGUARD_GOING_AWAY = 7;
     /**
      * A window is appearing above a locked keyguard.
@@ -487,6 +512,12 @@
     int TRANSIT_FLAG_IS_RECENTS = 0x80;
 
     /**
+     * Transition flag: Indicates that keyguard should go away with this transition.
+     * @hide
+     */
+    int TRANSIT_FLAG_KEYGUARD_GOING_AWAY = 0x100;
+
+    /**
      * @hide
      */
     @IntDef(flag = true, prefix = { "TRANSIT_FLAG_" }, value = {
@@ -497,7 +528,8 @@
             TRANSIT_FLAG_APP_CRASHED,
             TRANSIT_FLAG_OPEN_BEHIND,
             TRANSIT_FLAG_KEYGUARD_LOCKED,
-            TRANSIT_FLAG_IS_RECENTS
+            TRANSIT_FLAG_IS_RECENTS,
+            TRANSIT_FLAG_KEYGUARD_GOING_AWAY
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface TransitionFlags {}
@@ -943,6 +975,8 @@
             case TRANSIT_KEYGUARD_GOING_AWAY: return "KEYGUARD_GOING_AWAY";
             case TRANSIT_KEYGUARD_OCCLUDE: return "KEYGUARD_OCCLUDE";
             case TRANSIT_KEYGUARD_UNOCCLUDE: return "KEYGUARD_UNOCCLUDE";
+            case TRANSIT_PIP: return "PIP";
+            case TRANSIT_WAKE: return "WAKE";
             case TRANSIT_FIRST_CUSTOM: return "FIRST_CUSTOM";
             default:
                 if (type > TRANSIT_FIRST_CUSTOM) {
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index f8009919..a2d3e34 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -19,9 +19,12 @@
 import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
 import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
 import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
+import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
+import static android.window.WindowProviderService.isWindowProviderService;
 
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
@@ -36,7 +39,9 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.StrictMode;
 import android.window.WindowContext;
+import android.window.WindowProvider;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.IResultReceiver;
@@ -145,6 +150,7 @@
             throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
         }
         final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
+        assertWindowContextTypeMatches(wparams.type);
         // Only use the default token if we don't have a parent window and a token.
         if (mDefaultToken != null && mParentWindow == null && wparams.token == null) {
             wparams.token = mDefaultToken;
@@ -152,6 +158,34 @@
         wparams.mWindowContextToken = mWindowContextToken;
     }
 
+    private void assertWindowContextTypeMatches(@LayoutParams.WindowType int windowType) {
+        if (!(mContext instanceof WindowProvider)) {
+            return;
+        }
+        // Don't need to check sub-window type because sub window should be allowed to be attached
+        // to the parent window.
+        if (windowType >= FIRST_SUB_WINDOW && windowType <= LAST_SUB_WINDOW) {
+            return;
+        }
+        final WindowProvider windowProvider = (WindowProvider) mContext;
+        if (windowProvider.getWindowType() == windowType) {
+            return;
+        }
+        IllegalArgumentException exception = new IllegalArgumentException("Window type mismatch."
+                + " Window Context's window type is " + windowProvider.getWindowType()
+                + ", while LayoutParams' type is set to " + windowType + "."
+                + " Please create another Window Context via"
+                + " createWindowContext(getDisplay(), " + windowType + ", null)"
+                + " to add window with type:" + windowType);
+        if (!isWindowProviderService(windowProvider.getWindowContextOptions())) {
+            throw exception;
+        }
+        // Throw IncorrectCorrectViolation if the Window Context is allowed to provide multiple
+        // window types. Usually it's because the Window Context is a WindowProviderService.
+        StrictMode.onIncorrectContextUsed("WindowContext's window type must"
+                + " match type in WindowManager.LayoutParams", exception);
+    }
+
     @Override
     public void removeView(View view) {
         mGlobal.removeView(view, false);
diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java
index bbef3e6..e634d60 100644
--- a/core/java/android/view/WindowManagerPolicyConstants.java
+++ b/core/java/android/view/WindowManagerPolicyConstants.java
@@ -209,4 +209,41 @@
                 return Integer.toString(why);
         }
     }
+
+    /**
+     * How much to multiply the policy's type layer, to reserve room
+     * for multiple windows of the same type and Z-ordering adjustment
+     * with TYPE_LAYER_OFFSET.
+     */
+    int TYPE_LAYER_MULTIPLIER = 10000;
+
+    /**
+     * Offset from TYPE_LAYER_MULTIPLIER for moving a group of windows above
+     * or below others in the same layer.
+     */
+    int TYPE_LAYER_OFFSET = 1000;
+
+    /**
+     * How much to increment the layer for each window, to reserve room
+     * for effect surfaces between them.
+     */
+    int WINDOW_LAYER_MULTIPLIER = 5;
+
+    /**
+     * Animation thumbnail is as far as possible below the window above
+     * the thumbnail (or in other words as far as possible above the window
+     * below it).
+     */
+    int LAYER_OFFSET_THUMBNAIL = WINDOW_LAYER_MULTIPLIER - 1;
+
+    int SPLIT_DIVIDER_LAYER = TYPE_LAYER_MULTIPLIER * 3;
+    int WATERMARK_LAYER = TYPE_LAYER_MULTIPLIER * 100;
+    int STRICT_MODE_LAYER = TYPE_LAYER_MULTIPLIER * 101;
+    int WINDOW_FREEZE_LAYER = TYPE_LAYER_MULTIPLIER * 200;
+
+    /**
+     * Layers for screen rotation animation. We put these layers above
+     * WINDOW_FREEZE_LAYER so that screen freeze will cover all windows.
+     */
+    int SCREEN_FREEZE_LAYER_BASE = WINDOW_FREEZE_LAYER + TYPE_LAYER_MULTIPLIER;
 }
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index f6d6fde..52d3612 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -613,6 +613,36 @@
     public static final int CONTENT_CHANGE_TYPE_STATE_DESCRIPTION = 0x00000040;
 
     /**
+     * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
+     * A drag has started while accessibility is enabled. This is either via an
+     * AccessibilityAction, or via touch events. This is sent from the source that initiated the
+     * drag.
+     *
+     * @see AccessibilityNodeInfo.AccessibilityAction#ACTION_DRAG_START
+     */
+    public static final int CONTENT_CHANGE_TYPE_DRAG_STARTED = 0x00000080;
+
+    /**
+     * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
+     * A drag in with accessibility enabled has ended. This means the content has been
+     * successfully dropped. This is sent from the target that accepted the dragged content.
+     *
+     * @see AccessibilityNodeInfo.AccessibilityAction#ACTION_DRAG_DROP
+     */
+    public static final int CONTENT_CHANGE_TYPE_DRAG_DROPPED = 0x00000100;
+
+    /**
+     * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
+     * A drag in with accessibility enabled has ended. This means the content has been
+     * unsuccessfully dropped, the user has canceled the action via an AccessibilityAction, or
+     * no drop has been detected within a timeout and the drag was automatically cancelled. This is
+     * sent from the source that initiated the drag.
+     *
+     * @see AccessibilityNodeInfo.AccessibilityAction#ACTION_DRAG_CANCEL
+     */
+    public static final int CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 0x0000200;
+
+    /**
      * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
      * The window was added.
      */
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 085eb81..587a270 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -27,6 +27,7 @@
 import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ClipData;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Build;
@@ -4353,6 +4354,14 @@
             case R.id.accessibilityActionImeEnter:
                 return "ACTION_IME_ENTER";
             default:
+                // TODO(197520937): Use finalized constants in switch
+                if (action == R.id.accessibilityActionDragStart) {
+                    return "ACTION_DRAG";
+                } else if (action == R.id.accessibilityActionDragCancel) {
+                    return "ACTION_CANCEL_DRAG";
+                } else if (action == R.id.accessibilityActionDragDrop) {
+                    return "ACTION_DROP";
+                }
                 return "ACTION_UNKNOWN";
         }
     }
@@ -4995,6 +5004,46 @@
         @NonNull public static final AccessibilityAction ACTION_IME_ENTER =
                 new AccessibilityAction(R.id.accessibilityActionImeEnter);
 
+        /**
+         * Action to start a drag.
+         * <p>
+         * This action initiates a drag & drop within the system. The source's dragged content is
+         * prepared before the drag begins. In View, this action should prepare the arguments to
+         * {@link View#startDragAndDrop(ClipData, View.DragShadowBuilder, Object, int)} and then
+         * call {@link View#startDragAndDrop(ClipData, View.DragShadowBuilder, Object, int)}. The
+         * equivalent should be performed for other UI toolkits.
+         * </p>
+         *
+         * @see AccessibilityEvent#CONTENT_CHANGE_TYPE_DRAG_STARTED
+         */
+        @NonNull public static final AccessibilityAction ACTION_DRAG_START =
+                new AccessibilityAction(R.id.accessibilityActionDragStart);
+
+        /**
+         * Action to trigger a drop of the content being dragged.
+         * <p>
+         * This action is added to potential drop targets if the source started a drag with
+         * {@link #ACTION_DRAG_START}. In View, these targets are Views that accepted
+         * {@link android.view.DragEvent#ACTION_DRAG_STARTED} and have an
+         * {@link View.OnDragListener}.
+         * </p>
+         *
+         * @see AccessibilityEvent#CONTENT_CHANGE_TYPE_DRAG_DROPPED
+         */
+        @NonNull public static final AccessibilityAction ACTION_DRAG_DROP =
+                new AccessibilityAction(R.id.accessibilityActionDragDrop);
+
+        /**
+         * Action to cancel a drag.
+         * <p>
+         * This action is added to the source that started a drag with {@link #ACTION_DRAG_START}.
+         * </p>
+         *
+         * @see AccessibilityEvent#CONTENT_CHANGE_TYPE_DRAG_CANCELLED
+         */
+        @NonNull public static final AccessibilityAction ACTION_DRAG_CANCEL =
+                new AccessibilityAction(R.id.accessibilityActionDragCancel);
+
         private final int mActionId;
         private final CharSequence mLabel;
 
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index edcb59a..76e2261 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
+import android.app.ActivityTaskManager;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Parcel;
@@ -114,6 +115,7 @@
     private int mBooleanProperties;
     private int mId = UNDEFINED_WINDOW_ID;
     private int mParentId = UNDEFINED_WINDOW_ID;
+    private int mTaskId = ActivityTaskManager.INVALID_TASK_ID;
     private Region mRegionInScreen = new Region();
     private LongArray mChildIds;
     private CharSequence mTitle;
@@ -307,6 +309,28 @@
     }
 
     /**
+     * Gets the task ID.
+     *
+     * @return The task ID.
+     *
+     * @hide
+     */
+    public int getTaskId() {
+        return mTaskId;
+    }
+
+    /**
+     * Sets the task ID.
+     *
+     * @param taskId The task ID.
+     *
+     * @hide
+     */
+    public void setTaskId(int taskId) {
+        mTaskId = taskId;
+    }
+
+    /**
      * Sets the unique id of the IAccessibilityServiceConnection over which
      * this instance can send requests to the system.
      *
@@ -578,6 +602,7 @@
         parcel.writeInt(mBooleanProperties);
         parcel.writeInt(mId);
         parcel.writeInt(mParentId);
+        parcel.writeInt(mTaskId);
         mRegionInScreen.writeToParcel(parcel, flags);
         parcel.writeCharSequence(mTitle);
         parcel.writeLong(mAnchorId);
@@ -608,6 +633,7 @@
         mBooleanProperties = other.mBooleanProperties;
         mId = other.mId;
         mParentId = other.mParentId;
+        mTaskId = other.mTaskId;
         mRegionInScreen.set(other.mRegionInScreen);
         mTitle = other.mTitle;
         mAnchorId = other.mAnchorId;
@@ -631,6 +657,7 @@
         mBooleanProperties = parcel.readInt();
         mId = parcel.readInt();
         mParentId = parcel.readInt();
+        mTaskId = parcel.readInt();
         mRegionInScreen = Region.CREATOR.createFromParcel(parcel);
         mTitle = parcel.readCharSequence();
         mAnchorId = parcel.readLong();
@@ -676,6 +703,7 @@
         builder.append("title=").append(mTitle);
         builder.append(", displayId=").append(mDisplayId);
         builder.append(", id=").append(mId);
+        builder.append(", taskId=").append(mTaskId);
         builder.append(", type=").append(typeToString(mType));
         builder.append(", layer=").append(mLayer);
         builder.append(", region=").append(mRegionInScreen);
@@ -719,6 +747,7 @@
         mBooleanProperties = 0;
         mId = UNDEFINED_WINDOW_ID;
         mParentId = UNDEFINED_WINDOW_ID;
+        mTaskId = ActivityTaskManager.INVALID_TASK_ID;
         mRegionInScreen.setEmpty();
         mChildIds = null;
         mConnectionId = UNDEFINED_WINDOW_ID;
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 38019c9..5f036a3 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -1013,8 +1013,10 @@
      *
      * @param imeConsumesInput {@code true} when the IME is consuming input and the cursor should be
      * hidden, {@code false} when input to the editor resumes and the cursor should be shown again.
-     * @return {@code true} on success, {@code false} if the input connection is no longer valid, or
-     * the protocol is not supported.
+     * @return For editor authors, the return value will always be ignored. For IME authors, this
+     *         method returns {@code true} if the request was sent (whether or not the associated
+     *         editor does something based on this request), {@code false} if the input connection
+     *         is no longer valid.
      */
     default boolean setImeConsumesInput(boolean imeConsumesInput) {
         return false;
diff --git a/core/java/android/view/translation/Translator.java b/core/java/android/view/translation/Translator.java
index edd0d16..606f39d 100644
--- a/core/java/android/view/translation/Translator.java
+++ b/core/java/android/view/translation/Translator.java
@@ -18,6 +18,7 @@
 
 import static android.view.translation.TranslationManager.STATUS_SYNC_CALL_FAIL;
 import static android.view.translation.TranslationManager.SYNC_CALLS_TIMEOUT_MS;
+import static android.view.translation.UiTranslationController.DEBUG;
 
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
@@ -38,7 +39,6 @@
 import com.android.internal.os.IResultReceiver;
 
 import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
 import java.util.Objects;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
@@ -395,27 +395,26 @@
 
     private static class TranslationResponseCallbackImpl extends ITranslationCallback.Stub {
 
-        private final WeakReference<Consumer<TranslationResponse>> mCallback;
-        private final WeakReference<Executor> mExecutor;
+        private final Consumer<TranslationResponse> mCallback;
+        private final Executor mExecutor;
 
         TranslationResponseCallbackImpl(Consumer<TranslationResponse> callback, Executor executor) {
-            mCallback = new WeakReference<>(callback);
-            mExecutor = new WeakReference<>(executor);
+            mCallback = callback;
+            mExecutor = executor;
         }
 
         @Override
         public void onTranslationResponse(TranslationResponse response) throws RemoteException {
-            final Consumer<TranslationResponse> callback = mCallback.get();
+            if (DEBUG) {
+                Log.i(TAG, "onTranslationResponse called.");
+            }
             final Runnable runnable =
-                    () -> callback.accept(response);
-            if (callback != null) {
-                final Executor executor = mExecutor.get();
-                final long token = Binder.clearCallingIdentity();
-                try {
-                    executor.execute(runnable);
-                } finally {
-                    restoreCallingIdentity(token);
-                }
+                    () -> mCallback.accept(response);
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(runnable);
+            } finally {
+                restoreCallingIdentity(token);
             }
         }
     }
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index a833591..442d099 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -356,7 +356,11 @@
             }
             for (int i = 0; i < translatedResult.size(); i++) {
                 final AutofillId autofillId = new AutofillId(translatedResult.keyAt(i));
-                final View view = mViews.get(autofillId).get();
+                final WeakReference<View> viewRef = mViews.get(autofillId);
+                if (viewRef == null) {
+                    continue;
+                }
+                final View view = viewRef.get();
                 if (view == null) {
                     Log.w(TAG, "onTranslationCompleted: the view for autofill id " + autofillId
                             + " may be gone.");
@@ -416,7 +420,11 @@
                     Log.w(TAG, "No AutofillId is set in ViewTranslationResponse");
                     continue;
                 }
-                final View view = mViews.get(autofillId).get();
+                final WeakReference<View> viewRef = mViews.get(autofillId);
+                if (viewRef == null) {
+                    continue;
+                }
+                final View view = viewRef.get();
                 if (view == null) {
                     Log.w(TAG, "onTranslationCompleted: the view for autofill id " + autofillId
                             + " may be gone.");
diff --git a/core/java/android/view/translation/UiTranslationManager.java b/core/java/android/view/translation/UiTranslationManager.java
index b9ed32c..3012e93 100644
--- a/core/java/android/view/translation/UiTranslationManager.java
+++ b/core/java/android/view/translation/UiTranslationManager.java
@@ -33,6 +33,7 @@
 import android.util.Log;
 import android.view.View;
 import android.view.autofill.AutofillId;
+import android.widget.TextView;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -42,11 +43,50 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 
-// TODO(b/178044703): Describe what UI Translation is.
 /**
- * The {@link UiTranslationManager} class provides ways for apps to use the ui translation
+ * <p>The {@link UiTranslationManager} class provides ways for apps to use the ui translation
  * function in framework.
+ *
+ * <p> The UI translation provides ways for apps to support inline translation for the views. For
+ * example the system supports text translation for {@link TextView}. To support UI translation for
+ * your views, you should override the following methods to provide the content to be translated
+ * and deal with the translated result. Here is an example for {@link TextView}-like views:
+ *
+ * <pre><code>
+ * public class MyTextView extends View {
+ *     public MyTextView(...) {
+ *         // implements how to show the translated result in your View in
+ *         // ViewTranslationCallback and set it by setViewTranslationCallback()
+ *         setViewTranslationCallback(new MyViewTranslationCallback());
+ *     }
+ *
+ *     public void onCreateViewTranslationRequest(int[] supportedFormats,
+ *             Consumer<ViewTranslationRequest> requestsCollector) {
+ *        // collect the information that needs to be translated
+ *        ViewTranslationRequest.Builder requestBuilder =
+ *                     new ViewTranslationRequest.Builder(getAutofillId());
+ *        requestBuilder.setValue(ViewTranslationRequest.ID_TEXT,
+ *                         TranslationRequestValue.forText(etText()));
+ *        requestsCollector.accept(requestBuilder.build());
+ *     }
+ *
+ *     public void onProvideContentCaptureStructure(
+ *             ViewStructure structure, int flags) {
+ *         // set ViewTranslationResponse
+ *         super.onViewTranslationResponse(response);
+ *     }
+ * }
+ * </code></pre>
+ *
+ * <p>If your view provides its own virtual hierarchy (for example, if it's a browser that draws the
+ * HTML using {@link android.graphics.Canvas} or native libraries in a different render process),
+ * you must override {@link View#onCreateVirtualViewTranslationRequests(long[], int[], Consumer)} to
+ * provide the content to be translated and implement
+ * {@link View#onVirtualViewTranslationResponses(android.util.LongSparseArray)} for the translated
+ * result. You also need to implement {@link android.view.translation.ViewTranslationCallback} to
+ * handle the translated information show or hide in your {@link View}.
  */
 public final class UiTranslationManager {
 
@@ -248,14 +288,14 @@
         }
     }
 
-    // TODO(b/178044703): Fix the View API link when it becomes public.
     /**
      * Register for notifications of UI Translation state changes on the foreground activity. This
      * is available to the owning application itself and also the current input method.
      * <p>
      * The application whose UI is being translated can use this to customize the UI Translation
      * behavior in ways that aren't made easy by methods like
-     * View#onCreateTranslationRequest().
+     * {@link View#onCreateViewTranslationRequest(int[], Consumer)}.
+     *
      * <p>
      * Input methods can use this to offer complementary features to UI Translation; for example,
      * enabling outgoing message translation when the system is translating incoming messages in a
diff --git a/core/java/android/view/translation/ViewTranslationCallback.java b/core/java/android/view/translation/ViewTranslationCallback.java
index 6efd621..a075662 100644
--- a/core/java/android/view/translation/ViewTranslationCallback.java
+++ b/core/java/android/view/translation/ViewTranslationCallback.java
@@ -19,9 +19,17 @@
 import android.annotation.NonNull;
 import android.annotation.UiThread;
 import android.view.View;
+import android.view.contentcapture.ContentCaptureSession;
 
 /**
- * Callback for handling the translated information show or hide in the {@link View}.
+ * <p> Callback for handling the translated information show or hide in the {@link View}.
+ *
+ * <p> When the platform intelligence starts translation of an app's ui, the system will call
+ * {@link View#dispatchCreateViewTranslationRequest} to collect the {@link ViewTranslationRequest}s
+ * for translation purpose by traversing the hierarchy then send to translation service. After
+ * receiving the {@link ViewTranslationResponse}, the system will call
+ * {@link ViewTranslationCallback#onShowTranslation(View)} to show the translated information for
+ * the {@link View}.
  */
 @UiThread
 public interface ViewTranslationCallback {
@@ -33,13 +41,19 @@
      * method will not be called before {@link View#onViewTranslationResponse} or
      * {@link View#onVirtualViewTranslationResponses}.
      *
+     * <p> NOTE: For TextView implementation, {@link ContentCaptureSession#notifyViewTextChanged}
+     * shouldn't be called with the translated text, simply calling setText() here will trigger the
+     * method. You should either override {@code View#onProvideContentCaptureStructure()} to report
+     * the original text instead of the translated text or use a different approach to display the
+     * translated text.
+     *
      * See {@link View#onViewTranslationResponse} for how to get the translated information.
      *
      * @return {@code true} if the View handles showing the translation.
      */
     boolean onShowTranslation(@NonNull View view);
     /**
-     * Called when the user wants to show the original text instead of the translated text. This
+     * Called when user wants to view the original content instead of the translated content. This
      * method will not be called before {@link View#onViewTranslationResponse} or
      * {@link View#onViewTranslationResponse}.
      *
@@ -47,7 +61,8 @@
      */
     boolean onHideTranslation(@NonNull View view);
     /**
-     * Called when the user finish the Ui translation and no longer to show the translated text.
+     * Called when the translation state is no longer needed. It should restore the original content
+     * and clear all saved states.
      *
      * @return {@code true} if the View handles clearing the translation.
      */
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index e827f0a..91fc5a5 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -5824,6 +5824,25 @@
         return false;
     }
 
+    /** @hide */
+    public void updateAppInfo(@NonNull ApplicationInfo info) {
+        if (mApplication != null && mApplication.sourceDir.equals(info.sourceDir)) {
+            // Overlay paths are generated against a particular version of an application.
+            // The overlays paths of a newly upgraded application are incompatible with the
+            // old version of the application.
+            mApplication = info;
+        }
+        if (hasSizedRemoteViews()) {
+            for (RemoteViews layout : mSizedRemoteViews) {
+                layout.updateAppInfo(info);
+            }
+        }
+        if (hasLandscapeAndPortraitLayouts()) {
+            mLandscape.updateAppInfo(info);
+            mPortrait.updateAppInfo(info);
+        }
+    }
+
     private Context getContextForResources(Context context) {
         if (mApplication != null) {
             if (context.getUserId() == UserHandle.getUserId(mApplication.uid)
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index ca6e735..f5c1bcf 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -11857,6 +11857,15 @@
 
                 if (text != null) {
                     if (expandedTopChar > 0 || expandedBottomChar < text.length()) {
+                        // Cap the offsets to avoid an OOB exception. That can happen if the
+                        // displayed/layout text, on which these offsets are calculated, is longer
+                        // than the original text (such as when the view is translated by the
+                        // platform intelligence).
+                        // TODO(b/196433694): Figure out how to better handle the offset
+                        // calculations for this case (so we don't unnecessarily cutoff the original
+                        // text, for example).
+                        expandedTopChar = Math.min(expandedTopChar, text.length());
+                        expandedBottomChar = Math.min(expandedBottomChar, text.length());
                         text = text.subSequence(expandedTopChar, expandedBottomChar);
                     }
 
diff --git a/core/java/android/window/SizeConfigurationBuckets.java b/core/java/android/window/SizeConfigurationBuckets.java
index 7422f24..f474f0a 100644
--- a/core/java/android/window/SizeConfigurationBuckets.java
+++ b/core/java/android/window/SizeConfigurationBuckets.java
@@ -16,6 +16,7 @@
 
 package android.window;
 
+import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
 import static android.content.pm.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
 
@@ -25,6 +26,7 @@
 import android.os.Parcelable;
 import android.util.SparseIntArray;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DataClass;
 
 import java.util.Arrays;
@@ -54,10 +56,24 @@
     @Nullable
     private final int[] mSmallest;
 
+    /** Screen Layout Size (screenLayout & SCREENLAYOUT_SIZE_MASK) buckets */
+    @Nullable
+    private final int[] mScreenLayoutSize;
+
+    /**
+     * Screen Layout Long (screenLayout & SCREENLAYOUT_LONG_MASK) boolean. Only need to know if a
+     * value is set because only two possible buckets, SCREENLAYOUT_LONG_NO and
+     * SCREENLAYOUT_LONG_YES, so if either is set, then any change is a bucket change.
+     */
+    private final boolean mScreenLayoutLongSet;
+
     public SizeConfigurationBuckets(Configuration[] sizeConfigurations) {
         SparseIntArray horizontal = new SparseIntArray();
         SparseIntArray vertical = new SparseIntArray();
         SparseIntArray smallest = new SparseIntArray();
+        SparseIntArray screenLayoutSize = new SparseIntArray();
+        int curScreenLayoutSize;
+        boolean screenLayoutLongSet = false;
         for (int i = sizeConfigurations.length - 1; i >= 0; i--) {
             Configuration config = sizeConfigurations[i];
             if (config.screenHeightDp != Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
@@ -69,23 +85,42 @@
             if (config.smallestScreenWidthDp != Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
                 smallest.put(config.smallestScreenWidthDp, 0);
             }
+            if ((curScreenLayoutSize = config.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK)
+                    != Configuration.SCREENLAYOUT_SIZE_UNDEFINED) {
+                screenLayoutSize.put(curScreenLayoutSize, 0);
+            }
+            if (!screenLayoutLongSet && (config.screenLayout & Configuration.SCREENLAYOUT_LONG_MASK)
+                    != Configuration.SCREENLAYOUT_LONG_UNDEFINED) {
+                screenLayoutLongSet = true;
+            }
         }
         mHorizontal = horizontal.copyKeys();
         mVertical = vertical.copyKeys();
         mSmallest = smallest.copyKeys();
+        mScreenLayoutSize = screenLayoutSize.copyKeys();
+        mScreenLayoutLongSet = screenLayoutLongSet;
     }
 
     /**
      * Get the changes between two configurations but don't count changes in sizes if they don't
-     * cross boundaries that are  important to the app.
+     * cross boundaries that are important to the app.
      *
      * This is a static helper to deal with null `buckets`. When no buckets have been specified,
      * this actually filters out all 3 size-configs. This is legacy behavior.
      */
-    public static int filterDiff(int diff, Configuration oldConfig, Configuration newConfig,
-            @Nullable SizeConfigurationBuckets buckets) {
+    public static int filterDiff(int diff, @NonNull Configuration oldConfig,
+            @NonNull Configuration newConfig, @Nullable SizeConfigurationBuckets buckets) {
+        final boolean nonSizeLayoutFieldsUnchanged =
+                areNonSizeLayoutFieldsUnchanged(oldConfig.screenLayout, newConfig.screenLayout);
         if (buckets == null) {
-            return diff & ~(CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE);
+            // Only unflip CONFIG_SCREEN_LAYOUT if non-size-related  attributes of screen layout do
+            // not change.
+            if (nonSizeLayoutFieldsUnchanged) {
+                return diff & ~(CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE
+                        | CONFIG_SCREEN_LAYOUT);
+            } else {
+                return diff & ~(CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE);
+            }
         }
         if ((diff & CONFIG_SCREEN_SIZE) != 0) {
             final boolean crosses = buckets.crossesHorizontalSizeThreshold(oldConfig.screenWidthDp,
@@ -103,6 +138,13 @@
                 diff &= ~CONFIG_SMALLEST_SCREEN_SIZE;
             }
         }
+        if ((diff & CONFIG_SCREEN_LAYOUT) != 0 && nonSizeLayoutFieldsUnchanged) {
+            if (!buckets.crossesScreenLayoutSizeThreshold(oldConfig, newConfig)
+                    && !buckets.crossesScreenLayoutLongThreshold(oldConfig.screenLayout,
+                    newConfig.screenLayout)) {
+                diff &= ~CONFIG_SCREEN_LAYOUT;
+            }
+        }
         return diff;
     }
 
@@ -119,6 +161,61 @@
     }
 
     /**
+     * Returns whether a screen layout size threshold has been crossed.
+     */
+    @VisibleForTesting
+    public boolean crossesScreenLayoutSizeThreshold(@NonNull Configuration firstConfig,
+            @NonNull Configuration secondConfig) {
+        // If both the old and new screen layout are equal (both can be undefined), then no
+        // threshold is crossed.
+        if ((firstConfig.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK)
+                == (secondConfig.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK)) {
+            return false;
+        }
+        // Any time the new layout size is smaller than the old layout size, the activity has
+        // crossed a size threshold because layout size represents the smallest possible size the
+        // activity can occupy.
+        if (!secondConfig.isLayoutSizeAtLeast(firstConfig.screenLayout
+                & Configuration.SCREENLAYOUT_SIZE_MASK)) {
+            return true;
+        }
+        // If the new layout size is at least as large as the old layout size, then check if the new
+        // layout size has crossed a threshold.
+        if (mScreenLayoutSize != null) {
+            for (int screenLayoutSize : mScreenLayoutSize) {
+                if (firstConfig.isLayoutSizeAtLeast(screenLayoutSize)
+                        != secondConfig.isLayoutSizeAtLeast(screenLayoutSize)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean crossesScreenLayoutLongThreshold(int firstScreenLayout,
+            int secondScreenLayout) {
+        final int firstScreenLayoutLongValue = firstScreenLayout
+                & Configuration.SCREENLAYOUT_LONG_MASK;
+        final int secondScreenLayoutLongValue = secondScreenLayout
+                & Configuration.SCREENLAYOUT_LONG_MASK;
+        return mScreenLayoutLongSet && firstScreenLayoutLongValue != secondScreenLayoutLongValue;
+    }
+
+    /**
+     * Returns whether non-size related screen layout attributes have changed. If true, then
+     * {@link ActivityInfo#CONFIG_SCREEN_LAYOUT} should not be filtered out in
+     * {@link SizeConfigurationBuckets#filterDiff()} because the non-size related attributes
+     * do not have a bucket range like the size-related attributes of screen layout.
+     */
+    @VisibleForTesting
+    public static boolean areNonSizeLayoutFieldsUnchanged(int oldScreenLayout,
+            int newScreenLayout) {
+        final int nonSizeRelatedFields = Configuration.SCREENLAYOUT_LAYOUTDIR_MASK
+                | Configuration.SCREENLAYOUT_ROUND_MASK | Configuration.SCREENLAYOUT_COMPAT_NEEDED;
+        return (oldScreenLayout & nonSizeRelatedFields) == (newScreenLayout & nonSizeRelatedFields);
+    }
+
+    /**
      * The purpose of this method is to decide whether the activity needs to be relaunched upon
      * changing its size. In most cases the activities don't need to be relaunched, if the resize
      * is small, all the activity content has to do is relayout itself within new bounds. There are
@@ -132,7 +229,8 @@
      * it resizes width from 620dp to 700dp, it won't be relaunched as it stays on the same side
      * of the threshold.
      */
-    private static boolean crossesSizeThreshold(int[] thresholds, int firstDp,
+    @VisibleForTesting
+    public static boolean crossesSizeThreshold(int[] thresholds, int firstDp,
             int secondDp) {
         if (thresholds == null) {
             return false;
@@ -150,12 +248,13 @@
     @Override
     public String toString() {
         return Arrays.toString(mHorizontal) + " " + Arrays.toString(mVertical) + " "
-                + Arrays.toString(mSmallest);
+                + Arrays.toString(mSmallest) + " " + Arrays.toString(mScreenLayoutSize) + " "
+                + mScreenLayoutLongSet;
     }
 
 
 
-    // Code below generated by codegen v1.0.22.
+    // Code below generated by codegen v1.0.23.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -177,15 +276,25 @@
      *   Vertical (screenHeightDp) buckets
      * @param smallest
      *   Smallest (smallestScreenWidthDp) buckets
+     * @param screenLayoutSize
+     *   Screen Layout Size (screenLayout & SCREENLAYOUT_SIZE_MASK) buckets
+     * @param screenLayoutLongSet
+     *   Screen Layout Long (screenLayout & SCREENLAYOUT_LONG_MASK) boolean. Only need to know if a
+     *   value is set because only two possible buckets, SCREENLAYOUT_LONG_NO and
+     *   SCREENLAYOUT_LONG_YES, so if either is set, then any change is a bucket change.
      */
     @DataClass.Generated.Member
     public SizeConfigurationBuckets(
             @Nullable int[] horizontal,
             @Nullable int[] vertical,
-            @Nullable int[] smallest) {
+            @Nullable int[] smallest,
+            @Nullable int[] screenLayoutSize,
+            boolean screenLayoutLongSet) {
         this.mHorizontal = horizontal;
         this.mVertical = vertical;
         this.mSmallest = smallest;
+        this.mScreenLayoutSize = screenLayoutSize;
+        this.mScreenLayoutLongSet = screenLayoutLongSet;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -214,6 +323,24 @@
         return mSmallest;
     }
 
+    /**
+     * Screen Layout Size (screenLayout & SCREENLAYOUT_SIZE_MASK) buckets
+     */
+    @DataClass.Generated.Member
+    public @Nullable int[] getScreenLayoutSize() {
+        return mScreenLayoutSize;
+    }
+
+    /**
+     * Screen Layout Long (screenLayout & SCREENLAYOUT_LONG_MASK) boolean. Only need to know if a
+     * value is set because only two possible buckets, SCREENLAYOUT_LONG_NO and
+     * SCREENLAYOUT_LONG_YES, so if either is set, then any change is a bucket change.
+     */
+    @DataClass.Generated.Member
+    public boolean isScreenLayoutLongSet() {
+        return mScreenLayoutLongSet;
+    }
+
     @Override
     @DataClass.Generated.Member
     public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
@@ -221,13 +348,16 @@
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
         byte flg = 0;
+        if (mScreenLayoutLongSet) flg |= 0x10;
         if (mHorizontal != null) flg |= 0x1;
         if (mVertical != null) flg |= 0x2;
         if (mSmallest != null) flg |= 0x4;
+        if (mScreenLayoutSize != null) flg |= 0x8;
         dest.writeByte(flg);
         if (mHorizontal != null) dest.writeIntArray(mHorizontal);
         if (mVertical != null) dest.writeIntArray(mVertical);
         if (mSmallest != null) dest.writeIntArray(mSmallest);
+        if (mScreenLayoutSize != null) dest.writeIntArray(mScreenLayoutSize);
     }
 
     @Override
@@ -242,13 +372,17 @@
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
         byte flg = in.readByte();
+        boolean screenLayoutLongSet = (flg & 0x10) != 0;
         int[] horizontal = (flg & 0x1) == 0 ? null : in.createIntArray();
         int[] vertical = (flg & 0x2) == 0 ? null : in.createIntArray();
         int[] smallest = (flg & 0x4) == 0 ? null : in.createIntArray();
+        int[] screenLayoutSize = (flg & 0x8) == 0 ? null : in.createIntArray();
 
         this.mHorizontal = horizontal;
         this.mVertical = vertical;
         this.mSmallest = smallest;
+        this.mScreenLayoutSize = screenLayoutSize;
+        this.mScreenLayoutLongSet = screenLayoutLongSet;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -268,10 +402,10 @@
     };
 
     @DataClass.Generated(
-            time = 1615845864280L,
-            codegenVersion = "1.0.22",
+            time = 1628273704583L,
+            codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/window/SizeConfigurationBuckets.java",
-            inputSignatures = "private final @android.annotation.Nullable int[] mHorizontal\nprivate final @android.annotation.Nullable int[] mVertical\nprivate final @android.annotation.Nullable int[] mSmallest\npublic static  int filterDiff(int,android.content.res.Configuration,android.content.res.Configuration,android.window.SizeConfigurationBuckets)\nprivate  boolean crossesHorizontalSizeThreshold(int,int)\nprivate  boolean crossesVerticalSizeThreshold(int,int)\nprivate  boolean crossesSmallestSizeThreshold(int,int)\nprivate static  boolean crossesSizeThreshold(int[],int,int)\npublic @java.lang.Override java.lang.String toString()\nclass SizeConfigurationBuckets extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true)")
+            inputSignatures = "private final @android.annotation.Nullable int[] mHorizontal\nprivate final @android.annotation.Nullable int[] mVertical\nprivate final @android.annotation.Nullable int[] mSmallest\nprivate final @android.annotation.Nullable int[] mScreenLayoutSize\nprivate final  boolean mScreenLayoutLongSet\npublic static  int filterDiff(int,android.content.res.Configuration,android.content.res.Configuration,android.window.SizeConfigurationBuckets)\nprivate  boolean crossesHorizontalSizeThreshold(int,int)\nprivate  boolean crossesVerticalSizeThreshold(int,int)\nprivate  boolean crossesSmallestSizeThreshold(int,int)\npublic @com.android.internal.annotations.VisibleForTesting boolean crossesScreenLayoutSizeThreshold(android.content.res.Configuration,android.content.res.Configuration)\nprivate  boolean crossesScreenLayoutLongThreshold(int,int)\npublic static @com.android.internal.annotations.VisibleForTesting boolean areNonSizeLayoutFieldsUnchanged(int,int)\npublic static @com.android.internal.annotations.VisibleForTesting boolean crossesSizeThreshold(int[],int,int)\npublic @java.lang.Override java.lang.String toString()\nclass SizeConfigurationBuckets extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java
index 1efd2e3..acf20d7 100644
--- a/core/java/android/window/SplashScreenView.java
+++ b/core/java/android/window/SplashScreenView.java
@@ -89,11 +89,11 @@
     private boolean mNotCopyable;
     private boolean mIsCopied;
     private int mInitBackgroundColor;
-    private int mInitIconBackgroundColor;
     private View mIconView;
     private Bitmap mParceledIconBitmap;
     private View mBrandingImageView;
     private Bitmap mParceledBrandingBitmap;
+    private Bitmap mParceledIconBackgroundBitmap;
     private Duration mIconAnimationDuration;
     private Instant mIconAnimationStart;
 
@@ -130,11 +130,12 @@
         private final Context mContext;
         private int mIconSize;
         private @ColorInt int mBackgroundColor;
-        private @ColorInt int mIconBackground;
         private Bitmap mParceledIconBitmap;
+        private Bitmap mParceledIconBackgroundBitmap;
         private Drawable mIconDrawable;
         // It is only set for legacy splash screen which won't be sent across processes.
         private Drawable mOverlayDrawable;
+        private Drawable mIconBackground;
         private SurfaceControlViewHost.SurfacePackage mSurfacePackage;
         private RemoteCallback mClientCallback;
         private int mBrandingImageWidth;
@@ -155,7 +156,6 @@
         public Builder createFromParcel(SplashScreenViewParcelable parcelable) {
             mIconSize = parcelable.getIconSize();
             mBackgroundColor = parcelable.getBackgroundColor();
-            mIconBackground = parcelable.getIconBackground();
             mSurfacePackage = parcelable.mSurfacePackage;
             if (mSurfacePackage == null && parcelable.mIconBitmap != null) {
                 // We only create a Bitmap copies of immobile icons since animated icon are using
@@ -163,6 +163,11 @@
                 mIconDrawable = new BitmapDrawable(mContext.getResources(), parcelable.mIconBitmap);
                 mParceledIconBitmap = parcelable.mIconBitmap;
             }
+            if (parcelable.mIconBackground != null) {
+                mIconBackground = new BitmapDrawable(mContext.getResources(),
+                        parcelable.mIconBackground);
+                mParceledIconBackgroundBitmap = parcelable.mIconBackground;
+            }
             if (parcelable.mBrandingBitmap != null) {
                 setBrandingDrawable(new BitmapDrawable(mContext.getResources(),
                                 parcelable.mBrandingBitmap), parcelable.mBrandingWidth,
@@ -213,8 +218,8 @@
         /**
          * Set the background color for the icon.
          */
-        public Builder setIconBackground(int color) {
-            mIconBackground = color;
+        public Builder setIconBackground(Drawable iconBackground) {
+            mIconBackground = iconBackground;
             return this;
         }
 
@@ -245,7 +250,6 @@
             final SplashScreenView view = (SplashScreenView)
                     layoutInflater.inflate(R.layout.splash_screen_view, null, false);
             view.mInitBackgroundColor = mBackgroundColor;
-            view.mInitIconBackgroundColor = mIconBackground;
             if (mOverlayDrawable != null) {
                 view.setBackground(mOverlayDrawable);
             } else {
@@ -263,25 +267,29 @@
                         mIconAnimationDuration != null ? mIconAnimationDuration.toMillis() : 0);
                 view.mIconAnimationStart = mIconAnimationStart;
                 view.mIconAnimationDuration = mIconAnimationDuration;
-            } else {
-                view.mIconView = view.findViewById(R.id.splashscreen_icon_view);
-                if (mIconSize != 0) {
-                    final ViewGroup.LayoutParams params = view.mIconView.getLayoutParams();
-                    params.width = mIconSize;
-                    params.height = mIconSize;
-                    view.mIconView.setLayoutParams(params);
-                    if (mIconDrawable != null) {
-                        view.mIconView.setBackground(mIconDrawable);
-                    }
+            } else if (mIconSize != 0) {
+                ImageView imageView = view.findViewById(R.id.splashscreen_icon_view);
+                assert imageView != null;
+
+                final ViewGroup.LayoutParams params = imageView.getLayoutParams();
+                params.width = mIconSize;
+                params.height = mIconSize;
+                imageView.setLayoutParams(params);
+                if (mIconDrawable != null) {
+                    imageView.setImageDrawable(mIconDrawable);
                 }
+                if (mIconBackground != null) {
+                    imageView.setBackground(mIconBackground);
+                }
+                view.mIconView = imageView;
             }
             if (mOverlayDrawable != null || mIconDrawable == null) {
                 view.setNotCopyable();
             }
 
-            if (mParceledIconBitmap != null) {
-                view.mParceledIconBitmap = mParceledIconBitmap;
-            }
+            view.mParceledIconBackgroundBitmap = mParceledIconBackgroundBitmap;
+            view.mParceledIconBitmap = mParceledIconBitmap;
+
             // branding image
             if (mBrandingImageHeight > 0 && mBrandingImageWidth > 0 && mBrandingDrawable != null) {
                 final ViewGroup.LayoutParams params = view.mBrandingImageView.getLayoutParams();
@@ -310,6 +318,7 @@
         private SurfaceView createSurfaceView(@NonNull SplashScreenView view) {
             final SurfaceView surfaceView = new SurfaceView(view.getContext());
             surfaceView.setPadding(0, 0, 0, 0);
+            surfaceView.setBackground(mIconBackground);
             if (mSurfacePackage == null) {
                 if (DEBUG) {
                     Log.d(TAG,
@@ -475,7 +484,11 @@
         }
         setVisibility(GONE);
         if (mParceledIconBitmap != null) {
-            mIconView.setBackground(null);
+            if (mIconView instanceof ImageView) {
+                ((ImageView) mIconView).setImageDrawable(null);
+            } else if (mIconView != null) {
+                mIconView.setBackground(null);
+            }
             mParceledIconBitmap.recycle();
             mParceledIconBitmap = null;
         }
@@ -484,6 +497,13 @@
             mParceledBrandingBitmap.recycle();
             mParceledBrandingBitmap = null;
         }
+        if (mParceledIconBackgroundBitmap != null) {
+            if (mIconView != null) {
+                mIconView.setBackground(null);
+            }
+            mParceledIconBackgroundBitmap.recycle();
+            mParceledIconBackgroundBitmap = null;
+        }
         if (mWindow != null) {
             final DecorView decorView = (DecorView) mWindow.peekDecorView();
             if (DEBUG) {
@@ -600,15 +620,6 @@
     }
 
     /**
-     * Get the icon background color
-     * @hide
-     */
-    @TestApi
-    public @ColorInt int getIconBackgroundColor() {
-        return mInitIconBackgroundColor;
-    }
-
-    /**
      * Get the initial background color of this view.
      * @hide
      */
@@ -637,7 +648,7 @@
     public static class SplashScreenViewParcelable implements Parcelable {
         private int mIconSize;
         private int mBackgroundColor;
-        private int mIconBackground;
+        private Bitmap mIconBackground;
 
         private Bitmap mIconBitmap = null;
         private int mBrandingWidth;
@@ -653,11 +664,11 @@
         public SplashScreenViewParcelable(SplashScreenView view) {
             mIconSize = view.mIconView.getWidth();
             mBackgroundColor = view.getInitBackgroundColor();
-            mIconBackground = view.getIconBackgroundColor();
+            mIconBackground = copyDrawable(view.getIconView().getBackground());
             mSurfacePackage = view.mSurfacePackageCopy;
             if (mSurfacePackage == null) {
                 // We only need to copy the drawable if we are not using a SurfaceView
-                mIconBitmap = copyDrawable(view.getIconView().getBackground());
+                mIconBitmap = copyDrawable(((ImageView) view.getIconView()).getDrawable());
             }
             mBrandingBitmap = copyDrawable(view.getBrandingView().getBackground());
 
@@ -703,7 +714,7 @@
             mBrandingBitmap = source.readTypedObject(Bitmap.CREATOR);
             mIconAnimationStartMillis = source.readLong();
             mIconAnimationDurationMillis = source.readLong();
-            mIconBackground = source.readInt();
+            mIconBackground = source.readTypedObject(Bitmap.CREATOR);
             mSurfacePackage = source.readTypedObject(SurfaceControlViewHost.SurfacePackage.CREATOR);
             mClientCallback = source.readTypedObject(RemoteCallback.CREATOR);
         }
@@ -723,7 +734,7 @@
             dest.writeTypedObject(mBrandingBitmap, flags);
             dest.writeLong(mIconAnimationStartMillis);
             dest.writeLong(mIconAnimationDurationMillis);
-            dest.writeInt(mIconBackground);
+            dest.writeTypedObject(mIconBackground, flags);
             dest.writeTypedObject(mSurfacePackage, flags);
             dest.writeTypedObject(mClientCallback, flags);
         }
@@ -760,10 +771,6 @@
             return mBackgroundColor;
         }
 
-        int getIconBackground() {
-            return mIconBackground;
-        }
-
         /**
          * Sets the {@link RemoteCallback} that will be called by the client to notify the shell
          * of the removal of the {@link SplashScreenView}.
diff --git a/core/java/android/window/TransitionFilter.java b/core/java/android/window/TransitionFilter.java
index bb3f90b..db15145 100644
--- a/core/java/android/window/TransitionFilter.java
+++ b/core/java/android/window/TransitionFilter.java
@@ -27,6 +27,7 @@
 import android.content.ComponentName;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.view.WindowManager;
 
 /**
  * A parcelable filter that can be used for rerouting transitions to a remote. This is a local
@@ -55,6 +56,12 @@
      */
     @Nullable public @TransitionType int[] mTypeSet = null;
 
+    /** All flags must be set on a transition. */
+    public @WindowManager.TransitionFlags int mFlags = 0;
+
+    /** All flags must NOT be set on a transition. */
+    public @WindowManager.TransitionFlags int mNotFlags = 0;
+
     /**
      * A list of required changes. To pass, a transition must meet all requirements.
      */
@@ -65,6 +72,8 @@
 
     private TransitionFilter(Parcel in) {
         mTypeSet = in.createIntArray();
+        mFlags = in.readInt();
+        mNotFlags = in.readInt();
         mRequirements = in.createTypedArray(Requirement.CREATOR);
     }
 
@@ -81,10 +90,19 @@
             }
             if (!typePass) return false;
         }
+        if ((info.getFlags() & mFlags) != mFlags) {
+            return false;
+        }
+        if ((info.getFlags() & mNotFlags) != 0) {
+            return false;
+        }
         // Make sure info meets all of the requirements.
         if (mRequirements != null) {
             for (int i = 0; i < mRequirements.length; ++i) {
-                if (!mRequirements[i].matches(info)) return false;
+                final boolean matches = mRequirements[i].matches(info);
+                if (matches == mRequirements[i].mNot) {
+                    return false;
+                }
             }
         }
         return true;
@@ -94,6 +112,8 @@
     /** @hide */
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeIntArray(mTypeSet);
+        dest.writeInt(mFlags);
+        dest.writeInt(mNotFlags);
         dest.writeTypedArray(mRequirements, flags);
     }
 
@@ -123,10 +143,12 @@
         sb.append("{types=[");
         if (mTypeSet != null) {
             for (int i = 0; i < mTypeSet.length; ++i) {
-                sb.append((i == 0 ? "" : ",") + mTypeSet[i]);
+                sb.append((i == 0 ? "" : ",") + WindowManager.transitTypeToString(mTypeSet[i]));
             }
         }
-        sb.append("] checks=[");
+        sb.append("] flags=0x" + Integer.toHexString(mFlags));
+        sb.append("] notFlags=0x" + Integer.toHexString(mNotFlags));
+        sb.append(" checks=[");
         if (mRequirements != null) {
             for (int i = 0; i < mRequirements.length; ++i) {
                 sb.append((i == 0 ? "" : ",") + mRequirements[i]);
@@ -141,7 +163,21 @@
      */
     public static final class Requirement implements Parcelable {
         public int mActivityType = ACTIVITY_TYPE_UNDEFINED;
+
+        /** This only matches if the change is independent of its parents. */
+        public boolean mMustBeIndependent = true;
+
+        /** If this matches, the parent filter will fail */
+        public boolean mNot = false;
+
         public int[] mModes = null;
+
+        /** Matches only if all the flags here are set on the change. */
+        public @TransitionInfo.ChangeFlags int mFlags = 0;
+
+        /** If this needs to be a task. */
+        public boolean mMustBeTask = false;
+
         public @ContainerOrder int mOrder = CONTAINER_ORDER_ANY;
         public ComponentName mTopActivity;
 
@@ -150,7 +186,11 @@
 
         private Requirement(Parcel in) {
             mActivityType = in.readInt();
+            mMustBeIndependent = in.readBoolean();
+            mNot = in.readBoolean();
             mModes = in.createIntArray();
+            mFlags = in.readInt();
+            mMustBeTask = in.readBoolean();
             mOrder = in.readInt();
             mTopActivity = in.readTypedObject(ComponentName.CREATOR);
         }
@@ -159,7 +199,7 @@
         boolean matches(@NonNull TransitionInfo info) {
             for (int i = info.getChanges().size() - 1; i >= 0; --i) {
                 final TransitionInfo.Change change = info.getChanges().get(i);
-                if (!TransitionInfo.isIndependent(change, info)) {
+                if (mMustBeIndependent && !TransitionInfo.isIndependent(change, info)) {
                     // Only look at independent animating windows.
                     continue;
                 }
@@ -183,6 +223,12 @@
                     }
                     if (!pass) continue;
                 }
+                if ((change.getFlags() & mFlags) != mFlags) {
+                    continue;
+                }
+                if (mMustBeTask && change.getTaskInfo() == null) {
+                    continue;
+                }
                 return true;
             }
             return false;
@@ -208,7 +254,11 @@
         /** @hide */
         public void writeToParcel(@NonNull Parcel dest, int flags) {
             dest.writeInt(mActivityType);
+            dest.writeBoolean(mMustBeIndependent);
+            dest.writeBoolean(mNot);
             dest.writeIntArray(mModes);
+            dest.writeInt(mFlags);
+            dest.writeBoolean(mMustBeTask);
             dest.writeInt(mOrder);
             dest.writeTypedObject(mTopActivity, flags);
         }
@@ -236,7 +286,10 @@
         @Override
         public String toString() {
             StringBuilder out = new StringBuilder();
-            out.append("{atype=" + WindowConfiguration.activityTypeToString(mActivityType));
+            out.append('{');
+            if (mNot) out.append("NOT ");
+            out.append("atype=" + WindowConfiguration.activityTypeToString(mActivityType));
+            out.append(" independent=" + mMustBeIndependent);
             out.append(" modes=[");
             if (mModes != null) {
                 for (int i = 0; i < mModes.length; ++i) {
@@ -244,8 +297,11 @@
                 }
             }
             out.append("]").toString();
+            out.append(" flags=" + TransitionInfo.flagsToString(mFlags));
+            out.append(" mustBeTask=" + mMustBeTask);
             out.append(" order=" + containerOrderToString(mOrder));
             out.append(" topActivity=").append(mTopActivity);
+            out.append("}");
             return out.toString();
         }
     }
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index a814399..c2ffc03 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -23,8 +23,10 @@
 import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
 import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_NONE;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
@@ -91,8 +93,19 @@
     /** The container is the display. */
     public static final int FLAG_IS_DISPLAY = 1 << 5;
 
+    /** The container can show on top of lock screen. */
+    public static final int FLAG_OCCLUDES_KEYGUARD = 1 << 6;
+
+    /**
+     * Only for IS_DISPLAY containers. Is set if the display has system alert windows. This is
+     * used to prevent seamless rotation.
+     * TODO(b/194540864): Once we can include all windows in transition, then replace this with
+     *         something like FLAG_IS_SYSTEM_ALERT instead. Then we can do mixed rotations.
+     */
+    public static final int FLAG_DISPLAY_HAS_ALERT_WINDOWS = 1 << 7;
+
     /** The first unused bit. This can be used by remotes to attach custom flags to this change. */
-    public static final int FLAG_FIRST_CUSTOM = 1 << 6;
+    public static final int FLAG_FIRST_CUSTOM = 1 << 8;
 
     /** @hide */
     @IntDef(prefix = { "FLAG_" }, value = {
@@ -103,6 +116,8 @@
             FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT,
             FLAG_IS_VOICE_INTERACTION,
             FLAG_IS_DISPLAY,
+            FLAG_OCCLUDES_KEYGUARD,
+            FLAG_DISPLAY_HAS_ALERT_WINDOWS,
             FLAG_FIRST_CUSTOM
     })
     public @interface ChangeFlags {}
@@ -205,6 +220,10 @@
         return mOptions;
     }
 
+    /**
+     * @return the list of {@link Change}s in this transition. The list is sorted top-to-bottom
+     *         in Z (meaning index 0 is the top-most container).
+     */
     @NonNull
     public List<Change> getChanges() {
         return mChanges;
@@ -231,10 +250,17 @@
         mChanges.add(change);
     }
 
+    /**
+     * Whether this transition includes keyguard going away.
+     */
+    public boolean isKeyguardGoingAway() {
+        return (mFlags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0;
+    }
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
-        sb.append("{t=" + transitTypeToString(mType) + " f=" + Integer.toHexString(mFlags)
+        sb.append("{t=" + transitTypeToString(mType) + " f=0x" + Integer.toHexString(mFlags)
                 + " ro=" + mRootOffset + " c=[");
         for (int i = 0; i < mChanges.size(); ++i) {
             if (i > 0) {
@@ -283,6 +309,12 @@
         if ((flags & FLAG_IS_DISPLAY) != 0) {
             sb.append((sb.length() == 0 ? "" : "|") + "IS_DISPLAY");
         }
+        if ((flags & FLAG_OCCLUDES_KEYGUARD) != 0) {
+            sb.append((sb.length() == 0 ? "" : "|") + "OCCLUDES_KEYGUARD");
+        }
+        if ((flags & FLAG_DISPLAY_HAS_ALERT_WINDOWS) != 0) {
+            sb.append((sb.length() == 0 ? "" : "|") + "DISPLAY_HAS_ALERT_WINDOWS");
+        }
         if ((flags & FLAG_FIRST_CUSTOM) != 0) {
             sb.append((sb.length() == 0 ? "" : "|") + "FIRST_CUSTOM");
         }
@@ -328,8 +360,10 @@
         private final Rect mEndAbsBounds = new Rect();
         private final Point mEndRelOffset = new Point();
         private ActivityManager.RunningTaskInfo mTaskInfo = null;
+        private boolean mAllowEnterPip;
         private int mStartRotation = ROTATION_UNDEFINED;
         private int mEndRotation = ROTATION_UNDEFINED;
+        private int mRotationAnimation = ROTATION_ANIMATION_UNSPECIFIED;
 
         public Change(@Nullable WindowContainerToken container, @NonNull SurfaceControl leash) {
             mContainer = container;
@@ -347,8 +381,10 @@
             mEndAbsBounds.readFromParcel(in);
             mEndRelOffset.readFromParcel(in);
             mTaskInfo = in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
+            mAllowEnterPip = in.readBoolean();
             mStartRotation = in.readInt();
             mEndRotation = in.readInt();
+            mRotationAnimation = in.readInt();
         }
 
         /** Sets the parent of this change's container. The parent must be a participant or null. */
@@ -389,12 +425,25 @@
             mTaskInfo = taskInfo;
         }
 
+        /** Sets the allowEnterPip flag which represents AppOpsManager check on PiP permission */
+        public void setAllowEnterPip(boolean allowEnterPip) {
+            mAllowEnterPip = allowEnterPip;
+        }
+
         /** Sets the start and end rotation of this container. */
         public void setRotation(@Surface.Rotation int start, @Surface.Rotation int end) {
             mStartRotation = start;
             mEndRotation = end;
         }
 
+        /**
+         * Sets the app-requested animation type for rotation. Will be one of the
+         * ROTATION_ANIMATION_ values in {@link android.view.WindowManager.LayoutParams};
+         */
+        public void setRotationAnimation(int anim) {
+            mRotationAnimation = anim;
+        }
+
         /** @return the container that is changing. May be null if non-remotable (eg. activity) */
         @Nullable
         public WindowContainerToken getContainer() {
@@ -458,6 +507,10 @@
             return mTaskInfo;
         }
 
+        public boolean getAllowEnterPip() {
+            return mAllowEnterPip;
+        }
+
         public int getStartRotation() {
             return mStartRotation;
         }
@@ -466,6 +519,11 @@
             return mEndRotation;
         }
 
+        /** @return the rotation animation. */
+        public int getRotationAnimation() {
+            return mRotationAnimation;
+        }
+
         /** @hide */
         @Override
         public void writeToParcel(@NonNull Parcel dest, int flags) {
@@ -478,8 +536,10 @@
             mEndAbsBounds.writeToParcel(dest, flags);
             mEndRelOffset.writeToParcel(dest, flags);
             dest.writeTypedObject(mTaskInfo, flags);
+            dest.writeBoolean(mAllowEnterPip);
             dest.writeInt(mStartRotation);
             dest.writeInt(mEndRotation);
+            dest.writeInt(mRotationAnimation);
         }
 
         @NonNull
@@ -507,7 +567,7 @@
             return "{" + mContainer + "(" + mParent + ") leash=" + mLeash
                     + " m=" + modeToString(mMode) + " f=" + flagsToString(mFlags) + " sb="
                     + mStartAbsBounds + " eb=" + mEndAbsBounds + " eo=" + mEndRelOffset + " r="
-                    + mStartRotation + "->" + mEndRotation + "}";
+                    + mStartRotation + "->" + mEndRotation + ":" + mRotationAnimation + "}";
         }
     }
 
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index a735bbc..387837d 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -506,19 +506,23 @@
      * {@link #setAdjacentRoots(WindowContainerToken, WindowContainerToken)}, but can be used with
      * fragmentTokens when that TaskFragments haven't been created (but will be created in the same
      * {@link WindowContainerTransaction}).
+     * To reset it, pass {@code null} for {@code fragmentToken2}.
      * @param fragmentToken1    client assigned unique token to create TaskFragment with specified
      *                          in {@link TaskFragmentCreationParams#getFragmentToken()}.
      * @param fragmentToken2    client assigned unique token to create TaskFragment with specified
-     *                          in {@link TaskFragmentCreationParams#getFragmentToken()}.
+     *                          in {@link TaskFragmentCreationParams#getFragmentToken()}. If it is
+     *                          {@code null}, the transaction will reset the adjacent TaskFragment.
      * @hide
      */
     @NonNull
     public WindowContainerTransaction setAdjacentTaskFragments(
-            @NonNull IBinder fragmentToken1, @NonNull IBinder fragmentToken2) {
+            @NonNull IBinder fragmentToken1, @Nullable IBinder fragmentToken2,
+            @Nullable TaskFragmentAdjacentOptions options) {
         final HierarchyOp hierarchyOp =
                 new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS)
                         .setContainer(fragmentToken1)
                         .setReparentContainer(fragmentToken2)
+                        .setLaunchOptions(options != null ? options.toBundle() : null)
                         .build();
         mHierarchyOps.add(hierarchyOp);
         return this;
@@ -1296,4 +1300,52 @@
             }
         }
     }
+
+    /**
+     * Helper class for building an options Bundle that can be used to set adjacent rules of
+     * TaskFragments.
+     * @hide
+     */
+    public static class TaskFragmentAdjacentOptions {
+        private static final String DELAY_PRIMARY_LAST_ACTIVITY_REMOVAL =
+                "android:transaction.adjacent.option.delay_primary_removal";
+        private static final String DELAY_SECONDARY_LAST_ACTIVITY_REMOVAL =
+                "android:transaction.adjacent.option.delay_secondary_removal";
+
+        private boolean mDelayPrimaryLastActivityRemoval;
+        private boolean mDelaySecondaryLastActivityRemoval;
+
+        public TaskFragmentAdjacentOptions() {
+        }
+
+        public TaskFragmentAdjacentOptions(@NonNull Bundle bundle) {
+            mDelayPrimaryLastActivityRemoval = bundle.getBoolean(
+                    DELAY_PRIMARY_LAST_ACTIVITY_REMOVAL);
+            mDelaySecondaryLastActivityRemoval = bundle.getBoolean(
+                    DELAY_SECONDARY_LAST_ACTIVITY_REMOVAL);
+        }
+
+        public void setDelayPrimaryLastActivityRemoval(boolean delay) {
+            mDelayPrimaryLastActivityRemoval = delay;
+        }
+
+        public void setDelaySecondaryLastActivityRemoval(boolean delay) {
+            mDelaySecondaryLastActivityRemoval = delay;
+        }
+
+        public boolean isDelayPrimaryLastActivityRemoval() {
+            return mDelayPrimaryLastActivityRemoval;
+        }
+
+        public boolean isDelaySecondaryLastActivityRemoval() {
+            return mDelaySecondaryLastActivityRemoval;
+        }
+
+        Bundle toBundle() {
+            final Bundle b = new Bundle();
+            b.putBoolean(DELAY_PRIMARY_LAST_ACTIVITY_REMOVAL, mDelayPrimaryLastActivityRemoval);
+            b.putBoolean(DELAY_SECONDARY_LAST_ACTIVITY_REMOVAL, mDelaySecondaryLastActivityRemoval);
+            return b;
+        }
+    }
 }
diff --git a/core/java/android/window/WindowContext.java b/core/java/android/window/WindowContext.java
index 5d40085..cfccb71 100644
--- a/core/java/android/window/WindowContext.java
+++ b/core/java/android/window/WindowContext.java
@@ -26,6 +26,7 @@
 import android.content.ContextWrapper;
 import android.content.res.Configuration;
 import android.os.Bundle;
+import android.view.Display;
 import android.view.WindowManager;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -42,29 +43,33 @@
  * @hide
  */
 @UiContext
-public class WindowContext extends ContextWrapper {
+public class WindowContext extends ContextWrapper implements WindowProvider {
     private final WindowManager mWindowManager;
-    private final @WindowManager.LayoutParams.WindowType int mType;
-    private final @Nullable Bundle mOptions;
+    @WindowManager.LayoutParams.WindowType
+    private final int mType;
+    @Nullable
+    private final Bundle mOptions;
     private final ComponentCallbacksController mCallbacksController =
             new ComponentCallbacksController();
     private final WindowContextController mController;
 
     /**
-     * Default constructor. Will generate a {@link WindowTokenClient} and attach this context to
-     * the token.
+     * Default implementation of {@link WindowContext}
+     * <p>
+     * Note that the users should call {@link Context#createWindowContext(Display, int, Bundle)}
+     * to create a {@link WindowContext} instead of using this constructor
+     * </p><p>
+     * Example usage:
+     * <pre class="prettyprint">
+     * Bundle options = new Bundle();
+     * options.put(KEY_ROOT_DISPLAY_AREA_ID, displayAreaInfo.rootDisplayAreaId);
+     * Context windowContext = context.createWindowContext(display, windowType, options);
+     * </pre></p>
      *
-     * @param base Base {@link Context} for this new instance.
-     * @param type Window type to be used with this context.
-     * @param options A bundle used to pass window-related options. For example, on device with
-     *                multiple DisplayAreaGroups, one may specify the RootDisplayArea for the window
-     *                using {@link DisplayAreaOrganizer#KEY_ROOT_DISPLAY_AREA_ID} in the options.
-     *                Example usage:
-     *                Bundle options = new Bundle();
-     *                options.put(KEY_ROOT_DISPLAY_AREA_ID, displayAreaInfo.rootDisplayAreaId);
-     *                Context windowContext = context.createWindowContext(display, type, options);
+     * @param base    Base {@link Context} for this new instance.
+     * @param type    Window type to be used with this context.
+     * @param options A bundle used to pass window-related options.
      * @see DisplayAreaInfo#rootDisplayAreaId
-     * @hide
      */
     public WindowContext(@NonNull Context base, int type, @Nullable Bundle options) {
         super(base);
@@ -110,10 +115,13 @@
 
     @Override
     public void destroy() {
-        mCallbacksController.clearCallbacks();
-        // Called to the base ContextImpl to do final clean-up.
-        getBaseContext().destroy();
-        Reference.reachabilityFence(this);
+        try {
+            mCallbacksController.clearCallbacks();
+            // Called to the base ContextImpl to do final clean-up.
+            getBaseContext().destroy();
+        } finally {
+            Reference.reachabilityFence(this);
+        }
     }
 
     @Override
@@ -130,4 +138,15 @@
     void dispatchConfigurationChanged(@NonNull Configuration newConfig) {
         mCallbacksController.dispatchConfigurationChanged(newConfig);
     }
+
+    @Override
+    public int getWindowType() {
+        return mType;
+    }
+
+    @Nullable
+    @Override
+    public Bundle getWindowContextOptions() {
+        return mOptions;
+    }
 }
diff --git a/core/java/android/window/WindowInfosListener.java b/core/java/android/window/WindowInfosListener.java
new file mode 100644
index 0000000..4376e3e
--- /dev/null
+++ b/core/java/android/window/WindowInfosListener.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import android.view.InputWindowHandle;
+
+import libcore.util.NativeAllocationRegistry;
+
+/**
+ * Listener for getting {@link InputWindowHandle} updates from SurfaceFlinger.
+ * @hide
+ */
+public abstract class WindowInfosListener {
+    private final long mNativeListener;
+
+    public WindowInfosListener() {
+        NativeAllocationRegistry registry = NativeAllocationRegistry.createMalloced(
+                WindowInfosListener.class.getClassLoader(), nativeGetFinalizer());
+
+        mNativeListener = nativeCreate(this);
+        registry.registerNativeAllocation(this, mNativeListener);
+    }
+
+    /**
+     * Called when WindowInfos in SurfaceFlinger have changed.
+     * @param windowHandles Reverse Z ordered array of window information that was on screen,
+     *                      where the first value is the topmost window.
+     */
+    public abstract void onWindowInfosChanged(InputWindowHandle[] windowHandles);
+
+    /**
+     * Register the WindowInfosListener.
+     */
+    public void register() {
+        nativeRegister(mNativeListener);
+    }
+
+    /**
+     * Unregisters the WindowInfosListener.
+     */
+    public void unregister() {
+        nativeUnregister(mNativeListener);
+    }
+
+    private static native long nativeCreate(WindowInfosListener thiz);
+    private static native void nativeRegister(long ptr);
+    private static native void nativeUnregister(long ptr);
+    private static native long nativeGetFinalizer();
+}
diff --git a/core/java/android/window/WindowProvider.java b/core/java/android/window/WindowProvider.java
new file mode 100644
index 0000000..b078b93
--- /dev/null
+++ b/core/java/android/window/WindowProvider.java
@@ -0,0 +1,39 @@
+/*
+ * 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.window;
+
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.view.WindowManager.LayoutParams.WindowType;
+
+/**
+ * An interface to provide a non-activity window.
+ * Examples are {@link WindowContext} and {@link WindowProviderService}.
+ *
+ * @hide
+ */
+public interface WindowProvider {
+    /** @hide */
+    String KEY_IS_WINDOW_PROVIDER_SERVICE = "android.windowContext.isWindowProviderService";
+
+    /** Gets the window type of this provider */
+    @WindowType
+    int getWindowType();
+
+    /** Gets the launch options of this provider */
+    @Nullable
+    Bundle getWindowContextOptions();
+}
diff --git a/core/java/android/window/WindowProviderService.java b/core/java/android/window/WindowProviderService.java
index 5171adf..f8484d1 100644
--- a/core/java/android/window/WindowProviderService.java
+++ b/core/java/android/window/WindowProviderService.java
@@ -42,21 +42,39 @@
  * {@link WindowContext}, but is represented as {@link Service}.
  *
  * @see android.inputmethodservice.InputMethodService
- * @see android.accessibilityservice.AccessibilityService
  *
  * @hide
  */
 @TestApi
 @UiContext
-public abstract class WindowProviderService extends Service {
+public abstract class WindowProviderService extends Service implements WindowProvider {
 
+    private final Bundle mOptions;
     private final WindowTokenClient mWindowToken = new WindowTokenClient();
     private final WindowContextController mController = new WindowContextController(mWindowToken);
     private WindowManager mWindowManager;
     private boolean mInitialized;
 
     /**
-     * Returns the type of this {@link WindowProviderService}.
+     * Returns {@code true} if the {@code windowContextOptions} declares that it is a
+     * {@link WindowProviderService}.
+     *
+     * @hide
+     */
+    public static boolean isWindowProviderService(@Nullable Bundle windowContextOptions) {
+        if (windowContextOptions == null) {
+            return false;
+        }
+        return (windowContextOptions.getBoolean(KEY_IS_WINDOW_PROVIDER_SERVICE, false));
+    }
+
+    public WindowProviderService() {
+        mOptions = new Bundle();
+        mOptions.putBoolean(KEY_IS_WINDOW_PROVIDER_SERVICE, true);
+    }
+
+    /**
+     * Returns the window type of this {@link WindowProviderService}.
      * Each inheriting class must implement this method to provide the type of the window. It is
      * used similar to {@code type} of {@link Context#createWindowContext(int, Bundle)}
      *
@@ -68,15 +86,24 @@
     @SuppressLint("OnNameExpected")
     // Suppress the lint because it is not a callback and users should provide window type
     // so we cannot make it final.
-    public abstract @WindowType int getWindowType();
+    @WindowType
+    @Override
+    public abstract int getWindowType();
 
     /**
      * Returns the option of this {@link WindowProviderService}.
-     * Default is {@code null}. The inheriting class can implement this method to provide the
-     * customization {@code option} of the window. It is used similar to {@code options} of
-     * {@link Context#createWindowContext(int, Bundle)}
-     *
-     * @see Context#createWindowContext(int, Bundle)
+     * <p>
+     * The inheriting class can implement this method to provide the customization {@code option} of
+     * the window, but must be based on this method's returned value.
+     * It is used similar to {@code options} of {@link Context#createWindowContext(int, Bundle)}
+     * </p>
+     * <pre class="prettyprint">
+     * public Bundle getWindowContextOptions() {
+     *     final Bundle options = super.getWindowContextOptions();
+     *     options.put(KEY_ROOT_DISPLAY_AREA_ID, displayAreaInfo.rootDisplayAreaId);
+     *     return options;
+     * }
+     * </pre>
      *
      * @hide
      */
@@ -85,8 +112,10 @@
     // Suppress the lint because it is not a callback and users may override this API to provide
     // launch option. Also, the return value of this API is null by default.
     @Nullable
+    @CallSuper
+    @Override
     public Bundle getWindowContextOptions() {
-        return null;
+        return mOptions;
     }
 
     /**
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index 284b4b9..f3e3859 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -94,6 +94,8 @@
         onConfigurationChanged(newConfig, newDisplayId, true /* shouldReportConfigChange */);
     }
 
+    // TODO(b/192048581): rewrite this method based on WindowContext and WindowProviderService
+    //  are inherited from WindowProvider.
     /**
      * Called when {@link Configuration} updates from the server side receive.
      *
diff --git a/core/java/com/android/ims/internal/uce/common/CapInfo.java b/core/java/com/android/ims/internal/uce/common/CapInfo.java
index bca647a..f5c4b1f 100644
--- a/core/java/com/android/ims/internal/uce/common/CapInfo.java
+++ b/core/java/com/android/ims/internal/uce/common/CapInfo.java
@@ -20,6 +20,10 @@
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.Bundle;
+
+import java.util.Map;
+import java.util.HashMap;
 
 /** Class for capability discovery information.
  *  @hide */
@@ -88,6 +92,95 @@
     /** Time used to compute when to query again. */
     private long mCapTimestamp = 0;
 
+    private Map<String, String> mCapInfoMap = new HashMap<String, String>();
+
+    /** IM session feature tag key. */
+    public static final String INSTANT_MSG =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcse.im\"";
+    /** File transfer feature tag key. */
+    public static final String FILE_TRANSFER =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcse.ft\"";
+    /** File transfer Thumbnail feature tag key. */
+    public static final String FILE_TRANSFER_THUMBNAIL =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.ftthumb\"";
+    /** File transfer Store and forward feature tag key. */
+    public static final String FILE_TRANSFER_SNF =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.ftstandfw\"";
+    /** File transfer HTTP feature tag key. */
+    public static final String FILE_TRANSFER_HTTP =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.fthttp\"";
+    /** Image sharing feature tag key. */
+    public static final String IMAGE_SHARE =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.gsma-is\"";
+    /** Video sharing during a CS call feature tag key-- IR-74. */
+    public static final String VIDEO_SHARE_DURING_CS = "+g.3gpp.cs-voice";
+    /** Video sharing outside of voice call feature tag key-- IR-84. */
+    public static final String VIDEO_SHARE =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.gsma-vs\"";
+    /** Social presence feature tag key. */
+    public static final String SOCIAL_PRESENCE =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcse.sp\"";
+    /** Presence discovery feature tag key. */
+    public static final String CAPDISC_VIA_PRESENCE =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcse.dp\"";
+    /** IP voice call feature tag key (IR-92/IR-58). */
+    public static final String IP_VOICE =
+      "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.mmtel\"";
+    /** IP video call feature tag key (IR-92/IR-58). */
+    public static final String IP_VIDEO =
+      "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.mmtel\";video";
+    /** IP Geo location Pull using File Transfer feature tag key. */
+    public static final String GEOPULL_FT =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.geopullft\"";
+    /** IP Geo location Pull feature tag key. */
+    public static final String GEOPULL =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.geopull\"";
+    /** IP Geo location Push feature tag key. */
+    public static final String GEOPUSH =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.geopush\"";
+    /** Standalone messaging feature tag key. */
+    public static final String STANDALONE_MSG =
+      "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg;" +
+      "urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.largemsg\"";
+    /** Full Store and Forward Group Chat information feature tag key. */
+    public static final String FULL_SNF_GROUPCHAT =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.fullsfgroupchat\"";
+    /** RCS IP Voice call feature tag key.  */
+    public static final String RCS_IP_VOICE_CALL =
+      "+g.gsma.rcs.ipcall";
+    /** RCS IP Video call feature tag key.  */
+    public static final String RCS_IP_VIDEO_CALL =
+      "+g.gsma.rcs.ipvideocall";
+    /** RCS IP Video only call feature tag key.  */
+    public static final String RCS_IP_VIDEO_ONLY_CALL =
+      "+g.gsma.rcs.ipvideoonlycall";
+    /** IP Geo location Push using SMS feature tag key. */
+    public static final String GEOSMS =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gppapplication.ims.iari.rcs.geosms\"";
+    /** RCS call composer feature tag key. */
+    public static final String CALLCOMPOSER =
+      "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gppservice.ims.icsi.gsma.callcomposer\"";
+    /** RCS post-call feature tag key. */
+    public static final String POSTCALL =
+      "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gppservice.ims.icsi.gsma.callunanswered\"";
+    /** Shared map feature tag key. */
+    public static final String SHAREDMAP =
+      "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gppservice.ims.icsi.gsma.sharedmap\"";
+    /** Shared Sketch feature tag key. */
+    public static final String SHAREDSKETCH =
+      "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gppservice.ims.icsi.gsma.sharedsketch\"";
+    /** Chatbot communication feature tag key. */
+    public static final String CHATBOT =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gppapplication.ims.iari.rcs.chatbot\"";
+    /** Chatbot role feature tag key. */
+    public static final String CHATBOTROLE = "+g.gsma.rcs.isbot";
+    /** Standalone Chatbot communication feature tag key. */
+    public static final String STANDALONE_CHATBOT =
+      "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcs.chatbot.sa\"";
+    /** MMtel based call composer feature tag key. */
+    public static final String MMTEL_CALLCOMPOSER = "+g.gsma.callcomposer";
+
+
 
     /**
      * Constructor for the CapInfo class.
@@ -99,6 +192,7 @@
 
     /**
      * Checks whether IM is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isImSupported() {
@@ -107,6 +201,7 @@
 
     /**
      * Sets IM as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setImSupported(boolean imSupported) {
@@ -115,6 +210,7 @@
 
     /**
      * Checks whether FT Thumbnail is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isFtThumbSupported() {
@@ -123,16 +219,16 @@
 
     /**
      * Sets FT thumbnail as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setFtThumbSupported(boolean ftThumbSupported) {
         this.mFtThumbSupported = ftThumbSupported;
     }
 
-
-
     /**
      * Checks whether FT Store and Forward is supported
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isFtSnFSupported() {
@@ -141,6 +237,7 @@
 
     /**
      * Sets FT Store and Forward as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setFtSnFSupported(boolean  ftSnFSupported) {
@@ -149,6 +246,7 @@
 
    /**
     * Checks whether File transfer HTTP is supported.
+    * @deprecated Use {@link #isCapabilitySupported(String)} instead.
     */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public boolean isFtHttpSupported() {
@@ -157,6 +255,7 @@
 
    /**
     * Sets File transfer HTTP as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
     */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public void setFtHttpSupported(boolean  ftHttpSupported) {
@@ -165,6 +264,7 @@
 
     /**
      * Checks whether FT is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isFtSupported() {
@@ -173,6 +273,7 @@
 
     /**
      * Sets FT as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setFtSupported(boolean ftSupported) {
@@ -181,6 +282,7 @@
 
     /**
      * Checks whether IS is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isIsSupported() {
@@ -189,6 +291,7 @@
 
     /**
      * Sets IS as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setIsSupported(boolean isSupported) {
@@ -197,6 +300,7 @@
 
     /**
      * Checks whether video sharing is supported during a CS call.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isVsDuringCSSupported() {
@@ -204,8 +308,9 @@
     }
 
     /**
-     *  Sets video sharing as supported or not supported during a CS
-     *  call.
+     * Sets video sharing as supported or not supported during a CS
+     * call.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setVsDuringCSSupported(boolean vsDuringCSSupported) {
@@ -213,8 +318,9 @@
     }
 
     /**
-     *  Checks whether video sharing outside a voice call is
-     *   supported.
+     * Checks whether video sharing outside a voice call is
+     *  supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isVsSupported() {
@@ -223,6 +329,7 @@
 
     /**
      * Sets video sharing as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setVsSupported(boolean vsSupported) {
@@ -231,6 +338,7 @@
 
     /**
      * Checks whether social presence is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isSpSupported() {
@@ -239,6 +347,7 @@
 
     /**
      * Sets social presence as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setSpSupported(boolean spSupported) {
@@ -248,6 +357,7 @@
     /**
      * Checks whether capability discovery via presence is
      * supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isCdViaPresenceSupported() {
@@ -257,6 +367,7 @@
     /**
      * Sets capability discovery via presence as supported or not
      * supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setCdViaPresenceSupported(boolean cdViaPresenceSupported) {
@@ -265,6 +376,7 @@
 
     /**
      * Checks whether IP voice call is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isIpVoiceSupported() {
@@ -273,6 +385,7 @@
 
     /**
      * Sets IP voice call as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setIpVoiceSupported(boolean ipVoiceSupported) {
@@ -281,6 +394,7 @@
 
     /**
      * Checks whether IP video call is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isIpVideoSupported() {
@@ -289,6 +403,7 @@
 
     /**
      * Sets IP video call as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setIpVideoSupported(boolean ipVideoSupported) {
@@ -298,6 +413,7 @@
    /**
     * Checks whether Geo location Pull using File Transfer is
     * supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
     */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public boolean isGeoPullFtSupported() {
@@ -307,6 +423,7 @@
    /**
     * Sets Geo location Pull using File Transfer as supported or
     * not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
     */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public void setGeoPullFtSupported(boolean geoPullFtSupported) {
@@ -315,6 +432,7 @@
 
     /**
      * Checks whether Geo Pull is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isGeoPullSupported() {
@@ -323,6 +441,7 @@
 
     /**
      * Sets Geo Pull as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setGeoPullSupported(boolean geoPullSupported) {
@@ -331,6 +450,7 @@
 
     /**
      * Checks whether Geo Push is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isGeoPushSupported() {
@@ -339,6 +459,7 @@
 
     /**
      * Sets Geo Push as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setGeoPushSupported(boolean geoPushSupported) {
@@ -347,6 +468,7 @@
 
     /**
      * Checks whether short messaging is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isSmSupported() {
@@ -355,6 +477,7 @@
 
     /**
      * Sets short messaging as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setSmSupported(boolean smSupported) {
@@ -363,22 +486,32 @@
 
     /**
      * Checks whether store/forward and group chat are supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isFullSnFGroupChatSupported() {
         return mFullSnFGroupChatSupported;
     }
 
+    /**
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
+     */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isRcsIpVoiceCallSupported() {
         return mRcsIpVoiceCallSupported;
     }
 
+    /**
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
+     */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isRcsIpVideoCallSupported() {
         return mRcsIpVideoCallSupported;
     }
 
+    /**
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
+     */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public boolean isRcsIpVideoOnlyCallSupported() {
         return mRcsIpVideoOnlyCallSupported;
@@ -386,20 +519,32 @@
 
     /**
      * Sets store/forward and group chat supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setFullSnFGroupChatSupported(boolean fullSnFGroupChatSupported) {
         this.mFullSnFGroupChatSupported = fullSnFGroupChatSupported;
     }
 
+    /**
+     * @deprecated Use {@link #addCapability(String, String)} instead.
+     */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setRcsIpVoiceCallSupported(boolean rcsIpVoiceCallSupported) {
         this.mRcsIpVoiceCallSupported = rcsIpVoiceCallSupported;
     }
+
+    /**
+     * @deprecated Use {@link #addCapability(String, String)} instead.
+     */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setRcsIpVideoCallSupported(boolean rcsIpVideoCallSupported) {
         this.mRcsIpVideoCallSupported = rcsIpVideoCallSupported;
     }
+
+    /**
+     * @deprecated Use {@link #addCapability(String, String)} instead.
+     */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public void setRcsIpVideoOnlyCallSupported(boolean rcsIpVideoOnlyCallSupported) {
         this.mRcsIpVideoOnlyCallSupported = rcsIpVideoOnlyCallSupported;
@@ -407,6 +552,7 @@
 
     /**
      * Checks whether Geo Push via SMS is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     public boolean isGeoSmsSupported() {
         return mGeoSmsSupported;
@@ -414,6 +560,7 @@
 
     /**
      * Sets Geolocation Push via SMS as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     public void setGeoSmsSupported(boolean geoSmsSupported) {
          this.mGeoSmsSupported = geoSmsSupported;
@@ -421,6 +568,7 @@
 
     /**
      * Checks whether RCS call composer is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     public boolean isCallComposerSupported() {
         return mCallComposerSupported;
@@ -428,6 +576,7 @@
 
     /**
      * Sets call composer as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     public void setCallComposerSupported(boolean callComposerSupported) {
         this.mCallComposerSupported = callComposerSupported;
@@ -435,6 +584,7 @@
 
     /**
      * Checks whether post call is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     public boolean isPostCallSupported(){
         return mPostCallSupported;
@@ -442,13 +592,15 @@
 
     /**
      * Sets post call as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
-     public void setPostCallSupported(boolean postCallSupported) {
-         this.mPostCallSupported = postCallSupported;
-     }
+    public void setPostCallSupported(boolean postCallSupported) {
+        this.mPostCallSupported = postCallSupported;
+    }
 
     /**
      * Checks whether shared map is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     public boolean isSharedMapSupported() {
         return mSharedMapSupported;
@@ -456,6 +608,7 @@
 
     /**
      * Sets shared map as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     public void setSharedMapSupported(boolean sharedMapSupported) {
         this.mSharedMapSupported = sharedMapSupported;
@@ -463,6 +616,7 @@
 
     /**
      * Checks whether shared sketch is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     public boolean isSharedSketchSupported() {
         return mSharedSketchSupported;
@@ -470,6 +624,7 @@
 
     /**
      * Sets shared sketch as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     public void setSharedSketchSupported(boolean sharedSketchSupported) {
         this.mSharedSketchSupported = sharedSketchSupported;
@@ -477,6 +632,7 @@
 
     /**
      * Checks whether chatbot communication is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     public boolean isChatbotSupported() {
         return mChatbotSupported;
@@ -484,6 +640,7 @@
 
     /**
      * Sets chatbot communication as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     public void setChatbotSupported(boolean chatbotSupported) {
         this.mChatbotSupported = chatbotSupported;
@@ -491,6 +648,7 @@
 
     /**
      * Checks whether chatbot role is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     public boolean isChatbotRoleSupported() {
         return mChatbotRoleSupported;
@@ -498,6 +656,7 @@
 
     /**
      * Sets chatbot role as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     public void setChatbotRoleSupported(boolean chatbotRoleSupported) {
         this.mChatbotRoleSupported = chatbotRoleSupported;
@@ -505,6 +664,7 @@
 
     /**
      * Checks whether standalone chatbot communication is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     public boolean isSmChatbotSupported() {
         return mSmChatbotSupported;
@@ -512,6 +672,7 @@
 
     /**
      * Sets standalone chatbot communication as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     public void setSmChatbotSupported(boolean smChatbotSupported) {
         this.mSmChatbotSupported = smChatbotSupported;
@@ -519,6 +680,7 @@
 
     /**
      * Checks whether Mmtel based call composer is supported.
+     * @deprecated Use {@link #isCapabilitySupported(String)} instead.
      */
     public boolean isMmtelCallComposerSupported() {
         return mMmtelCallComposerSupported;
@@ -526,6 +688,7 @@
 
     /**
      * Sets Mmtel based call composer as supported or not supported.
+     * @deprecated Use {@link #addCapability(String, String)} instead.
      */
     public void setMmtelCallComposerSupported(boolean mmtelCallComposerSupported) {
         this.mMmtelCallComposerSupported = mmtelCallComposerSupported;
@@ -555,6 +718,84 @@
         this.mCapTimestamp = capTimestamp;
     }
 
+    /**
+     * Adds the feature tag string with supported versions to
+     * the mCapInfoMap.
+     * Map<String featureType, String versions>
+     * Versions format:
+     *    "+g.gsma.rcs.botversion=\"#=1"        -> Version 1 supported
+     *    "+g.gsma.rcs.botversion=\"#=1,#=2\""  -> Versions 1 and 2 are supported
+     *
+     * Example #1: Add standard feature tag with one version support
+     * addCapability(CapInfo.STANDALONE_CHATBOT, "+g.gsma.rcs.botversion=\"#=1");
+     * The above example indicates standalone chatbot feature tag is supported
+     * in version 1.
+     *
+     * Example #2: Add standard feature tag with multiple version support
+     * addCapability(CapInfo.CHATBOT, "+g.gsma.rcs.botversion=\"#=1,#=2\"");
+     * The above example indicates session based chatbot feature tag is supported
+     * in versions 1 and 2.
+     *
+     * Example #3: Add standard feature tag with no version support
+     * addCapability(CapInfo.INSTANT_MSG, "");
+     * The above example indicates im feature tag does not have version support.
+     *
+     * Example #4: Add custom/extension feature tag with no version support
+     * addCapability("+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcse.custom_im\"",
+     *               "");
+     * Call setNewFeatureTag(int presenceServiceHdl, String featureTag,
+     *           in PresServiceInfo serviceInfo, int userData) API
+     * in IPresenceService.aidl before calling addCapability() API
+     */
+    public void addCapability(String featureTagName, String versions) {
+        this.mCapInfoMap.put(featureTagName, versions);
+    }
+
+    /**
+     * Returns String of versions of the feature tag passed.
+     * Returns "" if versioning support is not present for the feature tag passed.
+     * Returns null if feature tag is not present.
+     *
+     * Example # 1:
+     * getCapabilityVersions(CapInfo.STANDALONE_CHATBOT);
+     * The above returns String in this format "+g.gsma.rcs.botversion=\"#=1,#=2\"",
+     * indicating more than one versions are supported for standalone chatbot feature tag
+     *
+     * Example # 2:
+     * getCapabilityVersions(CapInfo.INSTANT_MSG);
+     * The above returns empty String in this format "",
+     * indicating versions support is not present for im feature tag
+     *
+     * Example #3:
+     * getCapabilityVersions(
+     *   "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcse.custom_im\");
+     * The above returns String "",
+     * indicating version supported is not present for the custom feature tag passed.
+     */
+    public String getCapabilityVersions(String featureTagName) {
+        return mCapInfoMap.get(featureTagName);
+    }
+
+    /** Removes the entry of the feature tag passed, from the Map. */
+    public void removeCapability(String featureTagName) {
+        this.mCapInfoMap.remove(featureTagName);
+    }
+
+    /** Sets Map of feature tag string and string of supported versions. */
+    public void setCapInfoMap(Map<String, String> capInfoMap) {
+        this.mCapInfoMap = capInfoMap;
+    }
+
+    /** Gets Map of feature tag string and string of supported versions. */
+    public Map<String, String> getCapInfoMap() {
+        return mCapInfoMap;
+    }
+
+    /** Checks whether the featureTag is supported or not. */
+    public boolean isCapabilitySupported(String featureTag) {
+       return mCapInfoMap.containsKey(featureTag);
+    }
+
     public int describeContents() {
         // TODO Auto-generated method stub
         return 0;
@@ -594,6 +835,12 @@
         dest.writeInt(mRcsIpVideoOnlyCallSupported ? 1 : 0);
         dest.writeStringArray(mExts);
         dest.writeLong(mCapTimestamp);
+
+        Bundle capInfoBundle = new Bundle();
+        for (Map.Entry<String, String> entry : mCapInfoMap.entrySet()) {
+          capInfoBundle.putString(entry.getKey(), entry.getValue());
+        }
+        dest.writeBundle(capInfoBundle);
     }
 
     public static final Parcelable.Creator<CapInfo> CREATOR = new Parcelable.Creator<CapInfo>() {
@@ -646,5 +893,10 @@
 
         mExts = source.createStringArray();
         mCapTimestamp = source.readLong();
+
+        Bundle capInfoBundle = source.readBundle();
+        for (String key: capInfoBundle.keySet()) {
+          mCapInfoMap.put(key, capInfoBundle.getString(key));
+        }
     }
 }
diff --git a/core/java/com/android/ims/internal/uce/options/OptionsSipResponse.java b/core/java/com/android/ims/internal/uce/options/OptionsSipResponse.java
index acea0f0..3242081 100644
--- a/core/java/com/android/ims/internal/uce/options/OptionsSipResponse.java
+++ b/core/java/com/android/ims/internal/uce/options/OptionsSipResponse.java
@@ -30,6 +30,7 @@
     private int mSipResponseCode = 0;
     private int mRetryAfter = 0;
     private String mReasonPhrase = "";
+    private String mReasonHeader = "";
 
     /**
      * Gets the Options command ID.
@@ -117,6 +118,22 @@
     }
 
     /**
+     * Gets the reason header associated with the SIP response code.
+     * @hide
+     */
+    public String getReasonHeader() {
+        return mReasonHeader;
+    }
+
+    /**
+     * Sets the SIP response code reason phrase.
+     * @hide
+     */
+    public void setReasonHeader(String reasonHeader) {
+        this.mReasonHeader = reasonHeader;
+    }
+
+    /**
      * Constructor for the OptionsSipResponse class.
      * @hide
      */
@@ -138,6 +155,7 @@
         dest.writeString(mReasonPhrase);
         dest.writeParcelable(mCmdId, flags);
         dest.writeInt(mRetryAfter);
+        dest.writeString(mReasonHeader);
     }
 
     /** @hide */
@@ -164,5 +182,6 @@
         mReasonPhrase = source.readString();
         mCmdId = source.readParcelable(OptionsCmdId.class.getClassLoader());
         mRetryAfter = source.readInt();
+        mReasonHeader = source.readString();
     }
 }
diff --git a/core/java/com/android/ims/internal/uce/presence/PresSipResponse.java b/core/java/com/android/ims/internal/uce/presence/PresSipResponse.java
index 9549152..5e394ef 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresSipResponse.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresSipResponse.java
@@ -29,6 +29,7 @@
     private int mSipResponseCode = 0;
     private int mRetryAfter = 0;
     private String mReasonPhrase = "";
+    private String mReasonHeader = "";
 
     /**
      * Gets the Presence command ID.
@@ -123,6 +124,23 @@
     }
 
     /**
+     * Gets the reason header associated with the SIP response
+     * code.
+     * @hide
+     */
+    public String getReasonHeader() {
+        return mReasonHeader;
+    }
+
+    /**
+     * Sets the SIP response code reason header.
+     * @hide
+     */
+    public void setReasonHeader(String reasonHeader) {
+        this.mReasonHeader = reasonHeader;
+    }
+
+    /**
      * Constructor for the PresSipResponse class.
      * @hide
      */
@@ -141,6 +159,7 @@
         dest.writeString(mReasonPhrase);
         dest.writeParcelable(mCmdId, flags);
         dest.writeInt(mRetryAfter);
+        dest.writeString(mReasonHeader);
     }
 
     /** @hide */
@@ -168,5 +187,6 @@
         mReasonPhrase = source.readString();
         mCmdId = source.readParcelable(PresCmdId.class.getClassLoader());
         mRetryAfter = source.readInt();
+        mReasonHeader = source.readString();
     }
 }
\ No newline at end of file
diff --git a/core/java/com/android/ims/internal/uce/presence/PresTupleInfo.java b/core/java/com/android/ims/internal/uce/presence/PresTupleInfo.java
index 34a7b1e..ce3d568 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresTupleInfo.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresTupleInfo.java
@@ -27,6 +27,7 @@
     private String mFeatureTag = "";
     private String mContactUri = "";
     private String mTimestamp = "";
+    private String mVersion = "";
 
 
     /**
@@ -80,6 +81,22 @@
     }
 
     /**
+     * Gets the version.
+     * @hide
+     */
+    public String getVersion() {
+        return mVersion;
+    }
+
+    /**
+     * Sets the version.
+     * @hide
+     */
+    public void setVersion(String version) {
+        this.mVersion = version;
+    }
+
+    /**
      * Constructor for the PresTupleInfo class.
      * @hide
      */
@@ -96,6 +113,7 @@
         dest.writeString(mFeatureTag);
         dest.writeString(mContactUri);
         dest.writeString(mTimestamp);
+        dest.writeString(mVersion);
     }
 
     /** @hide */
@@ -121,5 +139,6 @@
         mFeatureTag = source.readString();
         mContactUri = source.readString();
         mTimestamp = source.readString();
+        mVersion = source.readString();
     }
 }
\ No newline at end of file
diff --git a/core/java/com/android/internal/app/IAppOpsStartedCallback.aidl b/core/java/com/android/internal/app/IAppOpsStartedCallback.aidl
index 3a108e7..06640cb 100644
--- a/core/java/com/android/internal/app/IAppOpsStartedCallback.aidl
+++ b/core/java/com/android/internal/app/IAppOpsStartedCallback.aidl
@@ -18,5 +18,6 @@
 
 // Iterface to observe op starts
 oneway interface IAppOpsStartedCallback {
-    void opStarted(int op, int uid, String packageName, String attributionTag, int flags, int mode);
+    void opStarted(int op, int uid, String packageName, String attributionTag, int flags, int mode,
+    int startedType, int attributionFlags, int attributionChainId);
 }
diff --git a/core/java/com/android/internal/display/BrightnessSynchronizer.java b/core/java/com/android/internal/display/BrightnessSynchronizer.java
index 55a2052..c9a9e51 100644
--- a/core/java/com/android/internal/display/BrightnessSynchronizer.java
+++ b/core/java/com/android/internal/display/BrightnessSynchronizer.java
@@ -16,11 +16,9 @@
 
 package com.android.internal.display;
 
-import android.annotation.NonNull;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
-import android.hardware.display.BrightnessInfo;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManager.DisplayListener;
 import android.net.Uri;
@@ -63,10 +61,10 @@
                     updateBrightnessFloatFromInt(msg.arg1);
                     break;
                 case MSG_UPDATE_INT:
-                    updateBrightnessIntFromFloat((BrightnessInfo) msg.obj);
+                    updateBrightnessIntFromFloat(Float.intBitsToFloat(msg.arg1));
                     break;
                 case MSG_UPDATE_BOTH:
-                    updateBoth((BrightnessInfo) msg.obj, Float.intBitsToFloat(msg.arg1));
+                    updateBoth(Float.intBitsToFloat(msg.arg1));
                     break;
                 default:
                     super.handleMessage(msg);
@@ -97,11 +95,11 @@
         brightnessSyncObserver = new BrightnessSyncObserver();
         brightnessSyncObserver.startObserving();
 
-        final BrightnessInfo brightnessInfo = getBrightnessInfo();
+        final float currentFloatBrightness = getScreenBrightnessFloat();
         final int currentIntBrightness = getScreenBrightnessInt(mContext);
 
-        if (brightnessInfo != null && !Float.isNaN(brightnessInfo.brightness)) {
-            updateBrightnessIntFromFloat(brightnessInfo);
+        if (!Float.isNaN(currentFloatBrightness)) {
+            updateBrightnessIntFromFloat(currentFloatBrightness);
         } else if (currentIntBrightness != -1) {
             updateBrightnessFloatFromInt(currentIntBrightness);
         } else {
@@ -114,23 +112,15 @@
 
     /**
      * Converts between the int brightness system and the float brightness system.
-     *
-     * @param brightnessInt The int brightness value to convert.
      */
     public static float brightnessIntToFloat(int brightnessInt) {
-        return brightnessIntToFloat(brightnessInt, null);
-    }
-
-    private static float brightnessIntToFloat(int brightnessInt, BrightnessInfo info) {
         if (brightnessInt == PowerManager.BRIGHTNESS_OFF) {
             return PowerManager.BRIGHTNESS_OFF_FLOAT;
         } else if (brightnessInt == PowerManager.BRIGHTNESS_INVALID) {
             return PowerManager.BRIGHTNESS_INVALID_FLOAT;
         } else {
-            final float minFloat = info != null
-                    ? info.brightnessMinimum : PowerManager.BRIGHTNESS_MIN;
-            final float maxFloat = info != null
-                    ? info.brightnessMaximum : PowerManager.BRIGHTNESS_MAX;
+            final float minFloat = PowerManager.BRIGHTNESS_MIN;
+            final float maxFloat = PowerManager.BRIGHTNESS_MAX;
             final float minInt = PowerManager.BRIGHTNESS_OFF + 1;
             final float maxInt = PowerManager.BRIGHTNESS_ON;
             return MathUtils.constrainedMap(minFloat, maxFloat, minInt, maxInt, brightnessInt);
@@ -138,28 +128,29 @@
     }
 
     /**
+     * Converts between the float brightness system and the int brightness system.
+     */
+    public static int brightnessFloatToInt(float brightnessFloat) {
+        return Math.round(brightnessFloatToIntRange(brightnessFloat));
+    }
+
+    /**
      * Translates specified value from the float brightness system to the int brightness system,
      * given the min/max of each range. Accounts for special values such as OFF and invalid values.
      * Value returned as a float primitive (to preserve precision), but is a value within the
      * int-system range.
-     *
-     * @param brightnessFloat The float brightness value to convert.
-     * @param info Brightness information to use in the conversion.
      */
-    public static int brightnessFloatToInt(float brightnessFloat, BrightnessInfo info) {
+    public static float brightnessFloatToIntRange(float brightnessFloat) {
         if (floatEquals(brightnessFloat, PowerManager.BRIGHTNESS_OFF_FLOAT)) {
             return PowerManager.BRIGHTNESS_OFF;
         } else if (Float.isNaN(brightnessFloat)) {
             return PowerManager.BRIGHTNESS_INVALID;
         } else {
-            final float minFloat = info != null
-                    ? info.brightnessMinimum : PowerManager.BRIGHTNESS_MIN;
-            final float maxFloat = info != null
-                    ? info.brightnessMaximum : PowerManager.BRIGHTNESS_MAX;
+            final float minFloat = PowerManager.BRIGHTNESS_MIN;
+            final float maxFloat = PowerManager.BRIGHTNESS_MAX;
             final float minInt = PowerManager.BRIGHTNESS_OFF + 1;
             final float maxInt = PowerManager.BRIGHTNESS_ON;
-            return Math.round(MathUtils.constrainedMap(minInt, maxInt, minFloat, maxFloat,
-                    brightnessFloat));
+            return MathUtils.constrainedMap(minInt, maxInt, minFloat, maxFloat, brightnessFloat);
         }
     }
 
@@ -194,37 +185,35 @@
      * @param value Brightness value as int to store in the float setting.
      */
     private void updateBrightnessFloatFromInt(int value) {
-        final BrightnessInfo info = getBrightnessInfo();
-        if (brightnessFloatToInt(mPreferredSettingValue, info) == value) {
+        if (brightnessFloatToInt(mPreferredSettingValue) == value) {
             return;
         }
 
-        mPreferredSettingValue = brightnessIntToFloat(value, info);
+        mPreferredSettingValue = brightnessIntToFloat(value);
         final int newBrightnessAsIntBits = Float.floatToIntBits(mPreferredSettingValue);
         mHandler.removeMessages(MSG_UPDATE_BOTH);
         mHandler.obtainMessage(MSG_UPDATE_BOTH, newBrightnessAsIntBits, 0).sendToTarget();
     }
 
     /**
-     * Updates the settings from the specified {@link BrightnessInfo}. This is called whenever the
-     * float brightness changed from DisplayManager. mPreferredSettingValue holds the most recently
-     * updated brightness value as a float that we would like the display to be set to.
+     * Updates the settings based on a passed in float value. This is called whenever the float
+     * setting changes. mPreferredSettingValue holds the most recently updated brightness value
+     * as a float that we would like the display to be set to.
      *
      * We then schedule an update to both the int and float settings, but, remove all the other
      * messages to update all, to prevent us getting stuck in a loop.
      *
-     * @param brightnessInfo Current brightness information
+     * @param value Brightness setting as float to store in int setting.
      */
-    private void updateBrightnessIntFromFloat(@NonNull BrightnessInfo brightnessInfo) {
-        final float value = brightnessInfo.brightness;
+    private void updateBrightnessIntFromFloat(float value) {
         if (floatEquals(mPreferredSettingValue, value)) {
             return;
         }
 
         mPreferredSettingValue = value;
+        final int newBrightnessAsIntBits = Float.floatToIntBits(mPreferredSettingValue);
         mHandler.removeMessages(MSG_UPDATE_BOTH);
-        mHandler.obtainMessage(MSG_UPDATE_BOTH, Float.floatToIntBits(value), 0, brightnessInfo)
-                .sendToTarget();
+        mHandler.obtainMessage(MSG_UPDATE_BOTH, newBrightnessAsIntBits, 0).sendToTarget();
     }
 
 
@@ -233,24 +222,16 @@
      * mDisplayManager.setBrightness automatically checks for changes
      * Settings.System.putIntForUser needs to be checked, to prevent an extra callback to this class
      *
-     * @param brightnessInfo Brightness information, takes precedent over newBrightnessFloat
      * @param newBrightnessFloat Brightness setting as float to store in both settings
      */
-    private void updateBoth(BrightnessInfo brightnessInfo, float newBrightnessFloat) {
-        int newBrightnessInt = brightnessFloatToInt(newBrightnessFloat, brightnessInfo);
+    private void updateBoth(float newBrightnessFloat) {
+        int newBrightnessInt = brightnessFloatToInt(newBrightnessFloat);
         mDisplayManager.setBrightness(Display.DEFAULT_DISPLAY, newBrightnessFloat);
         if (getScreenBrightnessInt(mContext) != newBrightnessInt) {
             Settings.System.putIntForUser(mContext.getContentResolver(),
                     Settings.System.SCREEN_BRIGHTNESS, newBrightnessInt, UserHandle.USER_CURRENT);
         }
-    }
 
-    private BrightnessInfo getBrightnessInfo() {
-        final Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
-        if (display != null) {
-            return display.getBrightnessInfo();
-        }
-        return null;
     }
 
     /**
@@ -282,15 +263,10 @@
 
             @Override
             public void onDisplayChanged(int displayId) {
-                if (displayId != Display.DEFAULT_DISPLAY) {
-                    return;
-                }
-
-                final BrightnessInfo info = getBrightnessInfo();
-                if (info != null) {
-                    mHandler.removeMessages(MSG_UPDATE_INT);
-                    mHandler.obtainMessage(MSG_UPDATE_INT, info).sendToTarget();
-                }
+                float currentFloat = getScreenBrightnessFloat();
+                int toSend = Float.floatToIntBits(currentFloat);
+                mHandler.removeMessages(MSG_UPDATE_INT);
+                mHandler.obtainMessage(MSG_UPDATE_INT, toSend, 0).sendToTarget();
             }
         };
 
diff --git a/core/java/com/android/internal/infra/AndroidFuture.java b/core/java/com/android/internal/infra/AndroidFuture.java
index db5d066..84391c1 100644
--- a/core/java/com/android/internal/infra/AndroidFuture.java
+++ b/core/java/com/android/internal/infra/AndroidFuture.java
@@ -24,6 +24,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.RemoteException;
+import android.util.EventLog;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
@@ -601,9 +602,14 @@
         String messageWithStackTrace = message + '\n' + stackTrace;
         Throwable throwable;
         try {
-            Class<?> clazz = Class.forName(className);
-            Constructor<?> constructor = clazz.getConstructor(String.class);
-            throwable = (Throwable) constructor.newInstance(messageWithStackTrace);
+            Class<?> clazz = Class.forName(className, true, Parcelable.class.getClassLoader());
+            if (Throwable.class.isAssignableFrom(clazz)) {
+                Constructor<?> constructor = clazz.getConstructor(String.class);
+                throwable = (Throwable) constructor.newInstance(messageWithStackTrace);
+            } else {
+                android.util.EventLog.writeEvent(0x534e4554, "186530450", -1, "");
+                throwable = new RuntimeException(className + ": " + messageWithStackTrace);
+            }
         } catch (Throwable t) {
             throwable = new RuntimeException(className + ": " + messageWithStackTrace);
             throwable.addSuppressed(t);
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 8e7fae7..aa7142e 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -45,6 +45,7 @@
 import android.view.ViewRootImpl;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor.Configuration;
 import com.android.internal.jank.InteractionJankMonitor.Session;
 import com.android.internal.util.FrameworkStatsLog;
 
@@ -69,6 +70,7 @@
     static final int REASON_CANCEL_NORMAL = 16;
     static final int REASON_CANCEL_NOT_BEGUN = 17;
     static final int REASON_CANCEL_SAME_VSYNC = 18;
+    static final int REASON_CANCEL_TIMEOUT = 19;
 
     /** @hide */
     @IntDef({
@@ -97,6 +99,9 @@
     private final Handler mHandler;
     private final ChoreographerWrapper mChoreographer;
 
+    @VisibleForTesting
+    public final boolean mSurfaceOnly;
+
     private long mBeginVsyncId = INVALID_ID;
     private long mEndVsyncId = INVALID_ID;
     private boolean mMetricsFinalized;
@@ -136,71 +141,86 @@
     }
 
     public FrameTracker(@NonNull Session session, @NonNull Handler handler,
-            @NonNull ThreadedRendererWrapper renderer, @NonNull ViewRootWrapper viewRootWrapper,
+            @Nullable ThreadedRendererWrapper renderer, @Nullable ViewRootWrapper viewRootWrapper,
             @NonNull SurfaceControlWrapper surfaceControlWrapper,
             @NonNull ChoreographerWrapper choreographer,
-            @NonNull FrameMetricsWrapper metrics, int traceThresholdMissedFrames,
-            int traceThresholdFrameTimeMillis, @Nullable FrameTrackerListener listener) {
+            @Nullable FrameMetricsWrapper metrics,
+            int traceThresholdMissedFrames, int traceThresholdFrameTimeMillis,
+            @Nullable FrameTrackerListener listener, @NonNull Configuration config) {
+        mSurfaceOnly = config.isSurfaceOnly();
         mSession = session;
-        mRendererWrapper = renderer;
-        mMetricsWrapper = metrics;
-        mViewRoot = viewRootWrapper;
+        mHandler = handler;
         mChoreographer = choreographer;
         mSurfaceControlWrapper = surfaceControlWrapper;
-        mHandler = handler;
-        mObserver = new HardwareRendererObserver(
-                this, mMetricsWrapper.getTiming(), handler, false /*waitForPresentTime*/);
+
+        // HWUI instrumentation init.
+        mRendererWrapper = mSurfaceOnly ? null : renderer;
+        mMetricsWrapper = mSurfaceOnly ? null : metrics;
+        mViewRoot = mSurfaceOnly ? null : viewRootWrapper;
+        mObserver = mSurfaceOnly
+                ? null
+                : new HardwareRendererObserver(this, mMetricsWrapper.getTiming(),
+                        handler, /* waitForPresentTime= */ false);
+
         mTraceThresholdMissedFrames = traceThresholdMissedFrames;
         mTraceThresholdFrameTimeMillis = traceThresholdFrameTimeMillis;
         mListener = listener;
 
-        // If the surface isn't valid yet, wait until it's created.
-        if (viewRootWrapper.getSurfaceControl().isValid()) {
-            mSurfaceControl = viewRootWrapper.getSurfaceControl();
+        if (mSurfaceOnly) {
+            mSurfaceControl = config.getSurfaceControl();
+            mSurfaceChangedCallback = null;
+        } else {
+            // HWUI instrumentation init.
+            // If the surface isn't valid yet, wait until it's created.
+            if (mViewRoot.getSurfaceControl().isValid()) {
+                mSurfaceControl = mViewRoot.getSurfaceControl();
+                mSurfaceChangedCallback = null;
+            } else {
+                mSurfaceChangedCallback = new ViewRootImpl.SurfaceChangedCallback() {
+                    @Override
+                    public void surfaceCreated(SurfaceControl.Transaction t) {
+                        synchronized (FrameTracker.this) {
+                            if (mSurfaceControl == null) {
+                                mSurfaceControl = mViewRoot.getSurfaceControl();
+                                if (mBeginVsyncId != INVALID_ID) {
+                                    mSurfaceControlWrapper.addJankStatsListener(
+                                            FrameTracker.this, mSurfaceControl);
+                                    postTraceStartMarker();
+                                }
+                            }
+                        }
+                    }
+
+                    @Override
+                    public void surfaceReplaced(SurfaceControl.Transaction t) {
+                    }
+
+                    @Override
+                    public void surfaceDestroyed() {
+
+                        // Wait a while to give the system a chance for the remaining
+                        // frames to arrive, then force finish the session.
+                        mHandler.postDelayed(() -> {
+                            synchronized (FrameTracker.this) {
+                                if (DEBUG) {
+                                    Log.d(TAG, "surfaceDestroyed: " + mSession.getName()
+                                            + ", finalized=" + mMetricsFinalized
+                                            + ", info=" + mJankInfos.size()
+                                            + ", vsync=" + mBeginVsyncId + "-" + mEndVsyncId);
+                                }
+                                if (!mMetricsFinalized) {
+                                    end(REASON_END_SURFACE_DESTROYED);
+                                    finish(mJankInfos.size() - 1);
+                                }
+                            }
+                        }, 50);
+                    }
+                };
+                // This callback has a reference to FrameTracker,
+                // remember to remove it to avoid leakage.
+                mViewRoot.addSurfaceChangedCallback(mSurfaceChangedCallback);
+            }
         }
-        mSurfaceChangedCallback = new ViewRootImpl.SurfaceChangedCallback() {
-            @Override
-            public void surfaceCreated(SurfaceControl.Transaction t) {
-                synchronized (FrameTracker.this) {
-                    if (mSurfaceControl == null) {
-                        mSurfaceControl = viewRootWrapper.getSurfaceControl();
-                        if (mBeginVsyncId != INVALID_ID) {
-                            mSurfaceControlWrapper.addJankStatsListener(
-                                    FrameTracker.this, mSurfaceControl);
-                            postTraceStartMarker();
-                        }
-                    }
-                }
-            }
-
-            @Override
-            public void surfaceReplaced(SurfaceControl.Transaction t) {
-            }
-
-            @Override
-            public void surfaceDestroyed() {
-
-                // Wait a while to give the system a chance for the remaining frames to arrive, then
-                // force finish the session.
-                mHandler.postDelayed(() -> {
-                    synchronized (FrameTracker.this) {
-                        if (DEBUG) {
-                            Log.d(TAG, "surfaceDestroyed: " + mSession.getName()
-                                    + ", finalized=" + mMetricsFinalized
-                                    + ", info=" + mJankInfos.size()
-                                    + ", vsync=" + mBeginVsyncId + "-" + mEndVsyncId);
-                        }
-                        if (!mMetricsFinalized) {
-                            end(REASON_END_SURFACE_DESTROYED);
-                            finish(mJankInfos.size() - 1);
-                        }
-                    }
-                }, 50);
-            }
-        };
-
-        // This callback has a reference to FrameTracker, remember to remove it to avoid leakage.
-        viewRootWrapper.addSurfaceChangedCallback(mSurfaceChangedCallback);
     }
 
     /**
@@ -208,16 +228,16 @@
      */
     public synchronized void begin() {
         mBeginVsyncId = mChoreographer.getVsyncId() + 1;
-        if (mSurfaceControl != null) {
-            postTraceStartMarker();
-        }
-        mRendererWrapper.addObserver(mObserver);
         if (DEBUG) {
             Log.d(TAG, "begin: " + mSession.getName() + ", begin=" + mBeginVsyncId);
         }
         if (mSurfaceControl != null) {
+            postTraceStartMarker();
             mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl);
         }
+        if (!mSurfaceOnly) {
+            mRendererWrapper.addObserver(mObserver);
+        }
         if (mListener != null) {
             mListener.onCujEvents(mSession, ACTION_SESSION_BEGIN);
         }
@@ -273,11 +293,12 @@
      * Cancel the trace session of the CUJ.
      */
     public synchronized void cancel(@Reasons int reason) {
+        mCancelled = true;
+
         // We don't need to end the trace section if it never begun.
         if (mTracingStarted) {
             Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
         }
-        mCancelled = true;
 
         // Always remove the observers in cancel call to avoid leakage.
         removeObservers();
@@ -377,7 +398,7 @@
         for (int i = mJankInfos.size() - 1; i >= 0; i--) {
             JankInfo info = mJankInfos.valueAt(i);
             if (info.frameVsyncId >= mEndVsyncId) {
-                if (info.hwuiCallbackFired && info.surfaceControlCallbackFired) {
+                if (isLastIndexCandidate(info)) {
                     lastIndex = i;
                 }
             } else {
@@ -395,6 +416,12 @@
         finish(indexOnOrAfterEnd);
     }
 
+    private boolean isLastIndexCandidate(JankInfo info) {
+        return mSurfaceOnly
+                ? info.surfaceControlCallbackFired
+                : info.hwuiCallbackFired && info.surfaceControlCallbackFired;
+    }
+
     private void finish(int indexOnOrAfterEnd) {
 
         mMetricsFinalized = true;
@@ -410,7 +437,8 @@
 
         for (int i = 0; i <= indexOnOrAfterEnd; i++) {
             JankInfo info = mJankInfos.valueAt(i);
-            if (info.isFirstFrame) {
+            final boolean isFirstDrawn = !mSurfaceOnly && info.isFirstFrame;
+            if (isFirstDrawn) {
                 continue;
             }
             if (info.surfaceControlCallbackFired) {
@@ -435,11 +463,11 @@
                 }
                 // TODO (b/174755489): Early latch currently gets fired way too often, so we have
                 // to ignore it for now.
-                if (!info.hwuiCallbackFired) {
+                if (!mSurfaceOnly && !info.hwuiCallbackFired) {
                     Log.w(TAG, "Missing HWUI jank callback for vsyncId: " + info.frameVsyncId);
                 }
             }
-            if (info.hwuiCallbackFired) {
+            if (!mSurfaceOnly && info.hwuiCallbackFired) {
                 maxFrameTimeNanos = Math.max(info.totalDurationNanos, maxFrameTimeNanos);
                 if (!info.surfaceControlCallbackFired) {
                     Log.w(TAG, "Missing SF jank callback for vsyncId: " + info.frameVsyncId);
@@ -462,7 +490,7 @@
         // Trigger perfetto if necessary.
         boolean overMissedFramesThreshold = mTraceThresholdMissedFrames != -1
                 && missedFramesCount >= mTraceThresholdMissedFrames;
-        boolean overFrameTimeThreshold = mTraceThresholdFrameTimeMillis != -1
+        boolean overFrameTimeThreshold = !mSurfaceOnly && mTraceThresholdFrameTimeMillis != -1
                 && maxFrameTimeNanos >= mTraceThresholdFrameTimeMillis * NANOS_IN_MILLISECOND;
         if (overMissedFramesThreshold || overFrameTimeThreshold) {
             triggerPerfetto();
@@ -473,7 +501,7 @@
                     mSession.getStatsdInteractionType(),
                     totalFramesCount,
                     missedFramesCount,
-                    maxFrameTimeNanos,
+                    maxFrameTimeNanos, /* will be 0 if mSurfaceOnly == true */
                     missedSfFramesCount,
                     missedAppFramesCount);
             if (mListener != null) {
@@ -496,10 +524,13 @@
      */
     @VisibleForTesting
     public void removeObservers() {
-        mRendererWrapper.removeObserver(mObserver);
         mSurfaceControlWrapper.removeJankStatsListener(this);
-        if (mSurfaceChangedCallback != null) {
-            mViewRoot.removeSurfaceChangedCallback(mSurfaceChangedCallback);
+        if (!mSurfaceOnly) {
+            // HWUI part.
+            mRendererWrapper.removeObserver(mObserver);
+            if (mSurfaceChangedCallback != null) {
+                mViewRoot.removeSurfaceChangedCallback(mSurfaceChangedCallback);
+            }
         }
     }
 
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index aabcd7f..aae6f50 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -18,11 +18,11 @@
 
 import static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY;
 
-import static com.android.internal.jank.FrameTracker.ChoreographerWrapper;
 import static com.android.internal.jank.FrameTracker.REASON_CANCEL_NORMAL;
 import static com.android.internal.jank.FrameTracker.REASON_CANCEL_NOT_BEGUN;
+import static com.android.internal.jank.FrameTracker.REASON_CANCEL_TIMEOUT;
 import static com.android.internal.jank.FrameTracker.REASON_END_NORMAL;
-import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
+import static com.android.internal.jank.FrameTracker.REASON_END_UNKNOWN;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP;
@@ -41,6 +41,7 @@
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_TO_AOD;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_UNLOCK_ANIMATION;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PIP_TRANSITION;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_PAGE_SCROLL;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON;
@@ -58,6 +59,7 @@
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__WALLPAPER_TRANSITION;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -72,11 +74,15 @@
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.Choreographer;
+import android.view.SurfaceControl;
 import android.view.View;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.FrameTracker.ChoreographerWrapper;
 import com.android.internal.jank.FrameTracker.FrameMetricsWrapper;
 import com.android.internal.jank.FrameTracker.FrameTrackerListener;
+import com.android.internal.jank.FrameTracker.Reasons;
+import com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
 import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper;
 import com.android.internal.jank.FrameTracker.ViewRootWrapper;
 import com.android.internal.util.PerfettoTrigger;
@@ -103,7 +109,7 @@
     private static final String ACTION_PREFIX = InteractionJankMonitor.class.getCanonicalName();
 
     private static final String DEFAULT_WORKER_NAME = TAG + "-Worker";
-    private static final long DEFAULT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5L);
+    private static final long DEFAULT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(2L);
     private static final String SETTINGS_ENABLED_KEY = "enabled";
     private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
     private static final String SETTINGS_THRESHOLD_MISSED_FRAMES_KEY =
@@ -163,6 +169,8 @@
     public static final int CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE = 32;
     public static final int CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON = 33;
     public static final int CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP = 34;
+    public static final int CUJ_PIP_TRANSITION = 35;
+    public static final int CUJ_WALLPAPER_TRANSITION = 36;
 
     private static final int NO_STATSD_LOGGING = -1;
 
@@ -206,6 +214,8 @@
             UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_QS_TILE,
             UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON,
             UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PIP_TRANSITION,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__WALLPAPER_TRANSITION,
     };
 
     private static volatile InteractionJankMonitor sInstance;
@@ -213,10 +223,10 @@
     private final DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener =
             this::updateProperties;
 
-    private FrameMetricsWrapper mMetrics;
-    private SparseArray<FrameTracker> mRunningTrackers;
-    private SparseArray<Runnable> mTimeoutActions;
-    private HandlerThread mWorker;
+    private final FrameMetricsWrapper mMetrics;
+    private final SparseArray<FrameTracker> mRunningTrackers;
+    private final SparseArray<Runnable> mTimeoutActions;
+    private final HandlerThread mWorker;
 
     private boolean mEnabled = DEFAULT_ENABLED;
     private int mSamplingInterval = DEFAULT_SAMPLING_INTERVAL;
@@ -260,6 +270,8 @@
             CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE,
             CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON,
             CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
+            CUJ_PIP_TRANSITION,
+            CUJ_WALLPAPER_TRANSITION,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface CujType {
@@ -310,24 +322,31 @@
     }
 
     /**
-     * Create a {@link FrameTracker} instance.
+     * Creates a {@link FrameTracker} instance.
      *
+     * @param config the config used in instrumenting
      * @param session the session associates with this tracker
      * @return instance of the FrameTracker
      */
     @VisibleForTesting
-    public FrameTracker createFrameTracker(Configuration conf, Session session) {
-        final View v = conf.mView;
-        final Context c = v.getContext().getApplicationContext();
-        final ThreadedRendererWrapper r = new ThreadedRendererWrapper(v.getThreadedRenderer());
-        final ViewRootWrapper vr = new ViewRootWrapper(v.getViewRootImpl());
-        final SurfaceControlWrapper sc = new SurfaceControlWrapper();
-        final ChoreographerWrapper cg = new ChoreographerWrapper(Choreographer.getInstance());
+    public FrameTracker createFrameTracker(Configuration config, Session session) {
+        final View view = config.mView;
+        final ThreadedRendererWrapper threadedRenderer =
+                view == null ? null : new ThreadedRendererWrapper(view.getThreadedRenderer());
+        final ViewRootWrapper viewRoot =
+                view == null ? null : new ViewRootWrapper(view.getViewRootImpl());
+
+        final SurfaceControlWrapper surfaceControl = new SurfaceControlWrapper();
+        final ChoreographerWrapper choreographer =
+                new ChoreographerWrapper(Choreographer.getInstance());
 
         synchronized (this) {
-            FrameTrackerListener eventsListener = (s, act) -> handleCujEvents(c, act, s);
-            return new FrameTracker(session, mWorker.getThreadHandler(), r, vr, sc, cg, mMetrics,
-                    mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis, eventsListener);
+            FrameTrackerListener eventsListener =
+                    (s, act) -> handleCujEvents(config.getContext(), act, s);
+            return new FrameTracker(session, mWorker.getThreadHandler(),
+                    threadedRenderer, viewRoot, surfaceControl, choreographer, mMetrics,
+                    mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis,
+                    eventsListener, config);
         }
     }
 
@@ -376,7 +395,7 @@
     }
 
     /**
-     * Begin a trace session.
+     * Begins a trace session.
      *
      * @param v an attached view.
      * @param cujType the specific {@link InteractionJankMonitor.CujType}.
@@ -385,8 +404,7 @@
     public boolean begin(View v, @CujType int cujType) {
         try {
             return beginInternal(
-                    new Configuration.Builder(cujType)
-                            .setView(v)
+                    Configuration.Builder.withView(cujType, v)
                             .build());
         } catch (IllegalArgumentException ex) {
             Log.d(TAG, "Build configuration failed!", ex);
@@ -395,7 +413,7 @@
     }
 
     /**
-     * Begin a trace session.
+     * Begins a trace session.
      *
      * @param builder the builder of the configurations for instrumenting the CUJ.
      * @return boolean true if the tracker is started successfully, false otherwise.
@@ -431,48 +449,60 @@
             tracker.begin();
 
             // Cancel the trace if we don't get an end() call in specified duration.
-            Runnable timeoutAction = () -> cancel(cujType);
-            mTimeoutActions.put(cujType, timeoutAction);
-            mWorker.getThreadHandler().postDelayed(timeoutAction, conf.mTimeout);
+            scheduleTimeoutAction(
+                    cujType, conf.mTimeout, () -> cancel(cujType, REASON_CANCEL_TIMEOUT));
             return true;
         }
     }
 
     /**
-     * End a trace session.
+     * Schedules a timeout action.
+     * @param cuj cuj type
+     * @param timeout duration to timeout
+     * @param action action once timeout
+     */
+    @VisibleForTesting
+    public void scheduleTimeoutAction(@CujType int cuj, long timeout, Runnable action) {
+        mTimeoutActions.put(cuj, action);
+        mWorker.getThreadHandler().postDelayed(action, timeout);
+    }
+
+    /**
+     * Ends a trace session.
      *
      * @param cujType the specific {@link InteractionJankMonitor.CujType}.
      * @return boolean true if the tracker is ended successfully, false otherwise.
      */
     public boolean end(@CujType int cujType) {
-        //TODO (163505250): This should be no-op if not in droid food rom.
         synchronized (this) {
-
             // remove the timeout action first.
             removeTimeout(cujType);
             FrameTracker tracker = getTracker(cujType);
             // Skip this call since we haven't started a trace yet.
             if (tracker == null) return false;
-            tracker.end(FrameTracker.REASON_END_NORMAL);
+            tracker.end(REASON_END_NORMAL);
             removeTracker(cujType);
             return true;
         }
     }
 
     /**
-     * Cancel the trace session.
+     * Cancels the trace session.
      *
      * @return boolean true if the tracker is cancelled successfully, false otherwise.
      */
     public boolean cancel(@CujType int cujType) {
-        //TODO (163505250): This should be no-op if not in droid food rom.
+        return cancel(cujType, REASON_CANCEL_NORMAL);
+    }
+
+    boolean cancel(@CujType int cujType, @Reasons int reason) {
         synchronized (this) {
             // remove the timeout action first.
             removeTimeout(cujType);
             FrameTracker tracker = getTracker(cujType);
             // Skip this call since we haven't started a trace yet.
             if (tracker == null) return false;
-            tracker.cancel(FrameTracker.REASON_CANCEL_NORMAL);
+            tracker.cancel(reason);
             removeTracker(cujType);
             return true;
         }
@@ -509,7 +539,7 @@
     }
 
     /**
-     * Trigger the perfetto daemon to collect and upload data.
+     * Triggers the perfetto daemon to collect and upload data.
      */
     @VisibleForTesting
     public void trigger(Session session) {
@@ -608,6 +638,10 @@
                 return "SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON";
             case CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP:
                 return "STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP";
+            case CUJ_PIP_TRANSITION:
+                return "PIP_TRANSITION";
+            case CUJ_WALLPAPER_TRANSITION:
+                return "WALLPAPER_TRANSITION";
         }
         return "UNKNOWN";
     }
@@ -618,32 +652,64 @@
      */
     public static class Configuration {
         private final View mView;
+        private final Context mContext;
         private final long mTimeout;
         private final String mTag;
+        private final boolean mSurfaceOnly;
+        private final SurfaceControl mSurfaceControl;
         private final @CujType int mCujType;
 
         /**
-         * A builder for building Configuration. <br/>
+         * A builder for building Configuration. {@link #setView(View)} is essential
+         * if {@link #setSurfaceOnly(boolean)} is not set, otherwise both
+         * {@link #setSurfaceControl(SurfaceControl)} and {@link #setContext(Context)}
+         * are necessary<br/>
          * <b>It may refer to an attached view, don't use static reference for any purpose.</b>
          */
         public static class Builder {
             private View mAttrView = null;
+            private Context mAttrContext = null;
             private long mAttrTimeout = DEFAULT_TIMEOUT_MS;
             private String mAttrTag = "";
+            private boolean mAttrSurfaceOnly;
+            private SurfaceControl mAttrSurfaceControl;
             private @CujType int mAttrCujType;
 
             /**
+             * Creates a builder which instruments only surface.
              * @param cuj The enum defined in {@link InteractionJankMonitor.CujType}.
+             * @param context context
+             * @param surfaceControl surface control
+             * @return builder
              */
-            public Builder(@CujType int cuj) {
+            public static Builder withSurface(@CujType int cuj, @NonNull Context context,
+                    @NonNull SurfaceControl surfaceControl) {
+                return new Builder(cuj)
+                        .setContext(context)
+                        .setSurfaceControl(surfaceControl)
+                        .setSurfaceOnly(true);
+            }
+
+            /**
+             * Creates a builder which instruments both surface and view.
+             * @param cuj The enum defined in {@link InteractionJankMonitor.CujType}.
+             * @param view view
+             * @return builder
+             */
+            public static Builder withView(@CujType int cuj, @NonNull View view) {
+                return new Builder(cuj).setView(view);
+            }
+
+            private Builder(@CujType int cuj) {
                 mAttrCujType = cuj;
             }
 
             /**
+             * Specifies a view, must be set if {@link #setSurfaceOnly(boolean)} is set to false.
              * @param view an attached view
              * @return builder
              */
-            public Builder setView(@NonNull View view) {
+            private Builder setView(@NonNull View view) {
                 mAttrView = view;
                 return this;
             }
@@ -669,20 +735,56 @@
             }
 
             /**
-             * Build the {@link Configuration} instance
+             * Indicates if only instrument with surface,
+             * if true, must also setup with {@link #setContext(Context)}
+             * and {@link #setSurfaceControl(SurfaceControl)}.
+             * @param surfaceOnly true if only instrument with surface, false otherwise
+             * @return builder Surface only builder.
+             */
+            private Builder setSurfaceOnly(boolean surfaceOnly) {
+                mAttrSurfaceOnly = surfaceOnly;
+                return this;
+            }
+
+            /**
+             * Specifies a context, must set if {@link #setSurfaceOnly(boolean)} is set.
+             */
+            private Builder setContext(Context context) {
+                mAttrContext = context;
+                return this;
+            }
+
+            /**
+             * Specifies a surface control, must be set if {@link #setSurfaceOnly(boolean)} is set.
+             */
+            private Builder setSurfaceControl(SurfaceControl surfaceControl) {
+                mAttrSurfaceControl = surfaceControl;
+                return this;
+            }
+
+            /**
+             * Builds the {@link Configuration} instance
              * @return the instance of {@link Configuration}
              * @throws IllegalArgumentException if any invalid attribute is set
              */
             public Configuration build() throws IllegalArgumentException {
-                return new Configuration(mAttrCujType, mAttrView, mAttrTag, mAttrTimeout);
+                return new Configuration(
+                        mAttrCujType, mAttrView, mAttrTag, mAttrTimeout,
+                        mAttrSurfaceOnly, mAttrContext, mAttrSurfaceControl);
             }
         }
 
-        private Configuration(@CujType int cuj, View view, String tag, long timeout) {
+        private Configuration(@CujType int cuj, View view, String tag, long timeout,
+                boolean surfaceOnly, Context context, SurfaceControl surfaceControl) {
             mCujType = cuj;
             mTag = tag;
             mTimeout = timeout;
             mView = view;
+            mSurfaceOnly = surfaceOnly;
+            mContext = context != null
+                    ? context
+                    : (view != null ? view.getContext().getApplicationContext() : null);
+            mSurfaceControl = surfaceControl;
             validate();
         }
 
@@ -698,14 +800,47 @@
                 shouldThrow = true;
                 msg.append("Invalid timeout value; ");
             }
-            if (mView == null || !mView.isAttachedToWindow()) {
-                shouldThrow = true;
-                msg.append("Null view or view is not attached yet; ");
+            if (mSurfaceOnly) {
+                if (mContext == null) {
+                    shouldThrow = true;
+                    msg.append("Must pass in a context if only instrument surface; ");
+                }
+                if (mSurfaceControl == null || !mSurfaceControl.isValid()) {
+                    shouldThrow = true;
+                    msg.append("Must pass in a valid surface control if only instrument surface; ");
+                }
+            } else {
+                if (mView == null || !mView.isAttachedToWindow()) {
+                    shouldThrow = true;
+                    msg.append("Null view or unattached view while instrumenting view; ");
+                }
             }
             if (shouldThrow) {
                 throw new IllegalArgumentException(msg.toString());
             }
         }
+
+        /**
+         * @return true if only instrumenting surface, false otherwise
+         */
+        public boolean isSurfaceOnly() {
+            return mSurfaceOnly;
+        }
+
+        /**
+         * @return the surafce control which is instrumenting
+         */
+        public SurfaceControl getSurfaceControl() {
+            return mSurfaceControl;
+        }
+
+        View getView() {
+            return mView;
+        }
+
+        Context getContext() {
+            return mContext;
+        }
     }
 
     /**
@@ -715,8 +850,8 @@
         @CujType
         private final int mCujType;
         private final long mTimeStamp;
-        @FrameTracker.Reasons
-        private int mReason = FrameTracker.REASON_END_UNKNOWN;
+        @Reasons
+        private int mReason = REASON_END_UNKNOWN;
         private final boolean mShouldNotify;
         private final String mName;
 
@@ -756,15 +891,15 @@
             return mTimeStamp;
         }
 
-        public void setReason(@FrameTracker.Reasons int reason) {
+        public void setReason(@Reasons int reason) {
             mReason = reason;
         }
 
-        public int getReason() {
+        public @Reasons int getReason() {
             return mReason;
         }
 
-        /** Determine if should notify the receivers of cuj events */
+        /** Determines if should notify the receivers of cuj events */
         public boolean shouldNotify() {
             return mShouldNotify;
         }
diff --git a/core/java/com/android/internal/os/ProcLocksReader.java b/core/java/com/android/internal/os/ProcLocksReader.java
new file mode 100644
index 0000000..bd3115fc5
--- /dev/null
+++ b/core/java/com/android/internal/os/ProcLocksReader.java
@@ -0,0 +1,89 @@
+/*
+ * 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.internal.os;
+
+import com.android.internal.util.ProcFileReader;
+
+import libcore.io.IoUtils;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+
+/**
+ * Reads and parses {@code locks} files in the {@code proc} filesystem.
+ * A typical example of /proc/locks
+ *
+ * 1: POSIX  ADVISORY  READ  18403 fd:09:9070 1073741826 1073742335
+ * 2: POSIX  ADVISORY  WRITE 18292 fd:09:34062 0 EOF
+ * 2: -> POSIX  ADVISORY  WRITE 18291 fd:09:34062 0 EOF
+ * 2: -> POSIX  ADVISORY  WRITE 18293 fd:09:34062 0 EOF
+ * 3: POSIX  ADVISORY  READ  3888 fd:09:13992 128 128
+ * 4: POSIX  ADVISORY  READ  3888 fd:09:14230 1073741826 1073742335
+ */
+public class ProcLocksReader {
+    private final String mPath;
+
+    public ProcLocksReader() {
+        mPath = "/proc/locks";
+    }
+
+    public ProcLocksReader(String path) {
+        mPath = path;
+    }
+
+    /**
+     * Checks if a process corresponding to a specific pid owns any file locks.
+     * @param pid The process ID for which we want to know the existence of file locks.
+     * @return true If the process holds any file locks, false otherwise.
+     * @throws IOException if /proc/locks can't be accessed.
+     */
+    public boolean hasFileLocks(int pid) throws Exception {
+        ProcFileReader reader = null;
+        long last = -1;
+        long id; // ordinal position of the lock in the list
+        int owner; // the PID of the process that owns the lock
+
+        try {
+            reader = new ProcFileReader(new FileInputStream(mPath));
+
+            while (reader.hasMoreData()) {
+                id = reader.nextLong(true); // lock id
+                if (id == last) {
+                    reader.finishLine(); // blocked lock
+                    continue;
+                }
+
+                reader.nextIgnored(); // lock type: POSIX?
+                reader.nextIgnored(); // lock type: MANDATORY?
+                reader.nextIgnored(); // lock type: RW?
+
+                owner = reader.nextInt(); // pid
+                if (owner == pid) {
+                    return true;
+                }
+                reader.finishLine();
+                last = id;
+            }
+        } catch (IOException e) {
+            // TODO: let ProcFileReader log the failed line
+            throw new Exception("Exception parsing /proc/locks");
+        } finally {
+            IoUtils.closeQuietly(reader);
+        }
+        return false;
+    }
+}
diff --git a/core/java/com/android/internal/util/ContrastColorUtil.java b/core/java/com/android/internal/util/ContrastColorUtil.java
index 8b3c133..7a712e5 100644
--- a/core/java/com/android/internal/util/ContrastColorUtil.java
+++ b/core/java/com/android/internal/util/ContrastColorUtil.java
@@ -291,10 +291,10 @@
      * Finds a suitable color such that there's enough contrast.
      *
      * @param color the color to start searching from.
-     * @param other the color to ensure contrast against. Assumed to be lighter than {@param color}
-     * @param findFg if true, we assume {@param color} is a foreground, otherwise a background.
+     * @param other the color to ensure contrast against. Assumed to be lighter than {@code color}
+     * @param findFg if true, we assume {@code color} is a foreground, otherwise a background.
      * @param minRatio the minimum contrast ratio required.
-     * @return a color with the same hue as {@param color}, potentially darkened to meet the
+     * @return a color with the same hue as {@code color}, potentially darkened to meet the
      *          contrast ratio.
      */
     public static int findContrastColor(int color, int other, boolean findFg, double minRatio) {
@@ -331,7 +331,7 @@
      * @param color the color to start searching from.
      * @param backgroundColor the color to ensure contrast against.
      * @param minRatio the minimum contrast ratio required.
-     * @return the same color as {@param color} with potentially modified alpha to meet contrast
+     * @return the same color as {@code color} with potentially modified alpha to meet contrast
      */
     public static int findAlphaToMeetContrast(int color, int backgroundColor, double minRatio) {
         int fg = color;
@@ -361,10 +361,10 @@
      * Finds a suitable color such that there's enough contrast.
      *
      * @param color the color to start searching from.
-     * @param other the color to ensure contrast against. Assumed to be darker than {@param color}
-     * @param findFg if true, we assume {@param color} is a foreground, otherwise a background.
+     * @param other the color to ensure contrast against. Assumed to be darker than {@code color}
+     * @param findFg if true, we assume {@code color} is a foreground, otherwise a background.
      * @param minRatio the minimum contrast ratio required.
-     * @return a color with the same hue as {@param color}, potentially darkened to meet the
+     * @return a color with the same hue as {@code color}, potentially lightened to meet the
      *          contrast ratio.
      */
     public static int findContrastColorAgainstDark(int color, int other, boolean findFg,
@@ -393,7 +393,8 @@
                 low = l;
             }
         }
-        return findFg ? fg : bg;
+        hsl[2] = high;
+        return ColorUtilsFromCompat.HSLToColor(hsl);
     }
 
     public static int ensureTextContrastOnBlack(int color) {
@@ -452,7 +453,7 @@
     }
 
     /**
-     * Resolves {@param color} to an actual color if it is {@link Notification#COLOR_DEFAULT}
+     * Resolves {@code color} to an actual color if it is {@link Notification#COLOR_DEFAULT}
      */
     public static int resolveColor(Context context, int color, boolean defaultBackgroundIsDark) {
         if (color == Notification.COLOR_DEFAULT) {
diff --git a/core/java/com/android/internal/util/ProcFileReader.java b/core/java/com/android/internal/util/ProcFileReader.java
index ead58c7d..0dd8ad8 100644
--- a/core/java/com/android/internal/util/ProcFileReader.java
+++ b/core/java/com/android/internal/util/ProcFileReader.java
@@ -28,8 +28,8 @@
  * requires each line boundary to be explicitly acknowledged using
  * {@link #finishLine()}. Assumes {@link StandardCharsets#US_ASCII} encoding.
  * <p>
- * Currently doesn't support formats based on {@code \0}, tabs, or repeated
- * delimiters.
+ * Currently doesn't support formats based on {@code \0}, tabs.
+ * Consecutive spaces are treated as a single delimiter.
  */
 public class ProcFileReader implements Closeable {
     private final InputStream mStream;
@@ -75,6 +75,11 @@
     private void consumeBuf(int count) throws IOException {
         // TODO: consider moving to read pointer, but for now traceview says
         // these copies aren't a bottleneck.
+
+        // skip all consecutive delimiters.
+        while (count < mTail && mBuffer[count] == ' ') {
+            count++;
+        }
         System.arraycopy(mBuffer, count, mBuffer, 0, mTail - count);
         mTail -= count;
         if (mTail == 0) {
@@ -159,11 +164,18 @@
      * Parse and return next token as base-10 encoded {@code long}.
      */
     public long nextLong() throws IOException {
+        return nextLong(false);
+    }
+
+    /**
+     * Parse and return next token as base-10 encoded {@code long}.
+     */
+    public long nextLong(boolean stopAtInvalid) throws IOException {
         final int tokenIndex = nextTokenIndex();
         if (tokenIndex == -1) {
             throw new ProtocolException("Missing required long");
         } else {
-            return parseAndConsumeLong(tokenIndex);
+            return parseAndConsumeLong(tokenIndex, stopAtInvalid);
         }
     }
 
@@ -176,7 +188,7 @@
         if (tokenIndex == -1) {
             return def;
         } else {
-            return parseAndConsumeLong(tokenIndex);
+            return parseAndConsumeLong(tokenIndex, false);
         }
     }
 
@@ -186,7 +198,10 @@
         return s;
     }
 
-    private long parseAndConsumeLong(int tokenIndex) throws IOException {
+    /**
+     * If stopAtInvalid is true, don't throw IOException but return whatever parsed so far.
+     */
+    private long parseAndConsumeLong(int tokenIndex, boolean stopAtInvalid) throws IOException {
         final boolean negative = mBuffer[0] == '-';
 
         // TODO: refactor into something like IntegralToString
@@ -194,7 +209,11 @@
         for (int i = negative ? 1 : 0; i < tokenIndex; i++) {
             final int digit = mBuffer[i] - '0';
             if (digit < 0 || digit > 9) {
-                throw invalidLong(tokenIndex);
+                if (stopAtInvalid) {
+                    break;
+                } else {
+                    throw invalidLong(tokenIndex);
+                }
             }
 
             // always parse as negative number and apply sign later; this
@@ -226,6 +245,18 @@
         return (int) value;
     }
 
+    /**
+     * Bypass the next token.
+     */
+    public void nextIgnored() throws IOException {
+        final int tokenIndex = nextTokenIndex();
+        if (tokenIndex == -1) {
+            throw new ProtocolException("Missing required token");
+        } else {
+            consumeBuf(tokenIndex + 1);
+        }
+    }
+
     @Override
     public void close() throws IOException {
         mStream.close();
diff --git a/core/java/com/android/internal/widget/NotificationActionListLayout.java b/core/java/com/android/internal/widget/NotificationActionListLayout.java
index 0c2d2a9..3191e23 100644
--- a/core/java/com/android/internal/widget/NotificationActionListLayout.java
+++ b/core/java/com/android/internal/widget/NotificationActionListLayout.java
@@ -53,6 +53,8 @@
     private int mEmphasizedHeight;
     private int mRegularHeight;
     @DimenRes private int mCollapsibleIndentDimen = R.dimen.notification_actions_padding_start;
+    int mNumNotGoneChildren;
+    int mNumPriorityChildren;
 
     public NotificationActionListLayout(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
@@ -76,15 +78,14 @@
                 && ((EmphasizedNotificationButton) actionView).isPriority();
     }
 
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        final int N = getChildCount();
+    private void countAndRebuildMeasureOrder() {
+        final int numChildren = getChildCount();
         int textViews = 0;
         int otherViews = 0;
-        int notGoneChildren = 0;
-        int priorityChildren = 0;
+        mNumNotGoneChildren = 0;
+        mNumPriorityChildren = 0;
 
-        for (int i = 0; i < N; i++) {
+        for (int i = 0; i < numChildren; i++) {
             View c = getChildAt(i);
             if (c instanceof TextView) {
                 textViews++;
@@ -92,9 +93,9 @@
                 otherViews++;
             }
             if (c.getVisibility() != GONE) {
-                notGoneChildren++;
+                mNumNotGoneChildren++;
                 if (isPriority(c)) {
-                    priorityChildren++;
+                    mNumPriorityChildren++;
                 }
             }
         }
@@ -119,17 +120,20 @@
         if (needRebuild) {
             rebuildMeasureOrder(textViews, otherViews);
         }
+    }
 
+    private int measureAndGetUsedWidth(int widthMeasureSpec, int heightMeasureSpec, int innerWidth,
+            boolean collapsePriorityActions) {
+        final int numChildren = getChildCount();
         final boolean constrained =
                 MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED;
-
-        final int innerWidth = MeasureSpec.getSize(widthMeasureSpec) - mPaddingLeft - mPaddingRight;
         final int otherSize = mMeasureOrderOther.size();
         int usedWidth = 0;
 
+        int maxPriorityWidth = 0;
         int measuredChildren = 0;
         int measuredPriorityChildren = 0;
-        for (int i = 0; i < N; i++) {
+        for (int i = 0; i < numChildren; i++) {
             // Measure shortest children first. To avoid measuring twice, we approximate by looking
             // at the text length.
             final boolean isPriority;
@@ -154,12 +158,20 @@
                 // measure in the order of (approx.) size, a large view can still take more than its
                 // share if the others are small.
                 int availableWidth = innerWidth - usedWidth;
-                int unmeasuredChildren = notGoneChildren - measuredChildren;
+                int unmeasuredChildren = mNumNotGoneChildren - measuredChildren;
                 int maxWidthForChild = availableWidth / unmeasuredChildren;
-                if (isPriority) {
+                if (isPriority && collapsePriorityActions) {
+                    // Collapsing the actions to just the width required to show the icon.
+                    if (maxPriorityWidth == 0) {
+                        maxPriorityWidth = getResources().getDimensionPixelSize(
+                                R.dimen.notification_actions_collapsed_priority_width);
+                    }
+                    maxWidthForChild = maxPriorityWidth + lp.leftMargin + lp.rightMargin;
+                } else if (isPriority) {
                     // Priority children get a larger maximum share of the total space:
                     //  maximum priority share = (nPriority + 1) / (MAX + 1)
-                    int unmeasuredPriorityChildren = priorityChildren - measuredPriorityChildren;
+                    int unmeasuredPriorityChildren = mNumPriorityChildren
+                            - measuredPriorityChildren;
                     int unmeasuredOtherChildren = unmeasuredChildren - unmeasuredPriorityChildren;
                     int widthReservedForOtherChildren = innerWidth * unmeasuredOtherChildren
                             / (Notification.MAX_ACTION_BUTTONS + 1);
@@ -187,6 +199,19 @@
         } else {
             mExtraStartPadding = 0;
         }
+        return usedWidth;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        countAndRebuildMeasureOrder();
+        final int innerWidth = MeasureSpec.getSize(widthMeasureSpec) - mPaddingLeft - mPaddingRight;
+        int usedWidth = measureAndGetUsedWidth(widthMeasureSpec, heightMeasureSpec, innerWidth,
+                false /* collapsePriorityButtons */);
+        if (mNumPriorityChildren != 0 && usedWidth >= innerWidth) {
+            usedWidth = measureAndGetUsedWidth(widthMeasureSpec, heightMeasureSpec, innerWidth,
+                    true /* collapsePriorityButtons */);
+        }
 
         mTotalWidth = usedWidth + mPaddingRight + mPaddingLeft + mExtraStartPadding;
         setMeasuredDimension(resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec),
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 98fc84c..4f27d21 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -224,6 +224,7 @@
                 "fd_utils.cpp",
                 "android_hardware_input_InputWindowHandle.cpp",
                 "android_hardware_input_InputApplicationHandle.cpp",
+                "android_window_WindowInfosListener.cpp",
             ],
 
             static_libs: [
@@ -236,11 +237,14 @@
                 "libgrallocusage",
                 "libscrypt_static",
                 "libstatssocket_lazy",
+                "libskia",
             ],
 
             shared_libs: [
                 "audioclient-types-aidl-cpp",
                 "audioflinger-aidl-cpp",
+                "audiopolicy-types-aidl-cpp",
+                "spatializer-aidl-cpp",
                 "av-types-aidl-cpp",
                 "android.hardware.camera.device@3.2",
                 "libandroidicu",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 2fd1e54..c18d227 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -207,6 +207,7 @@
 extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env);
 extern int register_com_android_internal_security_VerityUtils(JNIEnv* env);
 extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
+extern int register_android_window_WindowInfosListener(JNIEnv* env);
 
 // Namespace for Android Runtime flags applied during boot time.
 static const char* RUNTIME_NATIVE_BOOT_NAMESPACE = "runtime_native_boot";
@@ -1649,6 +1650,8 @@
         REG_JNI(register_com_android_internal_os_KernelCpuUidBpfMapReader),
         REG_JNI(register_com_android_internal_os_KernelSingleProcessCpuThreadReader),
         REG_JNI(register_com_android_internal_os_KernelSingleUidTimeReader),
+
+        REG_JNI(register_android_window_WindowInfosListener),
 };
 
 /*
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 666ab95..af77cb7 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -22,6 +22,7 @@
 # WindowManager
 per-file android_graphics_BLASTBufferQueue.cpp = file:/services/core/java/com/android/server/wm/OWNERS
 per-file android_view_Surface* = file:/services/core/java/com/android/server/wm/OWNERS
+per-file android_window_WindowInfosListener.cpp = file:/services/core/java/com/android/server/wm/OWNERS
 
 # Resources
 per-file android_content_res_* = file:/core/java/android/content/res/OWNERS
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index b46b5a2..d4ae6d7 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -105,9 +105,11 @@
 }
 
 static void nativeUpdate(JNIEnv* env, jclass clazz, jlong ptr, jlong surfaceControl, jlong width,
-                         jlong height, jint format) {
+                         jlong height, jint format, jlong transactionPtr) {
     sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
-    queue->update(reinterpret_cast<SurfaceControl*>(surfaceControl), width, height, format);
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionPtr);
+    queue->update(reinterpret_cast<SurfaceControl*>(surfaceControl), width, height, format,
+                  transaction);
 }
 
 static void nativeFlushShadowQueue(JNIEnv* env, jclass clazz, jlong ptr) {
@@ -144,7 +146,7 @@
         {"nativeGetSurface", "(JZ)Landroid/view/Surface;", (void*)nativeGetSurface},
         {"nativeDestroy", "(J)V", (void*)nativeDestroy},
         {"nativeSetNextTransaction", "(JJ)V", (void*)nativeSetNextTransaction},
-        {"nativeUpdate", "(JJJJI)V", (void*)nativeUpdate},
+        {"nativeUpdate", "(JJJJIJ)V", (void*)nativeUpdate},
         {"nativeFlushShadowQueue", "(J)V", (void*)nativeFlushShadowQueue},
         {"nativeMergeWithNextTransaction", "(JJJ)V", (void*)nativeMergeWithNextTransaction},
         {"nativeSetTransactionCompleteCallback",
diff --git a/core/jni/android_hardware_input_InputApplicationHandle.cpp b/core/jni/android_hardware_input_InputApplicationHandle.cpp
index 995bfa9..24d3531 100644
--- a/core/jni/android_hardware_input_InputApplicationHandle.cpp
+++ b/core/jni/android_hardware_input_InputApplicationHandle.cpp
@@ -28,6 +28,8 @@
 namespace android {
 
 static struct {
+    jclass clazz;
+    jmethodID ctor;
     jfieldID ptr;
     jfieldID name;
     jfieldID dispatchingTimeoutMillis;
@@ -101,6 +103,15 @@
     return *handle;
 }
 
+jobject android_view_InputApplicationHandle_fromInputApplicationInfo(
+        JNIEnv* env, gui::InputApplicationInfo inputApplicationInfo) {
+    jobject binderObject = javaObjectForIBinder(env, inputApplicationInfo.token);
+    ScopedLocalRef<jstring> name(env, env->NewStringUTF(inputApplicationInfo.name.data()));
+    return env->NewObject(gInputApplicationHandleClassInfo.clazz,
+                          gInputApplicationHandleClassInfo.ctor, binderObject, name.get(),
+                          inputApplicationInfo.dispatchingTimeoutMillis);
+}
+
 // --- JNI ---
 
 static void android_view_InputApplicationHandle_nativeDispose(JNIEnv* env, jobject obj) {
@@ -131,6 +142,10 @@
         var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
         LOG_FATAL_IF(! (var), "Unable to find field " fieldName);
 
+#define GET_METHOD_ID(var, clazz, methodName, methodSignature)  \
+    var = env->GetMethodID(clazz, methodName, methodSignature); \
+    LOG_ALWAYS_FATAL_IF(!(var), "Unable to find method " methodName);
+
 int register_android_view_InputApplicationHandle(JNIEnv* env) {
     int res = jniRegisterNativeMethods(env, "android/view/InputApplicationHandle",
             gInputApplicationHandleMethods, NELEM(gInputApplicationHandleMethods));
@@ -139,6 +154,10 @@
 
     jclass clazz;
     FIND_CLASS(clazz, "android/view/InputApplicationHandle");
+    gInputApplicationHandleClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
+
+    GET_METHOD_ID(gInputApplicationHandleClassInfo.ctor, clazz, "<init>",
+                  "(Landroid/os/IBinder;Ljava/lang/String;J)V");
 
     GET_FIELD_ID(gInputApplicationHandleClassInfo.ptr, clazz,
             "ptr", "J");
diff --git a/core/jni/android_hardware_input_InputApplicationHandle.h b/core/jni/android_hardware_input_InputApplicationHandle.h
index 7eb7ac4..5d88d8e 100644
--- a/core/jni/android_hardware_input_InputApplicationHandle.h
+++ b/core/jni/android_hardware_input_InputApplicationHandle.h
@@ -42,6 +42,9 @@
 extern std::shared_ptr<InputApplicationHandle> android_view_InputApplicationHandle_getHandle(
         JNIEnv* env, jobject inputApplicationHandleObj);
 
+extern jobject android_view_InputApplicationHandle_fromInputApplicationInfo(
+        JNIEnv* env, gui::InputApplicationInfo inputApplicationInfo);
+
 } // namespace android
 
 #endif // _ANDROID_VIEW_INPUT_APPLICATION_HANDLE_H
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index afc44ff..e4ef7d3 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -26,7 +26,9 @@
 #include <ui/Region.h>
 #include <utils/threads.h>
 
+#include <android/graphics/matrix.h>
 #include <gui/WindowInfo.h>
+#include "SkRegion.h"
 #include "android_hardware_input_InputApplicationHandle.h"
 #include "android_util_Binder.h"
 #include "core_jni_helpers.h"
@@ -44,6 +46,8 @@
 };
 
 static struct {
+    jclass clazz;
+    jmethodID ctor;
     jfieldID ptr;
     jfieldID inputApplicationHandle;
     jfieldID token;
@@ -69,11 +73,18 @@
     jfieldID packageName;
     jfieldID inputFeatures;
     jfieldID displayId;
-    jfieldID portalToDisplayId;
     jfieldID replaceTouchableRegionWithCrop;
     WeakRefHandleField touchableRegionSurfaceControl;
+    jfieldID transform;
+    jfieldID windowToken;
 } gInputWindowHandleClassInfo;
 
+static struct {
+    jclass clazz;
+    jmethodID ctor;
+    jfieldID nativeRegion;
+} gRegionClassInfo;
+
 static Mutex gHandleMutex;
 
 
@@ -166,8 +177,6 @@
             env->GetIntField(obj, gInputWindowHandleClassInfo.inputFeatures));
     mInfo.displayId = env->GetIntField(obj,
             gInputWindowHandleClassInfo.displayId);
-    mInfo.portalToDisplayId = env->GetIntField(obj,
-            gInputWindowHandleClassInfo.portalToDisplayId);
 
     jobject inputApplicationHandleObj = env->GetObjectField(obj,
             gInputWindowHandleClassInfo.inputApplicationHandle);
@@ -207,6 +216,14 @@
         mInfo.touchableRegionCropHandle.clear();
     }
 
+    jobject windowTokenObj = env->GetObjectField(obj, gInputWindowHandleClassInfo.windowToken);
+    if (windowTokenObj) {
+        mInfo.windowToken = ibinderForJavaObject(env, windowTokenObj);
+        env->DeleteLocalRef(windowTokenObj);
+    } else {
+        mInfo.windowToken.clear();
+    }
+
     env->DeleteLocalRef(obj);
     return true;
 }
@@ -236,6 +253,81 @@
     return handle;
 }
 
+jobject android_view_InputWindowHandle_fromWindowInfo(JNIEnv* env, gui::WindowInfo windowInfo) {
+    ScopedLocalRef<jobject>
+            applicationHandle(env,
+                              android_view_InputApplicationHandle_fromInputApplicationInfo(
+                                      env, windowInfo.applicationInfo));
+
+    jobject inputWindowHandle =
+            env->NewObject(gInputWindowHandleClassInfo.clazz, gInputWindowHandleClassInfo.ctor,
+                           applicationHandle.get(), windowInfo.displayId);
+    env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.token,
+                        javaObjectForIBinder(env, windowInfo.token));
+    env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.name,
+                        env->NewStringUTF(windowInfo.name.data()));
+    env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.layoutParamsFlags,
+                     static_cast<uint32_t>(windowInfo.flags.get()));
+    env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.layoutParamsType,
+                     static_cast<int32_t>(windowInfo.type));
+    env->SetLongField(inputWindowHandle, gInputWindowHandleClassInfo.dispatchingTimeoutMillis,
+                      std::chrono::duration_cast<std::chrono::milliseconds>(
+                              windowInfo.dispatchingTimeout)
+                              .count());
+    env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.frameLeft,
+                     windowInfo.frameLeft);
+    env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.frameTop, windowInfo.frameTop);
+    env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.frameRight,
+                     windowInfo.frameRight);
+    env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.frameBottom,
+                     windowInfo.frameBottom);
+    env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.surfaceInset,
+                     windowInfo.surfaceInset);
+    env->SetFloatField(inputWindowHandle, gInputWindowHandleClassInfo.scaleFactor,
+                       windowInfo.globalScaleFactor);
+
+    SkRegion* region = new SkRegion();
+    for (const auto& r : windowInfo.touchableRegion) {
+        region->op({r.left, r.top, r.right, r.bottom}, SkRegion::kUnion_Op);
+    }
+    ScopedLocalRef<jobject> regionObj(env,
+                                      env->NewObject(gRegionClassInfo.clazz,
+                                                     gRegionClassInfo.ctor));
+    env->SetLongField(regionObj.get(), gRegionClassInfo.nativeRegion,
+                      reinterpret_cast<jlong>(region));
+    env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.touchableRegion,
+                        regionObj.get());
+
+    env->SetBooleanField(inputWindowHandle, gInputWindowHandleClassInfo.visible,
+                         windowInfo.visible);
+    env->SetBooleanField(inputWindowHandle, gInputWindowHandleClassInfo.focusable,
+                         windowInfo.focusable);
+    env->SetBooleanField(inputWindowHandle, gInputWindowHandleClassInfo.hasWallpaper,
+                         windowInfo.hasWallpaper);
+    env->SetBooleanField(inputWindowHandle, gInputWindowHandleClassInfo.paused, windowInfo.paused);
+    env->SetBooleanField(inputWindowHandle, gInputWindowHandleClassInfo.trustedOverlay,
+                         windowInfo.trustedOverlay);
+    env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.touchOcclusionMode,
+                     static_cast<int32_t>(windowInfo.touchOcclusionMode));
+    env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.ownerPid, windowInfo.ownerPid);
+    env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.ownerUid, windowInfo.ownerUid);
+    env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.packageName,
+                        env->NewStringUTF(windowInfo.packageName.data()));
+    env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.inputFeatures,
+                     static_cast<int32_t>(windowInfo.inputFeatures.get()));
+
+    float transformVals[9];
+    for (int i = 0; i < 9; i++) {
+        transformVals[i] = windowInfo.transform[i % 3][i / 3];
+    }
+    ScopedLocalRef<jobject> matrixObj(env, AMatrix_newInstance(env, transformVals));
+    env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.transform, matrixObj.get());
+
+    env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.windowToken,
+                        javaObjectForIBinder(env, windowInfo.windowToken));
+
+    return inputWindowHandle;
+}
 
 // --- JNI ---
 
@@ -278,6 +370,10 @@
 
     jclass clazz;
     FIND_CLASS(clazz, "android/view/InputWindowHandle");
+    gInputWindowHandleClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
+
+    GET_METHOD_ID(gInputWindowHandleClassInfo.ctor, clazz, "<init>",
+                  "(Landroid/view/InputApplicationHandle;I)V");
 
     GET_FIELD_ID(gInputWindowHandleClassInfo.ptr, clazz,
             "ptr", "J");
@@ -351,12 +447,15 @@
     GET_FIELD_ID(gInputWindowHandleClassInfo.displayId, clazz,
             "displayId", "I");
 
-    GET_FIELD_ID(gInputWindowHandleClassInfo.portalToDisplayId, clazz,
-            "portalToDisplayId", "I");
-
     GET_FIELD_ID(gInputWindowHandleClassInfo.replaceTouchableRegionWithCrop, clazz,
             "replaceTouchableRegionWithCrop", "Z");
 
+    GET_FIELD_ID(gInputWindowHandleClassInfo.transform, clazz, "transform",
+                 "Landroid/graphics/Matrix;");
+
+    GET_FIELD_ID(gInputWindowHandleClassInfo.windowToken, clazz, "windowToken",
+                 "Landroid/os/IBinder;");
+
     jclass weakRefClazz;
     FIND_CLASS(weakRefClazz, "java/lang/ref/Reference");
 
@@ -371,6 +470,11 @@
     GET_FIELD_ID(gInputWindowHandleClassInfo.touchableRegionSurfaceControl.mNativeObject,
         surfaceControlClazz, "mNativeObject", "J");
 
+    jclass regionClazz;
+    FIND_CLASS(regionClazz, "android/graphics/Region");
+    gRegionClassInfo.clazz = MakeGlobalRefOrDie(env, regionClazz);
+    GET_METHOD_ID(gRegionClassInfo.ctor, gRegionClassInfo.clazz, "<init>", "()V");
+    GET_FIELD_ID(gRegionClassInfo.nativeRegion, gRegionClassInfo.clazz, "mNativeRegion", "J");
     return 0;
 }
 
diff --git a/core/jni/android_hardware_input_InputWindowHandle.h b/core/jni/android_hardware_input_InputWindowHandle.h
index 635480fc..408e0f1 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.h
+++ b/core/jni/android_hardware_input_InputWindowHandle.h
@@ -40,6 +40,9 @@
 extern sp<NativeInputWindowHandle> android_view_InputWindowHandle_getHandle(
         JNIEnv* env, jobject inputWindowHandleObj);
 
+extern jobject android_view_InputWindowHandle_fromWindowInfo(JNIEnv* env,
+                                                             gui::WindowInfo windowInfo);
+
 } // namespace android
 
 #endif // _ANDROID_VIEW_INPUT_WINDOW_HANDLE_H
diff --git a/core/jni/android_media_AudioDeviceAttributes.cpp b/core/jni/android_media_AudioDeviceAttributes.cpp
index 2a16dce..6879a60 100644
--- a/core/jni/android_media_AudioDeviceAttributes.cpp
+++ b/core/jni/android_media_AudioDeviceAttributes.cpp
@@ -24,6 +24,11 @@
 
 static jclass gAudioDeviceAttributesClass;
 static jmethodID gAudioDeviceAttributesCstor;
+static struct {
+    jfieldID mAddress;
+    jfieldID mNativeType;
+    // other fields unused by JNI
+} gAudioDeviceAttributesFields;
 
 namespace android {
 
@@ -33,12 +38,25 @@
     jint jNativeType = (jint)devTypeAddr->mType;
     ScopedLocalRef<jstring> jAddress(env, env->NewStringUTF(devTypeAddr->getAddress()));
 
-    *jAudioDeviceAttributes = env->NewObject(gAudioDeviceAttributesClass, gAudioDeviceAttributesCstor,
-            jNativeType, jAddress.get());
+    *jAudioDeviceAttributes =
+        env->NewObject(gAudioDeviceAttributesClass, gAudioDeviceAttributesCstor,
+                       jNativeType, jAddress.get());
 
     return jStatus;
 }
 
+jint createAudioDeviceTypeAddrFromJava(JNIEnv *env, AudioDeviceTypeAddr *devTypeAddr,
+                                       const jobject jAudioDeviceAttributes) {
+    devTypeAddr->mType = (audio_devices_t)env->GetIntField(jAudioDeviceAttributes,
+                         gAudioDeviceAttributesFields.mNativeType);
+
+    jstring jAddress = (jstring)env->GetObjectField(jAudioDeviceAttributes,
+                       gAudioDeviceAttributesFields.mAddress);
+    devTypeAddr->setAddress(ScopedUtfChars(env, jAddress).c_str());
+
+    return AUDIO_JAVA_SUCCESS;
+}
+
 } // namespace android
 
 int register_android_media_AudioDeviceAttributes(JNIEnv *env) {
@@ -48,5 +66,10 @@
     gAudioDeviceAttributesCstor =
             GetMethodIDOrDie(env, audioDeviceTypeAddressClass, "<init>", "(ILjava/lang/String;)V");
 
+    gAudioDeviceAttributesFields.mNativeType =
+            GetFieldIDOrDie(env, gAudioDeviceAttributesClass, "mNativeType", "I");
+    gAudioDeviceAttributesFields.mAddress =
+            GetFieldIDOrDie(env, gAudioDeviceAttributesClass, "mAddress", "Ljava/lang/String;");
+
     return 0;
 }
diff --git a/core/jni/android_media_AudioDeviceAttributes.h b/core/jni/android_media_AudioDeviceAttributes.h
index b49d9ba..4a1f40d 100644
--- a/core/jni/android_media_AudioDeviceAttributes.h
+++ b/core/jni/android_media_AudioDeviceAttributes.h
@@ -28,6 +28,9 @@
 
 extern jint createAudioDeviceAttributesFromNative(JNIEnv *env, jobject *jAudioDeviceAttributes,
                                         const AudioDeviceTypeAddr *devTypeAddr);
+
+extern jint createAudioDeviceTypeAddrFromJava(JNIEnv *env, AudioDeviceTypeAddr *devTypeAddr,
+                                        const jobject jAudioDeviceAttributes);
 } // namespace android
 
 #endif
\ No newline at end of file
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 29c03b3..509b7ad 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -27,6 +27,8 @@
 #include "core_jni_helpers.h"
 
 #include <android/media/AudioVibratorInfo.h>
+#include <android/media/INativeSpatializerCallback.h>
+#include <android/media/ISpatializer.h>
 #include <audiomanager/AudioManager.h>
 #include <media/AudioPolicy.h>
 #include <media/AudioSystem.h>
@@ -2023,6 +2025,18 @@
     AudioSystem::setRoutingCallback(android_media_AudioSystem_routing_callback);
 }
 
+void javaAudioFormatToNativeAudioConfig(JNIEnv *env, audio_config_t *nConfig,
+                                       const jobject jFormat, bool isInput) {
+    *nConfig = AUDIO_CONFIG_INITIALIZER;
+    nConfig->format = audioFormatToNative(env->GetIntField(jFormat, gAudioFormatFields.mEncoding));
+    nConfig->sample_rate = env->GetIntField(jFormat, gAudioFormatFields.mSampleRate);
+    jint jChannelMask = env->GetIntField(jFormat, gAudioFormatFields.mChannelMask);
+    if (isInput) {
+        nConfig->channel_mask = inChannelMaskToNative(jChannelMask);
+    } else {
+        nConfig->channel_mask = outChannelMaskToNative(jChannelMask);
+    }
+}
 
 static jint convertAudioMixToNative(JNIEnv *env,
                                     AudioMix *nAudioMix,
@@ -2043,13 +2057,7 @@
     nAudioMix->mCbFlags = env->GetIntField(jAudioMix, gAudioMixFields.mCallbackFlags);
 
     jobject jFormat = env->GetObjectField(jAudioMix, gAudioMixFields.mFormat);
-    nAudioMix->mFormat = AUDIO_CONFIG_INITIALIZER;
-    nAudioMix->mFormat.sample_rate = env->GetIntField(jFormat,
-                                                     gAudioFormatFields.mSampleRate);
-    nAudioMix->mFormat.channel_mask = outChannelMaskToNative(env->GetIntField(jFormat,
-                                                     gAudioFormatFields.mChannelMask));
-    nAudioMix->mFormat.format = audioFormatToNative(env->GetIntField(jFormat,
-                                                     gAudioFormatFields.mEncoding));
+    javaAudioFormatToNativeAudioConfig(env, &nAudioMix->mFormat, jFormat, false /*isInput*/);
     env->DeleteLocalRef(jFormat);
 
     jobject jRule = env->GetObjectField(jAudioMix, gAudioMixFields.mRule);
@@ -2430,6 +2438,12 @@
     return (jint)nativeToJavaStatus(status);
 }
 
+static jint android_media_AudioSystem_setHotwordDetectionServiceUid(JNIEnv *env, jobject thiz,
+                                                                    jint uid) {
+    status_t status = AudioSystem::setHotwordDetectionServiceUid(uid);
+    return (jint)nativeToJavaStatus(status);
+}
+
 static jint
 android_media_AudioSystem_setA11yServicesUids(JNIEnv *env, jobject thiz, jintArray uids) {
     std::vector<uid_t> nativeUidsVector;
@@ -2706,6 +2720,58 @@
     return (jint)check_AudioSystem_Command(AudioSystem::setVibratorInfos(vibratorInfos));
 }
 
+static jobject android_media_AudioSystem_getSpatializer(JNIEnv *env, jobject thiz,
+                                                       jobject jISpatializerCallback) {
+    sp<media::INativeSpatializerCallback> nISpatializerCallback
+            = interface_cast<media::INativeSpatializerCallback>(
+                    ibinderForJavaObject(env, jISpatializerCallback));
+    sp<media::ISpatializer> nSpatializer;
+    status_t status = AudioSystem::getSpatializer(nISpatializerCallback,
+                                        &nSpatializer);
+    if (status != NO_ERROR) {
+        return nullptr;
+    }
+    return javaObjectForIBinder(env, IInterface::asBinder(nSpatializer));
+}
+
+static jboolean android_media_AudioSystem_canBeSpatialized(JNIEnv *env, jobject thiz,
+                                                       jobject jaa, jobject jFormat,
+                                                       jobjectArray jDeviceArray) {
+    JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique();
+    jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get());
+    if (jStatus != (jint)AUDIO_JAVA_SUCCESS) {
+       return false;
+    }
+
+    AudioDeviceTypeAddrVector nDevices;
+
+    const size_t numDevices = env->GetArrayLength(jDeviceArray);
+    for (size_t i = 0;  i < numDevices; ++i) {
+        AudioDeviceTypeAddr device;
+        jobject jDevice  = env->GetObjectArrayElement(jDeviceArray, i);
+        if (jDevice == nullptr) {
+            return false;
+        }
+        jStatus = createAudioDeviceTypeAddrFromJava(env, &device, jDevice);
+        if (jStatus != (jint)AUDIO_JAVA_SUCCESS) {
+            return false;
+        }
+        nDevices.push_back(device);
+    }
+
+    audio_config_t nConfig;
+    javaAudioFormatToNativeAudioConfig(env, &nConfig, jFormat, false /*isInput*/);
+
+    bool canBeSpatialized;
+    status_t status =
+            AudioSystem::canBeSpatialized(paa.get(), &nConfig, nDevices, &canBeSpatialized);
+    if (status != NO_ERROR) {
+        ALOGW("%s native returned error %d", __func__, status);
+        return false;
+    }
+    return canBeSpatialized;
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gMethods[] =
@@ -2802,6 +2868,8 @@
          {"setSurroundFormatEnabled", "(IZ)I",
           (void *)android_media_AudioSystem_setSurroundFormatEnabled},
          {"setAssistantUid", "(I)I", (void *)android_media_AudioSystem_setAssistantUid},
+         {"setHotwordDetectionServiceUid", "(I)I",
+          (void *)android_media_AudioSystem_setHotwordDetectionServiceUid},
          {"setA11yServicesUids", "([I)I", (void *)android_media_AudioSystem_setA11yServicesUids},
          {"isHapticPlaybackSupported", "()Z",
           (void *)android_media_AudioSystem_isHapticPlaybackSupported},
@@ -2840,7 +2908,15 @@
           (void *)android_media_AudioSystem_removeUserIdDeviceAffinities},
          {"setCurrentImeUid", "(I)I", (void *)android_media_AudioSystem_setCurrentImeUid},
          {"setVibratorInfos", "(Ljava/util/List;)I",
-          (void *)android_media_AudioSystem_setVibratorInfos}};
+          (void *)android_media_AudioSystem_setVibratorInfos},
+         {"nativeGetSpatializer",
+          "(Landroid/media/INativeSpatializerCallback;)Landroid/os/IBinder;",
+          (void *)android_media_AudioSystem_getSpatializer},
+         {"canBeSpatialized",
+          "(Landroid/media/AudioAttributes;Landroid/media/AudioFormat;"
+          "[Landroid/media/AudioDeviceAttributes;)Z",
+          (void *)android_media_AudioSystem_canBeSpatialized}};
+
 
 static const JNINativeMethod gEventHandlerMethods[] = {
     {"native_setup",
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index b40491a..f44e829 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -50,8 +50,7 @@
 }
 
 void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appName,
-                         jstring devOptIn, jobjectArray featuresObj, jobject rulesFd,
-                         jlong rulesOffset, jlong rulesLength) {
+                         jstring devOptIn, jobjectArray featuresObj) {
     ScopedUtfChars pathChars(env, path);
     ScopedUtfChars appNameChars(env, appName);
     ScopedUtfChars devOptInChars(env, devOptIn);
@@ -74,11 +73,8 @@
         }
     }
 
-    int rulesFd_native = jniGetFDFromFileDescriptor(env, rulesFd);
-
     android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), appNameChars.c_str(),
-                                                     devOptInChars.c_str(), features,
-                                                     rulesFd_native, rulesOffset, rulesLength);
+                                                     devOptInChars.c_str(), features);
 }
 
 bool shouldUseAngle_native(JNIEnv* env, jobject clazz, jstring appName) {
@@ -124,8 +120,7 @@
         {"setInjectLayersPrSetDumpable", "()Z",
          reinterpret_cast<void*>(setInjectLayersPrSetDumpable_native)},
         {"setAngleInfo",
-         "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/io/"
-         "FileDescriptor;JJ)V",
+         "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V",
          reinterpret_cast<void*>(setAngleInfo_native)},
         {"getShouldUseAngle", "(Ljava/lang/String;)Z",
          reinterpret_cast<void*>(shouldUseAngle_native)},
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index e93b00d..86d7810 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -92,6 +92,7 @@
   jfieldID mSmallestScreenWidthDpOffset;
   jfieldID mScreenWidthDpOffset;
   jfieldID mScreenHeightDpOffset;
+  jfieldID mScreenLayoutOffset;
 } gConfigurationOffsets;
 
 static struct arraymap_offsets_t {
@@ -1019,6 +1020,7 @@
                    config.smallestScreenWidthDp);
   env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp);
   env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp);
+  env->SetIntField(result, gConfigurationOffsets.mScreenLayoutOffset, config.screenLayout);
   return result;
 }
 
@@ -1553,6 +1555,8 @@
       GetFieldIDOrDie(env, configurationClass, "screenWidthDp", "I");
   gConfigurationOffsets.mScreenHeightDpOffset =
       GetFieldIDOrDie(env, configurationClass, "screenHeightDp", "I");
+  gConfigurationOffsets.mScreenLayoutOffset =
+          GetFieldIDOrDie(env, configurationClass, "screenLayout", "I");
 
   jclass arrayMapClass = FindClassOrDie(env, "android/util/ArrayMap");
   gArrayMapOffsets.classObject = MakeGlobalRefOrDie(env, arrayMapClass);
diff --git a/core/jni/android_window_WindowInfosListener.cpp b/core/jni/android_window_WindowInfosListener.cpp
new file mode 100644
index 0000000..ab88b53
--- /dev/null
+++ b/core/jni/android_window_WindowInfosListener.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "WindowInfosListener"
+
+#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/Log.h>
+#include <gui/SurfaceComposerClient.h>
+#include <nativehelper/JNIHelp.h>
+#include <utils/Log.h>
+
+#include "android_hardware_input_InputWindowHandle.h"
+#include "core_jni_helpers.h"
+
+namespace android {
+
+using gui::WindowInfo;
+
+namespace {
+
+static struct {
+    jclass clazz;
+    jmethodID onWindowInfosChanged;
+} gListenerClassInfo;
+
+static jclass gInputWindowHandleClass;
+
+struct WindowInfosListener : public gui::WindowInfosListener {
+    WindowInfosListener(JNIEnv* env, jobject listener)
+          : mListener(env->NewWeakGlobalRef(listener)) {}
+
+    void onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos) override {
+        JNIEnv* env = AndroidRuntime::getJNIEnv();
+        LOG_ALWAYS_FATAL_IF(env == nullptr, "Unable to retrieve JNIEnv in onWindowInfoChanged.");
+
+        jobject listener = env->NewGlobalRef(mListener);
+        if (listener == nullptr) {
+            // Weak reference went out of scope
+            return;
+        }
+
+        jobjectArray jWindowHandlesArray =
+                env->NewObjectArray(windowInfos.size(), gInputWindowHandleClass, nullptr);
+        for (int i = 0; i < windowInfos.size(); i++) {
+            ScopedLocalRef<jobject>
+                    jWindowHandle(env,
+                                  android_view_InputWindowHandle_fromWindowInfo(env,
+                                                                                windowInfos[i]));
+            env->SetObjectArrayElement(jWindowHandlesArray, i, jWindowHandle.get());
+        }
+
+        env->CallVoidMethod(listener, gListenerClassInfo.onWindowInfosChanged, jWindowHandlesArray);
+        env->DeleteGlobalRef(listener);
+
+        if (env->ExceptionCheck()) {
+            ALOGE("WindowInfosListener.onWindowInfosChanged() failed.");
+            LOGE_EX(env);
+            env->ExceptionClear();
+        }
+    }
+
+    ~WindowInfosListener() override {
+        JNIEnv* env = AndroidRuntime::getJNIEnv();
+        env->DeleteWeakGlobalRef(mListener);
+    }
+
+private:
+    jweak mListener;
+};
+
+jlong nativeCreate(JNIEnv* env, jclass clazz, jobject obj) {
+    WindowInfosListener* listener = new WindowInfosListener(env, obj);
+    listener->incStrong((void*)nativeCreate);
+    return reinterpret_cast<jlong>(listener);
+}
+
+void destroyNativeService(void* ptr) {
+    WindowInfosListener* listener = reinterpret_cast<WindowInfosListener*>(ptr);
+    listener->decStrong((void*)nativeCreate);
+}
+
+void nativeRegister(JNIEnv* env, jclass clazz, jlong ptr) {
+    sp<WindowInfosListener> listener = reinterpret_cast<WindowInfosListener*>(ptr);
+    SurfaceComposerClient::getDefault()->addWindowInfosListener(listener);
+}
+
+void nativeUnregister(JNIEnv* env, jclass clazz, jlong ptr) {
+    sp<WindowInfosListener> listener = reinterpret_cast<WindowInfosListener*>(ptr);
+    SurfaceComposerClient::getDefault()->removeWindowInfosListener(listener);
+}
+
+static jlong nativeGetFinalizer(JNIEnv* /* env */, jclass /* clazz */) {
+    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyNativeService));
+}
+
+const JNINativeMethod gMethods[] = {
+        /* name, signature, funcPtr */
+        {"nativeCreate", "(Landroid/window/WindowInfosListener;)J", (void*)nativeCreate},
+        {"nativeRegister", "(J)V", (void*)nativeRegister},
+        {"nativeUnregister", "(J)V", (void*)nativeUnregister},
+        {"nativeGetFinalizer", "()J", (void*)nativeGetFinalizer}};
+
+} // namespace
+
+int register_android_window_WindowInfosListener(JNIEnv* env) {
+    int res = jniRegisterNativeMethods(env, "android/window/WindowInfosListener", gMethods,
+                                       NELEM(gMethods));
+    LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
+
+    jclass clazz = env->FindClass("android/window/WindowInfosListener");
+    gListenerClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
+    gListenerClassInfo.onWindowInfosChanged =
+            env->GetMethodID(gListenerClassInfo.clazz, "onWindowInfosChanged",
+                             "([Landroid/view/InputWindowHandle;)V");
+
+    clazz = env->FindClass("android/view/InputWindowHandle");
+    gInputWindowHandleClass = MakeGlobalRefOrDie(env, clazz);
+    return 0;
+}
+
+} // namespace android
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index c3d1596..6bc00e2 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -771,6 +771,8 @@
     optional SettingProto power_manager_constants = 93;
     reserved 94; // Used to be priv_app_oob_enabled
 
+    optional SettingProto power_button_long_press_duration_ms = 154 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
     message PrepaidSetup {
         option (android.msg_privacy).dest = DEST_EXPLICIT;
 
@@ -1063,5 +1065,5 @@
 
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 154;
+    // Next tag = 155;
 }
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 1bba12f..ba4a5b0 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -79,7 +79,7 @@
         optional SettingProto accessibility_magnification_mode = 34 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto button_targets = 35 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto accessibility_magnification_capability = 36 [ (android.privacy).dest = DEST_AUTOMATIC ];
-        // Settings for accessibility button mode (navigation bar or floating action menu).
+        // Settings for accessibility button related config
         optional SettingProto accessibility_button_mode = 37 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto accessibility_floating_menu_size = 38 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto accessibility_floating_menu_icon_type = 39 [ (android.privacy).dest = DEST_AUTOMATIC ];
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4dec0ff..1704452 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -550,6 +550,8 @@
     <protected-broadcast android:name="com.android.server.telecom.intent.action.CALLS_ADD_ENTRY" />
     <protected-broadcast android:name="com.android.settings.location.MODE_CHANGING" />
     <protected-broadcast android:name="com.android.settings.bluetooth.ACTION_DISMISS_PAIRING" />
+    <protected-broadcast android:name="com.android.settings.network.DELETE_SUBSCRIPTION" />
+    <protected-broadcast android:name="com.android.settings.network.SWITCH_TO_SUBSCRIPTION" />
     <protected-broadcast android:name="com.android.settings.wifi.action.NETWORK_REQUEST" />
 
     <protected-broadcast android:name="NotificationManagerService.TIMEOUT" />
@@ -616,7 +618,11 @@
 
     <protected-broadcast android:name="com.android.server.fingerprint.ACTION_LOCKOUT_RESET" />
     <protected-broadcast android:name="android.net.wifi.PASSPOINT_ICON_RECEIVED" />
+
     <protected-broadcast android:name="com.android.server.notification.CountdownConditionProvider" />
+    <protected-broadcast android:name="android.server.notification.action.ENABLE_NAS" />
+    <protected-broadcast android:name="android.server.notification.action.DISABLE_NAS" />
+    <protected-broadcast android:name="android.server.notification.action.LEARNMORE_NAS" />
 
     <protected-broadcast android:name="com.android.internal.location.ALARM_WAKEUP" />
     <protected-broadcast android:name="com.android.internal.location.ALARM_TIMEOUT" />
@@ -3462,6 +3468,13 @@
     <permission android:name="android.permission.TRIGGER_SHELL_BUGREPORT"
         android:protectionLevel="signature" />
 
+    <!-- Allows an application to trigger profcollect report upload via shell.
+        <p>Not for use by third-party applications.
+        @hide
+    -->
+    <permission android:name="android.permission.TRIGGER_SHELL_PROFCOLLECT_UPLOAD"
+        android:protectionLevel="signature" />
+
     <!-- Allows an application to be the status bar.  Currently used only by SystemUI.apk
         @hide
         @SystemApi -->
@@ -3503,7 +3516,7 @@
          use by third party apps.
          @hide -->
     <permission android:name="android.permission.UPDATE_APP_OPS_STATS"
-        android:protectionLevel="signature|privileged|installer" />
+        android:protectionLevel="signature|privileged|installer|role" />
 
     <!-- @SystemApi Allows an application to update the user app op restrictions.
          Not for use by third party apps.
diff --git a/core/res/res/drawable/btn_notification_emphasized.xml b/core/res/res/drawable/btn_notification_emphasized.xml
index 29c51f2a..7c09fb8 100644
--- a/core/res/res/drawable/btn_notification_emphasized.xml
+++ b/core/res/res/drawable/btn_notification_emphasized.xml
@@ -24,9 +24,9 @@
             android:insetBottom="@dimen/button_inset_vertical_material">
             <shape android:shape="rectangle">
                 <corners android:radius="@dimen/notification_action_button_radius" />
-                <padding android:left="12dp"
+                <padding android:left="16dp"
                          android:top="@dimen/button_padding_vertical_material"
-                         android:right="12dp"
+                         android:right="16dp"
                          android:bottom="@dimen/button_padding_vertical_material" />
                 <solid android:color="@color/white" />
             </shape>
diff --git a/core/res/res/layout/splash_screen_view.xml b/core/res/res/layout/splash_screen_view.xml
index 0b7b49c..2b9f952 100644
--- a/core/res/res/layout/splash_screen_view.xml
+++ b/core/res/res/layout/splash_screen_view.xml
@@ -21,7 +21,7 @@
     android:padding="0dp"
     android:orientation="vertical">
 
-    <View android:id="@+id/splashscreen_icon_view"
+    <ImageView android:id="@+id/splashscreen_icon_view"
           android:layout_height="wrap_content"
           android:layout_width="wrap_content"
           android:layout_gravity="center"
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index cf2f677..14221dc 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -341,7 +341,7 @@
     <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"استرداد محتوى النافذة"</string>
     <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"فحص محتوى نافذة يتم التفاعل معها"</string>
     <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"تفعيل الاستكشاف باللمس"</string>
-    <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"سيتم قول العناصر التي تم النقر عليها بصوت عال ويمكن استكشاف الشاشة باستخدام الإيماءات."</string>
+    <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"سيتم قول العناصر التي تم النقر عليها بصوت عالٍ ويمكن استكشاف الشاشة باستخدام الإيماءات."</string>
     <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"ملاحظة النص الذي تكتبه"</string>
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"يتضمن بيانات شخصية مثل أرقام بطاقات الائتمان وكلمات المرور."</string>
     <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"التحكم في تكبير الشاشة"</string>
@@ -2079,7 +2079,7 @@
     <string name="call_notification_hang_up_action" msgid="9130720590159188131">"قطع الاتصال"</string>
     <string name="call_notification_incoming_text" msgid="6143109825406638201">"مكالمة واردة"</string>
     <string name="call_notification_ongoing_text" msgid="3880832933933020875">"مكالمة جارية"</string>
-    <string name="call_notification_screening_text" msgid="8396931408268940208">"رصد مكالمة واردة"</string>
+    <string name="call_notification_screening_text" msgid="8396931408268940208">"يتم فحص المكالمة الواردة"</string>
     <plurals name="selected_count" formatted="false" msgid="3946212171128200491">
       <item quantity="zero">تم اختيار <xliff:g id="COUNT_1">%1$d</xliff:g> عنصر</item>
       <item quantity="two">تم اختيار عنصرين (<xliff:g id="COUNT_1">%1$d</xliff:g>)</item>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 61fb471..a8ae213 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1369,9 +1369,9 @@
     <string name="sms_control_message" msgid="6574313876316388239">"Aplikace &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;odesílá velký počet SMS zpráv. Chcete aplikaci povolit, aby zprávy odesílala i nadále?"</string>
     <string name="sms_control_yes" msgid="4858845109269524622">"Povolit"</string>
     <string name="sms_control_no" msgid="4845717880040355570">"Odmítnout"</string>
-    <string name="sms_short_code_confirm_message" msgid="1385416688897538724">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; chce odeslat zprávu na adresu &lt;b&gt;<xliff:g id="DEST_ADDRESS">%2$s</xliff:g>&lt;/b&gt;."</string>
+    <string name="sms_short_code_confirm_message" msgid="1385416688897538724">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; chce odeslat zprávu na číslo &lt;b&gt;<xliff:g id="DEST_ADDRESS">%2$s</xliff:g>&lt;/b&gt;."</string>
     <string name="sms_short_code_details" msgid="2723725738333388351">"Tato akce "<b>"může vést k naúčtování poplatků"</b>" na váš účet u mobilního operátora."</string>
-    <string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"Tato akce povede k naúčtování poplatku na váš účet u mobilního operátora."</b></string>
+    <string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"Tato akce může vést k naúčtování ceny služby třetí strany na vrub vašeho účtu u mobilního operátora."</b></string>
     <string name="sms_short_code_confirm_allow" msgid="920477594325526691">"Odeslat"</string>
     <string name="sms_short_code_confirm_deny" msgid="1356917469323768230">"Zrušit"</string>
     <string name="sms_short_code_remember_choice" msgid="1374526438647744862">"Zapamatovat moji volbu"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 542387f..865f026 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -984,7 +984,7 @@
     <string name="js_dialog_before_unload" msgid="7213364985774778744">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nMöchtest du diese Seite wirklich verlassen?"</string>
     <string name="save_password_label" msgid="9161712335355510035">"Bestätigen"</string>
     <string name="double_tap_toast" msgid="7065519579174882778">"Tipp: Zum Vergrößern und Verkleinern doppeltippen"</string>
-    <string name="autofill_this_form" msgid="3187132440451621492">"AutoFill"</string>
+    <string name="autofill_this_form" msgid="3187132440451621492">"Automatisches Ausfüllen"</string>
     <string name="setup_autofill" msgid="5431369130866618567">"Autom.Ausfüll.konf."</string>
     <string name="autofill_window_title" msgid="4379134104008111961">"Mit <xliff:g id="SERVICENAME">%1$s</xliff:g> automatisch ausfüllen"</string>
     <string name="autofill_address_name_separator" msgid="8190155636149596125">" "</string>
@@ -1937,7 +1937,7 @@
     <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"Phishing-Warnung"</string>
     <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Arbeitsprofil"</string>
     <string name="notification_alerted_content_description" msgid="6139691253611265992">"Gewarnt"</string>
-    <string name="notification_verified_content_description" msgid="6401483602782359391">"Bestätigt"</string>
+    <string name="notification_verified_content_description" msgid="6401483602782359391">"Verifiziert"</string>
     <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Maximieren"</string>
     <string name="expand_button_content_description_expanded" msgid="7484217944948667489">"Minimieren"</string>
     <string name="expand_action_accessibility" msgid="1947657036871746627">"Maximierung ein-/auschalten"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 9a07025..9a29add 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -271,7 +271,7 @@
     <string name="global_action_settings" msgid="4671878836947494217">"Ajustes"</string>
     <string name="global_action_assist" msgid="2517047220311505805">"Asistencia"</string>
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Asistente voz"</string>
-    <string name="global_action_lockdown" msgid="2475471405907902963">"Bloqueo seguro"</string>
+    <string name="global_action_lockdown" msgid="2475471405907902963">"Bloqueo de seguridad"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"&gt; 999"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Notificación nueva"</string>
     <string name="notification_channel_virtual_keyboard" msgid="6465975799223304567">"Teclado virtual"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index f388427..fcca1b39 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1949,7 +1949,7 @@
     <string name="maximize_button_text" msgid="4258922519914732645">"بزرگ کردن"</string>
     <string name="close_button_text" msgid="10603510034455258">"بستن"</string>
     <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>:‏ <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
-    <string name="call_notification_answer_action" msgid="5999246836247132937">"پاسخ"</string>
+    <string name="call_notification_answer_action" msgid="5999246836247132937">"پاسخ دادن"</string>
     <string name="call_notification_answer_video_action" msgid="2086030940195382249">"ویدیو"</string>
     <string name="call_notification_decline_action" msgid="3700345945214000726">"رد کردن"</string>
     <string name="call_notification_hang_up_action" msgid="9130720590159188131">"قطع تماس"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 17382c4..8ae4bc7 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -326,12 +326,12 @@
     <string name="permgroupdesc_phone" msgid="270048070781478204">"melakukan dan mengelola panggilan telepon"</string>
     <string name="permgrouplab_sensors" msgid="9134046949784064495">"Sensor tubuh"</string>
     <string name="permgroupdesc_sensors" msgid="2610631290633747752">"mengakses data sensor tentang tanda-tanda vital"</string>
-    <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Mengambil konten jendela"</string>
-    <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Memeriksa konten jendela tempat Anda berinteraksi."</string>
+    <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Membaca konten di jendela"</string>
+    <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Memeriksa konten di jendela yang sedang Anda buka."</string>
     <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Mengaktifkan Jelajahi dengan Sentuhan"</string>
     <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Item yang diketuk akan diucapkan dengan jelas dan layar dapat dijelajahi menggunakan gestur."</string>
     <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Mengamati teks yang Anda ketik"</string>
-    <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Meliputi data pribadi seperti nomor kartu kredit dan sandi."</string>
+    <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Termasuk data pribadi, seperti nomor kartu kredit dan sandi."</string>
     <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"Mengontrol perbesaran layar"</string>
     <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Mengontrol tingkat zoom dan pemosisian layar."</string>
     <string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Melakukan isyarat"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 6919a3d..316514e 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -327,13 +327,13 @@
     <string name="permgrouplab_sensors" msgid="9134046949784064495">"Sensori del corpo"</string>
     <string name="permgroupdesc_sensors" msgid="2610631290633747752">"accedere ai dati dei sensori relativi ai tuoi parametri vitali"</string>
     <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Recuperare contenuti della finestra"</string>
-    <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Esaminare i contenuti di una finestra con cui interagisci."</string>
+    <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Esamina i contenuti di una finestra con cui interagisci."</string>
     <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Attivare Esplora al tocco"</string>
-    <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Gli elementi toccati verranno pronunciati ad alta voce e sarà possibile esplorare lo schermo utilizzando i gesti."</string>
+    <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Gli elementi toccati verranno pronunciati ad alta voce e sarà possibile esplorare lo schermo con i gesti."</string>
     <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Osservare il testo digitato"</string>
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Sono inclusi dati personali come numeri di carte di credito e password."</string>
     <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"Controllare l\'ingrandimento del display"</string>
-    <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Controlla il livello di zoom e la posizione del display."</string>
+    <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Controlla la posizione e il livello di zoom del display."</string>
     <string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Eseguire gesti"</string>
     <string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Consente di toccare, far scorrere, pizzicare ed eseguire altri gesti."</string>
     <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Gesti con sensore di impronte"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 8765659..789e279 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -57,7 +57,7 @@
     <string name="imei" msgid="2157082351232630390">"IMEI"</string>
     <string name="meid" msgid="3291227361605924674">"MEID"</string>
     <string name="ClipMmi" msgid="4110549342447630629">"შემომავალი ზარის აბონენტის ID"</string>
-    <string name="ClirMmi" msgid="6752346475055446417">"გამავალი აბონენტის ID-ის დამალვა"</string>
+    <string name="ClirMmi" msgid="6752346475055446417">"გამავალი აბონენტის ID-ს დამალვა"</string>
     <string name="ColpMmi" msgid="4736462893284419302">"დაუკავშირდა Line ID-ს"</string>
     <string name="ColrMmi" msgid="5889782479745764278">"დაუკავშირდა Line ID Restriction-ს"</string>
     <string name="CfMmi" msgid="8390012691099787178">"ზარის გადამისამართება"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 28522d7..1747bdb 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -471,7 +471,7 @@
     <string name="permdesc_readPhoneState" msgid="7229063553502788058">"डिव्हाइस च्या फोन वैशिष्ट्यांवर अ‍ॅक्सेस करण्यास ॲपला अनुमती देते. ही परवानगी कॉल ॲक्टिव्हेट असला किंवा नसला तरीही, फोन नंबर आणि डिव्हाइस आयडी आणि कॉलद्वारे कनेक्ट केलेला रिमोट नंबर निर्धारित करण्यासाठी ॲपला अनुमती देते."</string>
     <string name="permlab_manageOwnCalls" msgid="9033349060307561370">"प्रणालीच्या माध्यमातून कॉल रूट करा"</string>
     <string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"कॉल करण्याचा अनुभव सुधारण्यासाठी ॲपला त्याचे कॉल प्रणालीच्या माध्यमातून रूट करू देते."</string>
-    <string name="permlab_callCompanionApp" msgid="3654373653014126884">"सिस्टम वापरून कॉल पाहा आणि नियंत्रण ठेवा."</string>
+    <string name="permlab_callCompanionApp" msgid="3654373653014126884">"सिस्टम वापरून कॉल पहा आणि नियंत्रण ठेवा."</string>
     <string name="permdesc_callCompanionApp" msgid="8474168926184156261">"डिव्हाइसवर येणार कॉल पाहण्यासाठी आणि नियंत्रित करण्यासाठी ॲपला अनुमती देते. यामध्ये कॉल करण्यासाठी कॉलचा नंबर आणि कॉलची स्थिती यासारख्या माहितीचा समावेश असतो."</string>
     <string name="permlab_exemptFromAudioRecordRestrictions" msgid="1164725468350759486">"ऑडिओ रेकॉर्ड प्रतिबंधांपासून मुक्त"</string>
     <string name="permdesc_exemptFromAudioRecordRestrictions" msgid="2425117015896871976">"ऑडिओ रेकॉर्ड करण्यासाठी प्रतिबंधांपासून ॲपला मुक्त करा."</string>
@@ -581,7 +581,7 @@
     <string name="fingerprint_acquired_imager_dirty" msgid="5236744087471419479">"सेन्सर साफ करा"</string>
     <string name="fingerprint_acquired_too_fast" msgid="6038375140739678098">"जरा जास्त वेळ धरून ठेवा"</string>
     <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"बोट खूप सावकाश हलविले. कृपया पुन्हा प्रयत्न करा."</string>
-    <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"दुसरी फिंगरप्रिंट वापरून पाहा"</string>
+    <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"दुसरी फिंगरप्रिंट वापरून पहा"</string>
     <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"खूप प्रखर"</string>
     <string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"अ‍ॅडजस्ट करण्याचा प्रयत्न करा"</string>
     <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"तुमच्या बोटाची स्थिती प्रत्येक वेळी थोडीशी बदला"</string>
@@ -629,7 +629,7 @@
     <string name="face_acquired_too_low" msgid="1512237819632165945">"फोन आणखी खाली हलवा."</string>
     <string name="face_acquired_too_right" msgid="2513391513020932655">"फोन डावीकडे हलवा."</string>
     <string name="face_acquired_too_left" msgid="8882499346502714350">"फोन उजवीकडे हलवा."</string>
-    <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"कृपया तुमच्या डिव्हाइसकडे थेट पाहा"</string>
+    <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"कृपया तुमच्या डिव्हाइसकडे थेट पहा"</string>
     <string name="face_acquired_not_detected" msgid="2945945257956443257">"तुमचा चेहरा थेट फोन समोर आणा."</string>
     <string name="face_acquired_too_much_motion" msgid="8199691445085189528">"खूप हलत आहे. फोन स्थिर धरून ठेवा."</string>
     <string name="face_acquired_recalibrate" msgid="8724013080976469746">"कृपया तुमच्या चेहऱ्याची पुन्हा नोंदणी करा."</string>
@@ -1701,9 +1701,9 @@
     <string name="accessibility_enable_service_title" msgid="3931558336268541484">"<xliff:g id="SERVICE">%1$s</xliff:g> ला तुमचे डिव्हाइसच संपूर्णपणे नियंत्रित करायची अनुमती द्यायची का?"</string>
     <string name="accessibility_enable_service_encryption_warning" msgid="8603532708618236909">"तुम्ही <xliff:g id="SERVICE">%1$s</xliff:g> सुरू केल्‍यास, तुमचे डिव्हाइस डेटा एंक्रिप्शनमध्ये सुधारणा करण्‍यासाठी स्क्रीन लॉक वापरणार नाही."</string>
     <string name="accessibility_service_warning_description" msgid="291674995220940133">"जी ॲप्स तुमच्या ॲक्सेसिबिलिटी गरजा पूर्ण करतात अशा ॲप्ससाठी संपूर्ण नियंत्रण योग्य आहे. पण ते सर्व ॲप्सना लागू होईल असे नाही."</string>
-    <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"स्क्रीन पाहा आणि नियंत्रित करा"</string>
+    <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"स्क्रीन पहा आणि नियंत्रित करा"</string>
     <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"ते स्क्रीनवरील सर्व आशय वाचू शकते आणि इतर ॲप्सवर आशय प्रदर्शित करू शकते."</string>
-    <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"पाहा आणि क्रिया करा"</string>
+    <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"पहा आणि क्रिया करा"</string>
     <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"तुम्ही ॲप किंवा हार्डवेअर सेन्सर कसे वापरता याचा हे मागोवा घेऊ शकते आणि इतर ॲप्ससोबत तुमच्या वतीने काम करू शकते."</string>
     <string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"अनुमती द्या"</string>
     <string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"नकार द्या"</string>
@@ -1982,7 +1982,7 @@
     <string name="work_mode_turn_on" msgid="3662561662475962285">"सुरू करा"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"ॲप उपलब्ध नाही"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> आता उपलब्ध नाही."</string>
-    <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"हे अ‍ॅप Android च्या जुन्या आवृत्ती साठी तयार करण्यात आले होते आणि योग्यरितीने कार्य करू शकणार नाही. अपडेट आहेत का ते तपासून पाहा, किंवा डेव्हलपरशी संपर्क साधण्याचा प्रयत्न करा."</string>
+    <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"हे अ‍ॅप Android च्या जुन्या आवृत्ती साठी तयार करण्यात आले होते आणि योग्यरितीने कार्य करू शकणार नाही. अपडेट आहेत का ते तपासून पहा, किंवा डेव्हलपरशी संपर्क साधण्याचा प्रयत्न करा."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"अपडेटसाठी तपासा"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"आपल्याकडे नवीन मेसेज आहेत"</string>
     <string name="new_sms_notification_content" msgid="3197949934153460639">"पाहण्‍यासाठी SMS अ‍ॅप उघडा"</string>
diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml
index 816ddd4..2e4578c 100644
--- a/core/res/res/values-night/colors.xml
+++ b/core/res/res/values-night/colors.xml
@@ -29,5 +29,8 @@
     <color name="resolver_empty_state_text">#FFFFFF</color>
     <color name="resolver_empty_state_icon">#FFFFFF</color>
 
+    <color name="call_notification_decline_color">#E66A5E</color>
+    <color name="call_notification_answer_color">#5DBA80</color>
+
     <color name="personal_apps_suspension_notification_color">#8AB4F8</color>
 </resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index c45ac41..b72b000 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -333,7 +333,7 @@
     <string name="permgrouplab_sensors" msgid="9134046949784064495">"Czujniki na ciele"</string>
     <string name="permgroupdesc_sensors" msgid="2610631290633747752">"dostęp do danych czujnika podstawowych funkcji życiowych"</string>
     <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Pobieranie zawartości okna"</string>
-    <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Sprawdzanie zawartości okna, z którego korzystasz."</string>
+    <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Sprawdzanie zawartości okna, z którego korzystasz."</string>
     <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Włączenie czytania dotykiem"</string>
     <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Klikane elementy będą wymawiane na głos, a ekran można przeglądać, używając gestów."</string>
     <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Obserwowanie wpisywanego tekstu"</string>
diff --git a/core/res/res/values-sw600dp/config.xml b/core/res/res/values-sw600dp/config.xml
index 34b6a54..861e329 100644
--- a/core/res/res/values-sw600dp/config.xml
+++ b/core/res/res/values-sw600dp/config.xml
@@ -51,5 +51,10 @@
 
     <!-- If true, show multiuser switcher by default unless the user specifically disables it. -->
     <bool name="config_showUserSwitcherByDefault">true</bool>
+
+    <!-- Enable dynamic keyguard positioning for large-width screens. This will cause the keyguard
+         to be aligned to one side of the screen when in landscape mode. -->
+    <bool name="config_enableDynamicKeyguardPositioning">true</bool>
+
 </resources>
 
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 18b588c..4a5be62 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -169,7 +169,7 @@
     <string name="httpErrorUnsupportedScheme" msgid="2664108769858966374">"நெறிமுறை ஆதரிக்கப்படவில்லை."</string>
     <string name="httpErrorFailedSslHandshake" msgid="546319061228876290">"பாதுகாப்பான இணைப்பை நிறுவ முடியவில்லை."</string>
     <string name="httpErrorBadUrl" msgid="754447723314832538">"URL தவறாக உள்ளதால் பக்கத்தைத் திறக்க முடியவில்லை."</string>
-    <string name="httpErrorFile" msgid="3400658466057744084">"கோப்பை அணுக முடியவில்லை."</string>
+    <string name="httpErrorFile" msgid="3400658466057744084">"ஃபைலை அணுக முடியவில்லை."</string>
     <string name="httpErrorFileNotFound" msgid="5191433324871147386">"கோரப்பட்ட கோப்பைக் கண்டறிய முடியவில்லை."</string>
     <string name="httpErrorTooManyRequests" msgid="2149677715552037198">"மிக அதிகமான கோரிக்கைகள் செயல்படுத்தப்படுகின்றன. பிறகு முயற்சிக்கவும்."</string>
     <string name="notification_title" msgid="5783748077084481121">"<xliff:g id="ACCOUNT">%1$s</xliff:g> க்கான உள்நுழைவு பிழை"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 893aeda..3e4f65c 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -68,7 +68,7 @@
     <string name="CnipMmi" msgid="4897531155968151160">"కాలింగ్ నంబర్ అందుబాటులో ఉంది"</string>
     <string name="CnirMmi" msgid="885292039284503036">"కాలింగ్ నంబర్ పరిమితం చేయబడింది"</string>
     <string name="ThreeWCMmi" msgid="2436550866139999411">"మూడు మార్గాల కాలింగ్"</string>
-    <string name="RuacMmi" msgid="1876047385848991110">"అవాంఛిత అంతరాయ కాల్‌ల తిరస్కరణ"</string>
+    <string name="RuacMmi" msgid="1876047385848991110">"అవాంఛిత అంతరాయ కాల్స్‌ల తిరస్కరణ"</string>
     <string name="CndMmi" msgid="185136449405618437">"కాలింగ్ నంబర్ బట్వాడా"</string>
     <string name="DndMmi" msgid="8797375819689129800">"అంతరాయం కలిగించవద్దు"</string>
     <string name="CLIRDefaultOnNextCallOn" msgid="4511621022859867988">"కాలర్ ID డిఫాల్ట్‌గా పరిమితానికి ఉంటుంది. తర్వాత కాల్: పరిమితం చేయబడింది"</string>
@@ -82,17 +82,17 @@
     <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"వాయిస్ సేవ లేదు"</string>
     <string name="RestrictedOnAllVoiceTitle" msgid="3982069078579103087">"వాయిస్ సేవ లేదా అత్యవసర కాలింగ్ లేదు"</string>
     <string name="RestrictedStateContent" msgid="7693575344608618926">"మీ క్యారియర్ తాత్కాలికంగా ఆఫ్ చేయబడింది"</string>
-    <string name="RestrictedStateContentMsimTemplate" msgid="5228235722511044687">"SIM <xliff:g id="SIMNUMBER">%d</xliff:g> కోసం మీ క్యారియర్ తాత్కాలికంగా ఆఫ్ చేసారు"</string>
+    <string name="RestrictedStateContentMsimTemplate" msgid="5228235722511044687">"SIM <xliff:g id="SIMNUMBER">%d</xliff:g> కోసం మీ క్యారియర్ తాత్కాలికంగా ఆఫ్ చేశారు"</string>
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"మొబైల్ నెట్‌వర్క్ అందుబాటులో లేదు"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"ప్రాధాన్య నెట్‌వర్క్‌ను మార్చుకోవడానికి ప్రయత్నించండి. మార్చడానికి నొక్కండి."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"అత్యవసర కాలింగ్ అందుబాటులో లేదు"</string>
-    <string name="EmergencyCallWarningSummary" msgid="1194185880092805497">"Wi-Fiతో అత్యవసర కాల్‌లు చేయలేరు"</string>
+    <string name="EmergencyCallWarningSummary" msgid="1194185880092805497">"Wi-Fiతో అత్యవసర కాల్స్‌ చేయలేరు"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"అలర్ట్‌లు"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"కాల్ ఫార్వార్డింగ్"</string>
     <string name="notification_channel_emergency_callback" msgid="54074839059123159">"అత్యవసర కాల్‌బ్యాక్ మోడ్"</string>
     <string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"మొబైల్ డేటా స్థితి"</string>
-    <string name="notification_channel_sms" msgid="1243384981025535724">"SMS సందేశాలు"</string>
-    <string name="notification_channel_voice_mail" msgid="8457433203106654172">"వాయిస్ మెయిల్ సందేశాలు"</string>
+    <string name="notification_channel_sms" msgid="1243384981025535724">"SMS మెసేజ్‌లు"</string>
+    <string name="notification_channel_voice_mail" msgid="8457433203106654172">"వాయిస్ మెయిల్ మెసేజ్‌లు"</string>
     <string name="notification_channel_wfc" msgid="9048240466765169038">"Wi-Fi కాలింగ్"</string>
     <string name="notification_channel_sim" msgid="5098802350325677490">"SIM స్టేటస్"</string>
     <string name="notification_channel_sim_high_prio" msgid="642361929452850928">"అధిక ప్రాధాన్యత గల SIM స్థితి"</string>
@@ -105,7 +105,7 @@
     <string name="serviceClassFAX" msgid="2561653371698904118">"ఫ్యాక్స్"</string>
     <string name="serviceClassSMS" msgid="1547664561704509004">"SMS"</string>
     <string name="serviceClassDataAsync" msgid="2029856900898545984">"నిరర్థకం"</string>
-    <string name="serviceClassDataSync" msgid="7895071363569133704">"సమకాలీకరణ"</string>
+    <string name="serviceClassDataSync" msgid="7895071363569133704">"సింక్‌"</string>
     <string name="serviceClassPacket" msgid="1430642951399303804">"ప్యాకెట్"</string>
     <string name="serviceClassPAD" msgid="6850244583416306321">"PAD"</string>
     <string name="roamingText0" msgid="7793257871609854208">"రోమింగ్ సూచిక ఆన్‌లో ఉంది"</string>
@@ -124,7 +124,7 @@
     <string name="roamingTextSearching" msgid="5323235489657753486">"సేవ కోసం శోధిస్తోంది"</string>
     <string name="wfcRegErrorTitle" msgid="3193072971584858020">"Wi‑Fi కాలింగ్‌ని సెటప్ చేయడం సాధ్యపడలేదు"</string>
   <string-array name="wfcOperatorErrorAlertMessages">
-    <item msgid="468830943567116703">"Wi-Fiతో కాల్‌లను చేయడానికి మరియు సందేశాలను పంపించడానికి, మొదట ఈ సేవను సెటప్ చేయాల్సిందిగా మీ క్యారియర్‌‌కి చెప్పండి. ఆ తర్వాత సెట్టింగ్‌ల నుండి Wi-Fi కాలింగ్‌ని మళ్లీ ఆన్ చేయండి. (లోపం కోడ్: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
+    <item msgid="468830943567116703">"Wi-Fiతో కాల్స్‌ను చేయడానికి మరియు మెసేజ్‌లను పంపించడానికి, మొదట ఈ సేవను సెటప్ చేయాల్సిందిగా మీ క్యారియర్‌‌కి చెప్పండి. ఆ తర్వాత సెట్టింగ్‌ల నుండి Wi-Fi కాలింగ్‌ని మళ్లీ ఆన్ చేయండి. (లోపం కోడ్: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
     <item msgid="4795145070505729156">"మీ క్యారియర్‌తో Wi‑Fi కాలింగ్‌ని నమోదు చేయడంలో సమస్య: <xliff:g id="CODE">%1$s</xliff:g>"</item>
@@ -171,15 +171,15 @@
     <string name="httpErrorBadUrl" msgid="754447723314832538">"URL చెల్లనిది అయినందువలన పేజీని తెరవడం సాధ్యపడలేదు."</string>
     <string name="httpErrorFile" msgid="3400658466057744084">"ఫైల్‌ను యాక్సెస్ చేయడం సాధ్యపడలేదు."</string>
     <string name="httpErrorFileNotFound" msgid="5191433324871147386">"అభ్యర్థించిన ఫైల్‌ను కనుగొనడం సాధ్యపడలేదు."</string>
-    <string name="httpErrorTooManyRequests" msgid="2149677715552037198">"చాలా ఎక్కువ అభ్యర్థనలు ప్రాసెస్ చేయబడుతున్నాయి. తర్వాత మళ్లీ ప్రయత్నించండి."</string>
+    <string name="httpErrorTooManyRequests" msgid="2149677715552037198">"చాలా ఎక్కువ రిక్వెస్ట్‌లు ప్రాసెస్ చేయబడుతున్నాయి. తర్వాత మళ్లీ ప్రయత్నించండి."</string>
     <string name="notification_title" msgid="5783748077084481121">"<xliff:g id="ACCOUNT">%1$s</xliff:g>కు సైన్‌ఇన్ ఎర్రర్"</string>
-    <string name="contentServiceSync" msgid="2341041749565687871">"సమకాలీకరణ"</string>
+    <string name="contentServiceSync" msgid="2341041749565687871">"సింక్‌"</string>
     <string name="contentServiceSyncNotificationTitle" msgid="5766411446676388623">"సమకాలీకరించడం సాధ్యపడదు"</string>
     <string name="contentServiceTooManyDeletesNotificationDesc" msgid="4562226280528716090">"చాలా ఎక్కువ <xliff:g id="CONTENT_TYPE">%s</xliff:g> తొలగించడానికి ప్రయత్నించారు."</string>
-    <string name="low_memory" product="tablet" msgid="5557552311566179924">"టాబ్లెట్ నిల్వ నిండింది. స్థలాన్ని ఖాళీ చేయడానికి కొన్ని ఫైల్‌లను తొలగించండి."</string>
-    <string name="low_memory" product="watch" msgid="3479447988234030194">"వాచ్ నిల్వ నిండింది. స్థలాన్ని ఖాళీ చేయడానికి కొన్ని ఫైల్‌లను తొలగించండి."</string>
+    <string name="low_memory" product="tablet" msgid="5557552311566179924">"టాబ్లెట్ నిల్వ నిండింది. స్థలాన్ని ఖాళీ చేయడానికి కొన్ని ఫైళ్లను తొలగించండి."</string>
+    <string name="low_memory" product="watch" msgid="3479447988234030194">"వాచ్ నిల్వ నిండింది. స్థలాన్ని ఖాళీ చేయడానికి కొన్ని ఫైళ్లను తొలగించండి."</string>
     <string name="low_memory" product="tv" msgid="6663680413790323318">"Android TV పరికరం నిల్వ నిండింది. కొంత ప్రదేశాన్ని ఖాళీ చేయడానికి కొన్ని ఫైల్‌‌‌లను తొలగించండి."</string>
-    <string name="low_memory" product="default" msgid="2539532364144025569">"ఫోన్ నిల్వ నిండింది. స్థలాన్ని ఖాళీ చేయడానికి కొన్ని ఫైల్‌లను తొలగించండి."</string>
+    <string name="low_memory" product="default" msgid="2539532364144025569">"ఫోన్ నిల్వ నిండింది. స్థలాన్ని ఖాళీ చేయడానికి కొన్ని ఫైళ్లను తొలగించండి."</string>
     <plurals name="ssl_ca_cert_warning" formatted="false" msgid="2288194355006173029">
       <item quantity="other">ప్రమాణపత్ర అధికారాలు ఇన్‌స్టాల్ చేయబడ్డాయి</item>
       <item quantity="one">ప్రమాణపత్ర అధికారం ఇన్‌స్టాల్ చేయబడింది</item>
@@ -190,7 +190,7 @@
     <string name="work_profile_deleted" msgid="5891181538182009328">"కార్యాలయ ప్రొఫైల్ తొలగించబడింది"</string>
     <string name="work_profile_deleted_details" msgid="3773706828364418016">"కార్యాలయ ప్రొఫైల్ నిర్వాహక యాప్ లేదు లేదా పాడైంది. తత్ఫలితంగా, మీ కార్యాలయ ప్రొఫైల్ మరియు సంబంధిత డేటా తొలగించబడ్డాయి. సహాయం కోసం మీ నిర్వాహకులను సంప్రదించండి."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"ఈ పరికరంలో మీ కార్యాలయ ప్రొఫైల్ ఇప్పుడు అందుబాటులో లేదు"</string>
-    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"చాలా ఎక్కువ పాస్‌వర్డ్ ప్రయత్నాలు చేసారు"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"చాలా ఎక్కువ పాస్‌వర్డ్ ప్రయత్నాలు చేశారు"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"వ్యక్తిగత వినియోగం కోసం నిర్వాహకులు పరికరాన్ని తీసి వేశారు"</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"పరికరం నిర్వహించబడింది"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"మీ సంస్థ ఈ పరికరాన్ని నిర్వహిస్తుంది మరియు నెట్‌వర్క్ ట్రాఫిక్‌ని పర్యవేక్షించవచ్చు. వివరాల కోసం నొక్కండి."</string>
@@ -247,21 +247,21 @@
     <string name="global_action_power_options" msgid="1185286119330160073">"పవర్"</string>
     <string name="global_action_restart" msgid="4678451019561687074">"రీస్టార్ట్ చేయి"</string>
     <string name="global_action_emergency" msgid="1387617624177105088">"ఎమర్జెన్సీ"</string>
-    <string name="global_action_bug_report" msgid="5127867163044170003">"బగ్ నివేదిక"</string>
+    <string name="global_action_bug_report" msgid="5127867163044170003">"బగ్ రిపోర్ట్‌"</string>
     <string name="global_action_logout" msgid="6093581310002476511">"సెషన్‌ను ముగించు"</string>
     <string name="global_action_screenshot" msgid="2610053466156478564">"స్క్రీన్‌షాట్"</string>
     <string name="bugreport_title" msgid="8549990811777373050">"బగ్ రిపోర్ట్‌"</string>
-    <string name="bugreport_message" msgid="5212529146119624326">"ఇది ఇ-మెయిల్ సందేశం రూపంలో పంపడానికి మీ ప్రస్తుత పరికర స్థితి గురించి సమాచారాన్ని సేకరిస్తుంది. బగ్ నివేదికను ప్రారంభించడం మొదలుకొని పంపడానికి సిద్ధం చేసే వరకు ఇందుకు కొంత సమయం పడుతుంది; దయచేసి ఓపిక పట్టండి."</string>
-    <string name="bugreport_option_interactive_title" msgid="7968287837902871289">"ప్రభావశీల నివేదిక"</string>
-    <string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"చాలా సందర్భాల్లో దీన్ని ఉపయోగించండి. ఇది నివేదిక ప్రోగ్రెస్‌ను ట్రాక్ చేయడానికి, సమస్య గురించి మరిన్ని వివరాలను నమోదు చేయడానికి మరియు స్క్రీన్‌షాట్‌లు తీయడానికి మిమ్మల్ని అనుమతిస్తుంది. ఇది నివేదించడానికి ఎక్కువ సమయం పట్టే తక్కువ వినియోగ విభాగాలను విడిచిపెట్టవచ్చు."</string>
-    <string name="bugreport_option_full_title" msgid="7681035745950045690">"పూర్తి నివేదిక"</string>
-    <string name="bugreport_option_full_summary" msgid="1975130009258435885">"మీ పరికరం ప్రతిస్పందనరహితంగా ఉన్నప్పుడు లేదా చాలా నెమ్మదిగా ఉన్నప్పుడు లేదా మీకు అన్ని నివేదిక విభాగాలు అవసరమైనప్పుడు సిస్టమ్‌కి అంతరాయ స్థాయి కనిష్టంగా ఉండేలా చేయడానికి ఈ ఎంపిక ఉపయోగించండి. ఇది మరిన్ని వివరాలను నమోదు చేయడానికి లేదా అదనపు స్క్రీన్‌షాట్‌లు తీయడానికి మిమ్మల్ని అనుమతించదు."</string>
+    <string name="bugreport_message" msgid="5212529146119624326">"ఇది ఇ-మెయిల్ మెసేజ్‌ రూపంలో పంపడానికి మీ ప్రస్తుత పరికర స్థితి గురించి సమాచారాన్ని సేకరిస్తుంది. బగ్ రిపోర్ట్‌ను ప్రారంభించడం మొదలుకొని పంపడానికి సిద్ధం చేసే వరకు ఇందుకు కొంత సమయం పడుతుంది; దయచేసి ఓపిక పట్టండి."</string>
+    <string name="bugreport_option_interactive_title" msgid="7968287837902871289">"ప్రభావశీల రిపోర్ట్‌"</string>
+    <string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"చాలా సందర్భాల్లో దీన్ని ఉపయోగించండి. ఇది రిపోర్ట్‌ ప్రోగ్రెస్‌ను ట్రాక్ చేయడానికి, సమస్య గురించి మరిన్ని వివరాలను నమోదు చేయడానికి మరియు స్క్రీన్‌షాట్‌లు తీయడానికి మిమ్మల్ని అనుమతిస్తుంది. ఇది నివేదించడానికి ఎక్కువ సమయం పట్టే తక్కువ వినియోగ విభాగాలను విడిచిపెట్టవచ్చు."</string>
+    <string name="bugreport_option_full_title" msgid="7681035745950045690">"పూర్తి రిపోర్ట్‌"</string>
+    <string name="bugreport_option_full_summary" msgid="1975130009258435885">"మీ పరికరం ప్రతిస్పందనరహితంగా ఉన్నప్పుడు లేదా చాలా నెమ్మదిగా ఉన్నప్పుడు లేదా మీకు అన్ని రిపోర్ట్‌ విభాగాలు అవసరమైనప్పుడు సిస్టమ్‌కి అంతరాయ స్థాయి కనిష్టంగా ఉండేలా చేయడానికి ఈ ఎంపిక ఉపయోగించండి. ఇది మరిన్ని వివరాలను నమోదు చేయడానికి లేదా అదనపు స్క్రీన్‌షాట్‌లు తీయడానికి మిమ్మల్ని అనుమతించదు."</string>
     <plurals name="bugreport_countdown" formatted="false" msgid="3906120379260059206">
-      <item quantity="other">బగ్ నివేదిక కోసం <xliff:g id="NUMBER_1">%d</xliff:g> సెకన్లలో స్క్రీన్‌షాట్ తీయబోతోంది.</item>
-      <item quantity="one">బగ్ నివేదిక కోసం <xliff:g id="NUMBER_0">%d</xliff:g> సెకనులో స్క్రీన్‌షాట్ తీయబోతోంది.</item>
+      <item quantity="other">బగ్ రిపోర్ట్‌ కోసం <xliff:g id="NUMBER_1">%d</xliff:g> సెకన్లలో స్క్రీన్‌షాట్ తీయబోతోంది.</item>
+      <item quantity="one">బగ్ రిపోర్ట్‌ కోసం <xliff:g id="NUMBER_0">%d</xliff:g> సెకనులో స్క్రీన్‌షాట్ తీయబోతోంది.</item>
     </plurals>
-    <string name="bugreport_screenshot_success_toast" msgid="7986095104151473745">"బగ్ నివేదికతో ఉన్న స్క్రీన్‌షాట్ తీయబడింది"</string>
-    <string name="bugreport_screenshot_failure_toast" msgid="6736320861311294294">"బగ్ నివేదికతో ఉన్న స్క్రీన్‌షాట్‌ను తీయడం విఫలమైంది"</string>
+    <string name="bugreport_screenshot_success_toast" msgid="7986095104151473745">"బగ్ రిపోర్ట్‌తో ఉన్న స్క్రీన్‌షాట్ తీయబడింది"</string>
+    <string name="bugreport_screenshot_failure_toast" msgid="6736320861311294294">"బగ్ రిపోర్ట్‌తో ఉన్న స్క్రీన్‌షాట్‌ను తీయడం విఫలమైంది"</string>
     <string name="global_action_toggle_silent_mode" msgid="8464352592860372188">"నిశ్శబ్ద మోడ్"</string>
     <string name="global_action_silent_mode_on_status" msgid="2371892537738632013">"ధ్వని ఆఫ్‌లో ఉంది"</string>
     <string name="global_action_silent_mode_off_status" msgid="6608006545950920042">"ధ్వని ఆన్‌లో ఉంది"</string>
@@ -279,8 +279,8 @@
     <string name="notification_channel_security" msgid="8516754650348238057">"సెక్యూరిటీ"</string>
     <string name="notification_channel_car_mode" msgid="2123919247040988436">"కార్‌ మోడ్"</string>
     <string name="notification_channel_account" msgid="6436294521740148173">"ఖాతా స్థితి"</string>
-    <string name="notification_channel_developer" msgid="1691059964407549150">"డెవలపర్ సందేశాలు"</string>
-    <string name="notification_channel_developer_important" msgid="7197281908918789589">"ముఖ్యమైన డెవలపర్ సందేశాలు"</string>
+    <string name="notification_channel_developer" msgid="1691059964407549150">"డెవలపర్ మెసేజ్‌లు"</string>
+    <string name="notification_channel_developer_important" msgid="7197281908918789589">"ముఖ్యమైన డెవలపర్ మెసేజ్‌లు"</string>
     <string name="notification_channel_updates" msgid="7907863984825495278">"అప్‌డేట్‌లు"</string>
     <string name="notification_channel_network_status" msgid="2127687368725272809">"నెట్‌వర్క్ స్థితి"</string>
     <string name="notification_channel_network_alerts" msgid="6312366315654526528">"నెట్‌వర్క్ హెచ్చరికలు"</string>
@@ -309,13 +309,13 @@
     <string name="permgrouplab_calendar" msgid="6426860926123033230">"Calendar"</string>
     <string name="permgroupdesc_calendar" msgid="6762751063361489379">"మీ క్యాలెండర్‌ను యాక్సెస్ చేయడానికి"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
-    <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS సందేశాలను పంపడం మరియు వీక్షించడం"</string>
+    <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS మెసేజ్‌లను పంపడం మరియు వీక్షించడం"</string>
     <string name="permgrouplab_storage" msgid="1938416135375282333">"ఫైల్స్, మీడియా"</string>
-    <string name="permgroupdesc_storage" msgid="6351503740613026600">"మీ పరికరంలోని ఫోటోలు, మీడియా మరియు ఫైల్‌లను యాక్సెస్ చేయడానికి"</string>
+    <string name="permgroupdesc_storage" msgid="6351503740613026600">"మీ పరికరంలోని ఫోటోలు, మీడియా మరియు ఫైళ్లను యాక్సెస్ చేయడానికి"</string>
     <string name="permgrouplab_microphone" msgid="2480597427667420076">"మైక్రోఫోన్"</string>
     <string name="permgroupdesc_microphone" msgid="1047786732792487722">"ఆడియోను రికార్డ్ చేయడానికి"</string>
     <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"ఫిజికల్ యాక్టివిటీ"</string>
-    <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"భౌతిక కార్యకలాపాన్ని యాక్సెస్ చేయండి"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"భౌతిక యాక్టివిటీని యాక్సెస్ చేయండి"</string>
     <string name="permgrouplab_camera" msgid="9090413408963547706">"కెమెరా"</string>
     <string name="permgroupdesc_camera" msgid="7585150538459320326">"చిత్రాలను తీయడానికి మరియు వీడియోను రికార్డ్ చేయడానికి"</string>
     <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"సమీపంలోని పరికరాలు"</string>
@@ -323,7 +323,7 @@
     <string name="permgrouplab_calllog" msgid="7926834372073550288">"కాల్ లాగ్‌లు"</string>
     <string name="permgroupdesc_calllog" msgid="2026996642917801803">"ఫోన్ కాల్ లాగ్‌ని చదవండి మరియు రాయండి"</string>
     <string name="permgrouplab_phone" msgid="570318944091926620">"ఫోన్"</string>
-    <string name="permgroupdesc_phone" msgid="270048070781478204">"ఫోన్ కాల్‌లు చేయడం మరియు నిర్వహించడం"</string>
+    <string name="permgroupdesc_phone" msgid="270048070781478204">"ఫోన్ కాల్స్‌ చేయడం మరియు నిర్వహించడం"</string>
     <string name="permgrouplab_sensors" msgid="9134046949784064495">"శరీర సెన్సార్‌లు"</string>
     <string name="permgroupdesc_sensors" msgid="2610631290633747752">"మీ అత్యంత కీలకమైన గుర్తుల గురించి సెన్సార్ డేటాని యాక్సెస్ చేస్తుంది"</string>
     <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"విండో కంటెంట్‍ను తిరిగి పొందుతుంది"</string>
@@ -349,42 +349,42 @@
     <string name="permlab_fullScreenIntent" msgid="4310888199502509104">"లాక్ చేసి ఉన్న పరికరంలో నోటిఫికేషన్‌లను ఫుల్ స్క్రీన్ యాక్టివిటీలుగా డిస్‌ప్లే చేస్తుంది"</string>
     <string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"లాక్ చేసి ఉన్న పరికరంలో నోటిఫికేషన్‌లను ఫుల్ స్క్రీన్ యాక్టివిటీలుగా డిస్‌ప్లే చేయడానికి యాప్‌ను అనుమతిస్తుంది"</string>
     <string name="permlab_install_shortcut" msgid="7451554307502256221">"షార్ట్‌కట్‌లను ఇన్‌స్టాల్ చేయడం"</string>
-    <string name="permdesc_install_shortcut" msgid="4476328467240212503">"వినియోగదారు ప్రమేయం లేకుండానే హోమ్‌స్క్రీన్ సత్వరమార్గాలను జోడించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
-    <string name="permlab_uninstall_shortcut" msgid="295263654781900390">"సత్వరమార్గాలను అన్ఇన్‌స్టాల్ చేయడం"</string>
-    <string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"వినియోగదారు ప్రమేయం లేకుండానే హోమ్‌స్క్రీన్ సత్వరమార్గాలను తీసివేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
-    <string name="permlab_processOutgoingCalls" msgid="4075056020714266558">"అవుట్‌గోయింగ్ కాల్‌లను దారి మళ్లించడం"</string>
+    <string name="permdesc_install_shortcut" msgid="4476328467240212503">"వినియోగదారు ప్రమేయం లేకుండానే హోమ్‌స్క్రీన్ షార్ట్‌కట్‌లను జోడించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
+    <string name="permlab_uninstall_shortcut" msgid="295263654781900390">"షార్ట్‌కట్‌లను అన్ఇన్‌స్టాల్ చేయడం"</string>
+    <string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"వినియోగదారు ప్రమేయం లేకుండానే హోమ్‌స్క్రీన్ షార్ట్‌కట్‌లను తీసివేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
+    <string name="permlab_processOutgoingCalls" msgid="4075056020714266558">"అవుట్‌గోయింగ్ కాల్స్‌ను దారి మళ్లించడం"</string>
     <string name="permdesc_processOutgoingCalls" msgid="7833149750590606334">"కాల్‌ను వేరే నంబర్‌కు దారి మళ్లించే లేదా మొత్తంగా కాల్‌ను ఆపివేసే ఎంపిక సహాయంతో అవుట్‌గోయింగ్ కాల్ సమయంలో డయల్ చేయబడుతున్న నంబర్‌ను చూడటానికి యాప్‌ను అనుమతిస్తుంది."</string>
-    <string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"ఫోన్ కాల్‌లకు సమాధానమివ్వు"</string>
-    <string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"ఇన్‌కమింగ్ ఫోన్ కాల్‌లకు సమాధానమివ్వడానికి యాప్‌ను అనుమతిస్తుంది."</string>
-    <string name="permlab_receiveSms" msgid="505961632050451881">"వచన సందేశాలను (SMS) స్వీకరించడం"</string>
-    <string name="permdesc_receiveSms" msgid="1797345626687832285">"SMS సందేశాలను స్వీకరించడానికి మరియు ప్రాసెస్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. యాప్ మీ డివైజ్‌కు పంపబడిన సందేశాలను మీకు చూపకుండానే పర్యవేక్షించగలదని లేదా తొలగించగలదని దీని అర్థం."</string>
-    <string name="permlab_receiveMms" msgid="4000650116674380275">"వచన సందేశాలను (MMS) స్వీకరించడం"</string>
-    <string name="permdesc_receiveMms" msgid="958102423732219710">"MMS సందేశాలను స్వీకరించడానికి మరియు ప్రాసెస్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. యాప్ మీ డివైజ్‌కు పంపబడిన సందేశాలను మీకు చూపకుండానే పర్యవేక్షించగలదని లేదా తొలగించగలదని దీని అర్థం."</string>
-    <string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"సెల్ ప్రసార సందేశాలను ఫార్వర్డ్ చేయడం"</string>
-    <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"సెల్ ప్రసార సందేశాలను అందుకుంటే, వాటిని ఫార్వర్డ్ చేసే విధంగా సెల్ ప్రసార మాడ్యూల్‌కు కట్టుబడి ఉండటానికి యాప్‌ను అనుమతిస్తుంది. సెల్ ప్రసార హెచ్చరికలు అత్యవసర పరిస్థితుల గురించి మిమ్మల్ని హెచ్చరించడానికి కొన్ని స్థానాల్లో అందించబడతాయి. అత్యవసర సెల్ ప్రసారం అందుకున్నప్పుడు హానికరమైన యాప్‌లు మీ పరికరం యొక్క పనితీరు లేదా నిర్వహణకు అంతరాయం కలిగించవచ్చు."</string>
+    <string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"ఫోన్ కాల్స్‌కు సమాధానమివ్వు"</string>
+    <string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"ఇన్‌కమింగ్ ఫోన్ కాల్స్‌కు సమాధానమివ్వడానికి యాప్‌ను అనుమతిస్తుంది."</string>
+    <string name="permlab_receiveSms" msgid="505961632050451881">"వచన మెసేజ్‌లను (SMS) స్వీకరించడం"</string>
+    <string name="permdesc_receiveSms" msgid="1797345626687832285">"SMS మెసేజ్‌లను స్వీకరించడానికి మరియు ప్రాసెస్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. యాప్ మీ డివైజ్‌కు పంపబడిన మెసేజ్‌లను మీకు చూపకుండానే పర్యవేక్షించగలదని లేదా తొలగించగలదని దీని అర్థం."</string>
+    <string name="permlab_receiveMms" msgid="4000650116674380275">"వచన మెసేజ్‌లను (MMS) స్వీకరించడం"</string>
+    <string name="permdesc_receiveMms" msgid="958102423732219710">"MMS మెసేజ్‌లను స్వీకరించడానికి మరియు ప్రాసెస్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. యాప్ మీ డివైజ్‌కు పంపబడిన మెసేజ్‌లను మీకు చూపకుండానే పర్యవేక్షించగలదని లేదా తొలగించగలదని దీని అర్థం."</string>
+    <string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"సెల్ ప్రసార మెసేజ్‌లను ఫార్వర్డ్ చేయడం"</string>
+    <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"సెల్ ప్రసార మెసేజ్‌లను అందుకుంటే, వాటిని ఫార్వర్డ్ చేసే విధంగా సెల్ ప్రసార మాడ్యూల్‌కు కట్టుబడి ఉండటానికి యాప్‌ను అనుమతిస్తుంది. సెల్ ప్రసార హెచ్చరికలు అత్యవసర పరిస్థితుల గురించి మిమ్మల్ని హెచ్చరించడానికి కొన్ని స్థానాల్లో అందించబడతాయి. అత్యవసర సెల్ ప్రసారం అందుకున్నప్పుడు హానికరమైన యాప్‌లు మీ పరికరం యొక్క పనితీరు లేదా నిర్వహణకు అంతరాయం కలిగించవచ్చు."</string>
     <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"కొనసాగుతున్న కాల్స్‌ను మేనేజ్ చేయి"</string>
     <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"మీ పరికరంలో కొనసాగుతున్న కాల్స్‌ను చూడటానికి అలాగే వాటిని కంట్రోల్ చేయడానికి ఒక యాప్‌కు అనుమతిస్తోంది."</string>
-    <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"సెల్ ప్రసార సందేశాలను చదవడం"</string>
-    <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"మీ పరికరం స్వీకరించిన సెల్ ప్రసార సందేశాలను చదవడానికి యాప్‌ను అనుమతిస్తుంది. సెల్ ప్రసార హెచ్చరికలు అత్యవసర పరిస్థితుల గురించి మిమ్మల్ని హెచ్చరించడానికి కొన్ని స్థానాల్లో అందించబడతాయి. అత్యవసర సెల్ ప్రసారం స్వీకరించినప్పుడు హానికరమైన యాప్‌లు మీ పరికరం యొక్క పనితీరు లేదా నిర్వహణకు అంతరాయం కలిగించవచ్చు."</string>
+    <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"సెల్ ప్రసార మెసేజ్‌లను చదవడం"</string>
+    <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"మీ పరికరం స్వీకరించిన సెల్ ప్రసార మెసేజ్‌లను చదవడానికి యాప్‌ను అనుమతిస్తుంది. సెల్ ప్రసార హెచ్చరికలు అత్యవసర పరిస్థితుల గురించి మిమ్మల్ని హెచ్చరించడానికి కొన్ని స్థానాల్లో అందించబడతాయి. అత్యవసర సెల్ ప్రసారం స్వీకరించినప్పుడు హానికరమైన యాప్‌లు మీ పరికరం యొక్క పనితీరు లేదా నిర్వహణకు అంతరాయం కలిగించవచ్చు."</string>
     <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"చందా చేయబడిన ఫీడ్‌లను చదవడం"</string>
     <string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"ప్రస్తుతం సమకాలీకరించిన ఫీడ్‌ల గురించి వివరాలను పొందడానికి యాప్‌ను అనుమతిస్తుంది."</string>
-    <string name="permlab_sendSms" msgid="7757368721742014252">"SMS సందేశాలను పంపడం మరియు వీక్షించడం"</string>
-    <string name="permdesc_sendSms" msgid="6757089798435130769">"SMS సందేశాలు పంపడానికి యాప్‌ను అనుమతిస్తుంది. దీని వలన ఊహించని ఛార్జీలు విధించబడవచ్చు. హానికరమైన యాప్‌లు మీ నిర్ధారణ లేకుండానే సందేశాలను పంపడం ద్వారా మీకు డబ్బు ఖర్చయ్యేలా చేయవచ్చు."</string>
-    <string name="permlab_readSms" msgid="5164176626258800297">"మీ వచన సందేశాలు (SMS లేదా MMS) చదవడం"</string>
-    <string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"ఈ యాప్‌ మీ టాబ్లెట్‌లో నిల్వ చేసిన అన్ని SMS (వచన) సందేశాలను చదవగలదు."</string>
+    <string name="permlab_sendSms" msgid="7757368721742014252">"SMS మెసేజ్‌లను పంపడం మరియు వీక్షించడం"</string>
+    <string name="permdesc_sendSms" msgid="6757089798435130769">"SMS మెసేజ్‌లు పంపడానికి యాప్‌ను అనుమతిస్తుంది. దీని వలన ఊహించని ఛార్జీలు విధించబడవచ్చు. హానికరమైన యాప్‌లు మీ నిర్ధారణ లేకుండానే మెసేజ్‌లను పంపడం ద్వారా మీకు డబ్బు ఖర్చయ్యేలా చేయవచ్చు."</string>
+    <string name="permlab_readSms" msgid="5164176626258800297">"మీ వచన మెసేజ్‌లు (SMS లేదా MMS) చదవడం"</string>
+    <string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"ఈ యాప్‌ మీ టాబ్లెట్‌లో నిల్వ చేసిన అన్ని SMS (వచన) మెసేజ్‌లను చదవగలదు."</string>
     <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"ఈ యాప్ మీ Android TV పరికరంలో నిల్వ అయిన SMS (వచనం) సందేశాలన్నింటినీ చదవగలదు."</string>
-    <string name="permdesc_readSms" product="default" msgid="774753371111699782">"ఈ యాప్‌ మీ ఫోన్‌లో నిల్వ చేసిన అన్ని SMS (వచన) సందేశాలను చదవగలదు."</string>
-    <string name="permlab_receiveWapPush" msgid="4223747702856929056">"వచన సందేశాలను (WAP) స్వీకరించడం"</string>
-    <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"WAP సందేశాలను స్వీకరించడానికి మరియు ప్రాసెస్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. ఈ అనుమతి మీకు పంపబడిన సందేశాలను మీకు చూపకుండానే పర్యవేక్షించగల లేదా తొలగించగల సామర్థ్యాన్ని కలిగి ఉంటుంది."</string>
-    <string name="permlab_getTasks" msgid="7460048811831750262">"అమలవుతున్న అనువర్తనాలను పునరుద్ధరించడం"</string>
+    <string name="permdesc_readSms" product="default" msgid="774753371111699782">"ఈ యాప్‌ మీ ఫోన్‌లో నిల్వ చేసిన అన్ని SMS (వచన) మెసేజ్‌లను చదవగలదు."</string>
+    <string name="permlab_receiveWapPush" msgid="4223747702856929056">"వచన మెసేజ్‌లను (WAP) స్వీకరించడం"</string>
+    <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"WAP మెసేజ్‌లను స్వీకరించడానికి మరియు ప్రాసెస్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. ఈ అనుమతి మీకు పంపబడిన మెసేజ్‌లను మీకు చూపకుండానే పర్యవేక్షించగల లేదా తొలగించగల సామర్థ్యాన్ని కలిగి ఉంటుంది."</string>
+    <string name="permlab_getTasks" msgid="7460048811831750262">"అమలవుతున్న యాప్‌లను పునరుద్ధరించడం"</string>
     <string name="permdesc_getTasks" msgid="7388138607018233726">"ప్రస్తుతం మరియు ఇటీవల అమలవుతున్న విధుల గురించి వివరణాత్మక సమాచారాన్ని తిరిగి పొందడానికి యాప్‌ను అనుమతిస్తుంది. ఇది పరికరంలో ఉపయోగించబడిన యాప్‌ల గురించి సమాచారాన్ని కనుగొనడానికి యాప్‌ను అనుమతించవచ్చు."</string>
     <string name="permlab_manageProfileAndDeviceOwners" msgid="639849495253987493">"ప్రొఫైల్ మరియు పరికర యజమానులను నిర్వహించడం"</string>
-    <string name="permdesc_manageProfileAndDeviceOwners" msgid="7304240671781989283">"ప్రొఫైల్ యజమానులను మరియు పరికరం యజమానిని సెట్ చేయడానికి అనువర్తనాలను అనుమతిస్తుంది."</string>
+    <string name="permdesc_manageProfileAndDeviceOwners" msgid="7304240671781989283">"ప్రొఫైల్ యజమానులను మరియు పరికరం యజమానిని సెట్ చేయడానికి యాప్‌లను అనుమతిస్తుంది."</string>
     <string name="permlab_reorderTasks" msgid="7598562301992923804">"అమలవుతోన్న యాప్‌లను మళ్లీ క్రమం చేయడం"</string>
     <string name="permdesc_reorderTasks" msgid="8796089937352344183">"విధులను ముందుకు మరియు నేపథ్యానికి తరలించడానికి యాప్‌ను అనుమతిస్తుంది. యాప్ మీ ప్రమేయం లేకుండానే దీన్ని చేయవచ్చు."</string>
     <string name="permlab_enableCarMode" msgid="893019409519325311">"కారు మోడ్‌ను ప్రారంభించడం"</string>
     <string name="permdesc_enableCarMode" msgid="56419168820473508">"కారు మోడ్‌ను ప్రారంభించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
-    <string name="permlab_killBackgroundProcesses" msgid="6559320515561928348">"ఇతర అనువర్తనాలను మూసివేయడం"</string>
+    <string name="permlab_killBackgroundProcesses" msgid="6559320515561928348">"ఇతర యాప్‌లను మూసివేయడం"</string>
     <string name="permdesc_killBackgroundProcesses" msgid="2357013583055434685">"ఇతర యాప్‌ల నేపథ్య ప్రాసెస్‌లను ముగించడానికి యాప్‌ను అనుమతిస్తుంది. దీని వలన ఇతర యాప్‌లు అమలు కాకుండా ఆపివేయబడవచ్చు."</string>
     <string name="permlab_systemAlertWindow" msgid="5757218350944719065">"ఈ యాప్ ఇతర యాప్‌ల పైభాగాన కనిపించగలదు"</string>
     <string name="permdesc_systemAlertWindow" msgid="1145660714855738308">"ఈ యాప్ ఇతర యాప్‌ల పైభాగాన లేదా స్క్రీన్ యొక్క ఇతర భాగాలపైన కనిపించగలదు. ఇది సాధారణ యాప్ వినియోగానికి అంతరాయం కలిగించవచ్చు మరియు ఆ ఇతర యాప్‌లు కనిపించే విధానాన్ని మార్చవచ్చు."</string>
@@ -421,9 +421,9 @@
     <string name="permlab_readCallLog" msgid="1739990210293505948">"కాల్ లాగ్‌ను చదవడం"</string>
     <string name="permdesc_readCallLog" msgid="8964770895425873433">"ఈ యాప్‌ మీ కాల్ చరిత్రను చదవగలదు."</string>
     <string name="permlab_writeCallLog" msgid="670292975137658895">"కాల్ లాగ్‌ను వ్రాయడం"</string>
-    <string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"ఇన్‌కమింగ్ మరియు అవుట్‌గోయింగ్ కాల్‌ల గురించిన డేటాతో సహా మీ టాబ్లెట్ యొక్క కాల్ లాగ్‌ను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ కాల్ లాగ్‌ను ఎరేజ్ చేయడానికి లేదా సవరించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
-    <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"ఇన్‌కమింగ్ మరియు అవుట్‌గోయింగ్ కాల్‌లకు సంబంధించిన డేటాతో సహా మీ Android TV పరికరం కాల్ లాగ్‌ను సవరించడానికి యాప్‌ని అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ కాల్ లాగ్‌ను తీసివేయడానికి లేదా సవరించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
-    <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"ఇన్‌కమింగ్ మరియు అవుట్‌గోయింగ్ కాల్‌ల గురించిన డేటాతో సహా మీ ఫోన్ యొక్క కాల్ లాగ్‌ను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ కాల్ లాగ్‌ను ఎరేజ్ చేయడానికి లేదా సవరించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
+    <string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"ఇన్‌కమింగ్ మరియు అవుట్‌గోయింగ్ కాల్స్‌ల గురించిన డేటాతో సహా మీ టాబ్లెట్ యొక్క కాల్ లాగ్‌ను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ కాల్ లాగ్‌ను ఎరేజ్ చేయడానికి లేదా సవరించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
+    <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"ఇన్‌కమింగ్ మరియు అవుట్‌గోయింగ్ కాల్స్‌కు సంబంధించిన డేటాతో సహా మీ Android TV పరికరం కాల్ లాగ్‌ను సవరించడానికి యాప్‌ని అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ కాల్ లాగ్‌ను తీసివేయడానికి లేదా సవరించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
+    <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"ఇన్‌కమింగ్ మరియు అవుట్‌గోయింగ్ కాల్స్‌ల గురించిన డేటాతో సహా మీ ఫోన్ యొక్క కాల్ లాగ్‌ను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. హానికరమైన యాప్‌లు మీ కాల్ లాగ్‌ను ఎరేజ్ చేయడానికి లేదా సవరించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"శరీర సెన్సార్‌లను (గుండె స్పందన రేటు మానిటర్‌ల వంటివి) యాక్సెస్ చేయండి"</string>
     <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"మీ శారీరక పరిస్థితిని అనగా మీ గుండె స్పందన రేటు వంటి వాటిని పర్యవేక్షించే సెన్సార్‌ల నుండి డేటాను యాక్సెస్ చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"క్యాలెండర్ ఈవెంట్‌లు మరియు వివరాలను చదవడం"</string>
@@ -431,9 +431,9 @@
     <string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"ఈ యాప్‌ మీ Android TV పరికరంలో నిల్వ చేసిన క్యాలెండర్ ఈవెంట్‌లన్నీ చదవగలదు, మీ క్యాలెండర్ డేటాను షేర్ చేయగలదు లేదా సేవ్ చేయగలదు."</string>
     <string name="permdesc_readCalendar" product="default" msgid="9118823807655829957">"ఈ యాప్ మీ ఫోన్‌లో నిల్వ చేసిన క్యాలెండర్ ఈవెంట్‌లన్నీ చదవగలదు మరియు మీ క్యాలెండర్ డేటాను షేర్ చేయగలదు లేదా సేవ్ చేయగలదు."</string>
     <string name="permlab_writeCalendar" msgid="6422137308329578076">"యజమానికి తెలియకుండానే క్యాలెండర్ ఈవెంట్‌లను జోడించి లేదా సవరించి, అతిథులకు ఇమెయిల్ పంపడం"</string>
-    <string name="permdesc_writeCalendar" product="tablet" msgid="8722230940717092850">"ఈ యాప్ మీ టాబ్లెట్‌లో క్యాలెండర్ ఈవెంట్‌లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ యజమానుల నుండి వచ్చినట్లుగా సందేశాలను పంపగలదు లేదా ఈవెంట్‌లను వాటి యజమానులకు తెలియకుండానే మార్చగలదు."</string>
-    <string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"ఈ యాప్ మీ Android TV పరికరంలో క్యాలెండర్ ఈవెంట్‌లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ యజమానుల నుండి వచ్చినట్లుగా సందేశాలను పంపగలదు లేదా ఈవెంట్‌లను వాటి యజమానులకు తెలియకుండానే మార్చగలదు."</string>
-    <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"ఈ యాప్ మీ ఫోన్‌లో క్యాలెండర్ ఈవెంట్‌లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ యజమానుల నుండి వచ్చినట్లుగా సందేశాలను పంపగలదు లేదా ఈవెంట్‌లను వాటి యజమానులకు తెలియకుండానే మార్చగలదు."</string>
+    <string name="permdesc_writeCalendar" product="tablet" msgid="8722230940717092850">"ఈ యాప్ మీ టాబ్లెట్‌లో క్యాలెండర్ ఈవెంట్‌లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ యజమానుల నుండి వచ్చినట్లుగా మెసేజ్‌లను పంపగలదు లేదా ఈవెంట్‌లను వాటి యజమానులకు తెలియకుండానే మార్చగలదు."</string>
+    <string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"ఈ యాప్ మీ Android TV పరికరంలో క్యాలెండర్ ఈవెంట్‌లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ యజమానుల నుండి వచ్చినట్లుగా మెసేజ్‌లను పంపగలదు లేదా ఈవెంట్‌లను వాటి యజమానులకు తెలియకుండానే మార్చగలదు."</string>
+    <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"ఈ యాప్ మీ ఫోన్‌లో క్యాలెండర్ ఈవెంట్‌లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ యజమానుల నుండి వచ్చినట్లుగా మెసేజ్‌లను పంపగలదు లేదా ఈవెంట్‌లను వాటి యజమానులకు తెలియకుండానే మార్చగలదు."</string>
     <string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"అదనపు స్థాన ప్రదాత ఆదేశాలను యాక్సెస్ చేయడం"</string>
     <string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"అదనపు స్థాన ప్రదాత ఆదేశాలను యాక్సెస్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. ఇది GPS లేదా ఇతర స్థాన మూలాల నిర్వహణలో యాప్‌ ప్రమేయం ఉండేలా అనుమతించవచ్చు."</string>
     <string name="permlab_accessFineLocation" msgid="6426318438195622966">"స్క్రీన్‌పై ఉన్నప్పుడు మాత్రమే ఖచ్చితమైన స్థానాన్ని యాక్సెస్ చేయండి"</string>
@@ -464,15 +464,15 @@
     <string name="permdesc_vibrate" msgid="8733343234582083721">"వైబ్రేటర్‌ను నియంత్రించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permdesc_vibrator_state" msgid="7050024956594170724">"వైబ్రేటర్ స్థితిని యాక్సెస్ చేసేందుకు యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_callPhone" msgid="1798582257194643320">"నేరుగా కాల్ చేసే ఫోన్ నంబర్‌లు"</string>
-    <string name="permdesc_callPhone" msgid="5439809516131609109">"మీ ప్రమేయం లేకుండా ఫోన్ నంబర్‌లకు కాల్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. దీని వలన అనుకోని ఛార్జీలు విధించబడవచ్చు లేదా కాల్‌లు రావచ్చు. ఇది అత్యవసర నంబర్‌లకు కాల్ చేయడానికి యాప్‌ను అనుమతించదని గుర్తుంచుకోండి. హానికరమైన యాప్‌లు మీ నిర్ధారణ లేకుండానే కాల్‌లు చేయడం ద్వారా మీకు డబ్బు ఖర్చయ్యేలా చేయవచ్చు."</string>
+    <string name="permdesc_callPhone" msgid="5439809516131609109">"మీ ప్రమేయం లేకుండా ఫోన్ నంబర్‌లకు కాల్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. దీని వలన అనుకోని ఛార్జీలు విధించబడవచ్చు లేదా కాల్స్‌ రావచ్చు. ఇది అత్యవసర నంబర్‌లకు కాల్ చేయడానికి యాప్‌ను అనుమతించదని గుర్తుంచుకోండి. హానికరమైన యాప్‌లు మీ నిర్ధారణ లేకుండానే కాల్స్‌ చేయడం ద్వారా మీకు డబ్బు ఖర్చయ్యేలా చేయవచ్చు."</string>
     <string name="permlab_accessImsCallService" msgid="442192920714863782">"IMS కాల్ సేవ యాక్సెస్ అనుమతి"</string>
-    <string name="permdesc_accessImsCallService" msgid="6328551241649687162">"మీ ప్రమేయం లేకుండా కాల్‌లు చేయడం కోసం IMS సేవను ఉపయోగించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
+    <string name="permdesc_accessImsCallService" msgid="6328551241649687162">"మీ ప్రమేయం లేకుండా కాల్స్‌ చేయడం కోసం IMS సేవను ఉపయోగించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_readPhoneState" msgid="8138526903259297969">"ఫోన్ స్థితి మరియు గుర్తింపుని చదవడం"</string>
     <string name="permdesc_readPhoneState" msgid="7229063553502788058">"పరికరం యొక్క ఫోన్ ఫీచర్‌లను యాక్సెస్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. ఈ అనుమతి ఫోన్ నంబర్ మరియు పరికరం IDలను, కాల్ సక్రియంగా ఉందా లేదా అనే విషయాన్ని మరియు కాల్ ద్వారా కనెక్ట్ చేయబడిన రిమోట్ నంబర్‌ను కనుగొనడానికి యాప్‌ను అనుమతిస్తుంది."</string>
-    <string name="permlab_manageOwnCalls" msgid="9033349060307561370">"కాల్‌లను సిస్టమ్ ద్వారా వెళ్లేలా చేయి"</string>
-    <string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"కాలింగ్ అనుభవాన్ని మెరుగుపరచడం కోసం తన కాల్‌లను సిస్టమ్ ద్వారా వెళ్లేలా చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
-    <string name="permlab_callCompanionApp" msgid="3654373653014126884">"సిస్టమ్ ద్వారా కాల్‌లను చూసి, నియంత్రించండి."</string>
-    <string name="permdesc_callCompanionApp" msgid="8474168926184156261">"పరికరంలో కొనసాగుతున్న కాల్‌లను చూడడానికి మరియు నియంత్రించడానికి యాప్‌ను అనుమతిస్తుంది. ఇందులో కాల్ కోసం కాల్‌ల నంబర్‌లు మరియు రాష్ట్ర కాల్ వంటి సమాచారం ఉంటుంది."</string>
+    <string name="permlab_manageOwnCalls" msgid="9033349060307561370">"కాల్స్‌ను సిస్టమ్ ద్వారా వెళ్లేలా చేయి"</string>
+    <string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"కాలింగ్ అనుభవాన్ని మెరుగుపరచడం కోసం తన కాల్స్‌ను సిస్టమ్ ద్వారా వెళ్లేలా చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
+    <string name="permlab_callCompanionApp" msgid="3654373653014126884">"సిస్టమ్ ద్వారా కాల్స్‌ను చూసి, నియంత్రించండి."</string>
+    <string name="permdesc_callCompanionApp" msgid="8474168926184156261">"పరికరంలో కొనసాగుతున్న కాల్స్‌ను చూడడానికి మరియు నియంత్రించడానికి యాప్‌ను అనుమతిస్తుంది. ఇందులో కాల్ కోసం కాల్స్‌ల నంబర్‌లు మరియు రాష్ట్ర కాల్ వంటి సమాచారం ఉంటుంది."</string>
     <string name="permlab_exemptFromAudioRecordRestrictions" msgid="1164725468350759486">"ఆడియో రికార్డ్ పరిమితుల నుండి మినహాయింపు"</string>
     <string name="permdesc_exemptFromAudioRecordRestrictions" msgid="2425117015896871976">"ఆడియోను రికార్డ్ చేయడానికి యాప్‌ను పరిమితుల నుండి మినహాయించండి."</string>
     <string name="permlab_acceptHandover" msgid="2925523073573116523">"మరో యాప్ నుండి కాల్‌ని కొనసాగించండి"</string>
@@ -500,9 +500,9 @@
     <string name="permdesc_setTimeZone" product="tv" msgid="9069045914174455938">"మీ Android TV పరికరం సమయ మండలిని మార్చడానికి యాప్‌ని అనుమతిస్తుంది."</string>
     <string name="permdesc_setTimeZone" product="default" msgid="4611828585759488256">"ఫోన్ యొక్క సమయ మండలిని మార్చడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_getAccounts" msgid="5304317160463582791">"పరికరంలో ఖాతాలను కనుగొనడం"</string>
-    <string name="permdesc_getAccounts" product="tablet" msgid="1784452755887604512">"టాబ్లెట్‌కు తెలిసిన ఖాతాల జాబితాను పొందడానికి యాప్‌ను అనుమతిస్తుంది. దీనిలో మీరు ఇన్‌స్టాల్ చేసిన యాప్‌ల ద్వారా సృష్టించబడిన ఖాతాలు ఏవైనా ఉండవచ్చు."</string>
-    <string name="permdesc_getAccounts" product="tv" msgid="437604680436540822">"మీ Android TV పరికరానికి తెలిసిన ఖాతాల జాబితాను పొందడానికి యాప్‌ను అనుమతిస్తుంది. దీనిలో మీరు ఇన్‌స్టాల్ చేసిన యాప్‌ల ద్వారా సృష్టించబడిన ఖాతాలు ఏవైనా ఉండవచ్చు."</string>
-    <string name="permdesc_getAccounts" product="default" msgid="2491273043569751867">"ఫోన్‌కు తెలిసిన ఖాతాల జాబితాను పొందడానికి యాప్‌ను అనుమతిస్తుంది. దీనిలో మీరు ఇన్‌స్టాల్ చేసిన యాప్‌ల ద్వారా సృష్టించబడిన ఖాతాలు ఏవైనా ఉండవచ్చు."</string>
+    <string name="permdesc_getAccounts" product="tablet" msgid="1784452755887604512">"టాబ్లెట్‌కు తెలిసిన ఖాతాల లిస్ట్‌ను పొందడానికి యాప్‌ను అనుమతిస్తుంది. దీనిలో మీరు ఇన్‌స్టాల్ చేసిన యాప్‌ల ద్వారా సృష్టించబడిన ఖాతాలు ఏవైనా ఉండవచ్చు."</string>
+    <string name="permdesc_getAccounts" product="tv" msgid="437604680436540822">"మీ Android TV పరికరానికి తెలిసిన ఖాతాల లిస్ట్‌ను పొందడానికి యాప్‌ను అనుమతిస్తుంది. దీనిలో మీరు ఇన్‌స్టాల్ చేసిన యాప్‌ల ద్వారా సృష్టించబడిన ఖాతాలు ఏవైనా ఉండవచ్చు."</string>
+    <string name="permdesc_getAccounts" product="default" msgid="2491273043569751867">"ఫోన్‌కు తెలిసిన ఖాతాల లిస్ట్‌ను పొందడానికి యాప్‌ను అనుమతిస్తుంది. దీనిలో మీరు ఇన్‌స్టాల్ చేసిన యాప్‌ల ద్వారా సృష్టించబడిన ఖాతాలు ఏవైనా ఉండవచ్చు."</string>
     <string name="permlab_accessNetworkState" msgid="2349126720783633918">"నెట్‌వర్క్ కనెక్షన్‌లను వీక్షించడం"</string>
     <string name="permdesc_accessNetworkState" msgid="4394564702881662849">"ఏ నెట్‌వర్క్‌లు ఉన్నాయి మరియు కనెక్ట్ చేయబడ్డాయి వంటి నెట్‌వర్క్ కనెక్షన్‌ల గురించి సమాచారాన్ని వీక్షించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_createNetworkSockets" msgid="3224420491603590541">"నెట్‌వర్క్‌ను పూర్తిగా యాక్సెస్ చేయగలగడం"</string>
@@ -516,9 +516,9 @@
     <string name="permlab_changeWifiState" msgid="7947824109713181554">"Wi-Fiకి కనెక్ట్ చేయడం మరియు దాని నుండి డిస్‌కనెక్ట్ చేయడం"</string>
     <string name="permdesc_changeWifiState" msgid="7170350070554505384">"Wi-Fi యాక్సెస్ స్థానాలకు కనెక్ట్ చేయడానికి మరియు వాటి నుండి డిస్‌కనెక్ట్ చేయడానికి మరియు Wi-Fi నెట్‌వర్క్‌ల కోసం పరికర కాన్ఫిగరేషన్‌కు మార్పులు చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"Wi-Fi Multicast స్వీకరణను అనుమతించడం"</string>
-    <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"మల్టీక్యాస్ట్ చిరునామాలను ఉపయోగించి మీ టాబ్లెట్‌కు మాత్రమే కాకుండా Wi-Fi నెట్‌వర్క్‌లోని అన్ని పరికరాలకు పంపబడిన ప్యాకెట్‌లను స్వీకరించడానికి యాప్‌ను అనుమతిస్తుంది. మల్టీక్యాస్ట్ యేతర మోడ్ కంటే ఇది ఎక్కువ పవర్ ఉపయోగిస్తుంది."</string>
-    <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"మల్టీక్యాస్ట్ చిరునామాలను ఉపయోగించి మీ Android TV పరికరానికి మాత్రమే కాకుండా Wi-Fi నెట్‌వర్క్‌లోని అన్ని పరికరాలకు పంపిన ప్యాకెట్‌లను స్వీకరించడానికి యాప్‌ని అనుమతిస్తుంది. ఇది మల్టీక్యాస్ట్ యేతర మోడ్ కంటే ఎక్కువ పవర్‌ను ఉపయోగిస్తుంది."</string>
-    <string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"మల్టీక్యాస్ట్ చిరునామాలను ఉపయోగించి మీ ఫోన్‌కు మాత్రమే కాకుండా Wi-Fi నెట్‌వర్క్‌లోని అన్ని పరికరాలకు పంపబడిన ప్యాకెట్‌లను స్వీకరించడానికి యాప్‌ను అనుమతిస్తుంది. మల్టీక్యాస్ట్ యేతర మోడ్ కంటే ఇది ఎక్కువ పవర్ ఉపయోగిస్తుంది."</string>
+    <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"మల్టీక్యాస్ట్ అడ్రస్‌లను ఉపయోగించి మీ టాబ్లెట్‌కు మాత్రమే కాకుండా Wi-Fi నెట్‌వర్క్‌లోని అన్ని పరికరాలకు పంపబడిన ప్యాకెట్‌లను స్వీకరించడానికి యాప్‌ను అనుమతిస్తుంది. మల్టీక్యాస్ట్ యేతర మోడ్ కంటే ఇది ఎక్కువ పవర్ ఉపయోగిస్తుంది."</string>
+    <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"మల్టీక్యాస్ట్ అడ్రస్‌లను ఉపయోగించి మీ Android TV పరికరానికి మాత్రమే కాకుండా Wi-Fi నెట్‌వర్క్‌లోని అన్ని పరికరాలకు పంపిన ప్యాకెట్‌లను స్వీకరించడానికి యాప్‌ని అనుమతిస్తుంది. ఇది మల్టీక్యాస్ట్ యేతర మోడ్ కంటే ఎక్కువ పవర్‌ను ఉపయోగిస్తుంది."</string>
+    <string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"మల్టీక్యాస్ట్ అడ్రస్‌లను ఉపయోగించి మీ ఫోన్‌కు మాత్రమే కాకుండా Wi-Fi నెట్‌వర్క్‌లోని అన్ని పరికరాలకు పంపబడిన ప్యాకెట్‌లను స్వీకరించడానికి యాప్‌ను అనుమతిస్తుంది. మల్టీక్యాస్ట్ యేతర మోడ్ కంటే ఇది ఎక్కువ పవర్ ఉపయోగిస్తుంది."</string>
     <string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"బ్లూటూత్ సెట్టింగ్‌లను యాక్సెస్ చేయడం"</string>
     <string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"స్థానిక బ్లూటూత్ టాబ్లెట్‌ను కాన్ఫిగర్ చేయడానికి మరియు రిమోట్ పరికరాలతో దాన్ని కనుగొనడానికి మరియు జత చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"మీ Android TV పరికరంలో బ్లూటూత్‌ను కాన్ఫిగర్ చేయడానికి మరియు రిమోట్ పరికరాలతో దాన్ని కనుగొని, జత చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
@@ -547,7 +547,7 @@
     <string name="permdesc_nfc" msgid="8352737680695296741">"సమీప ఫీల్డ్ కమ్యూనికేషన్ (NFC) ట్యాగ్‌లు, కార్డులు మరియు రీడర్‌లతో కమ్యూనికేట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_disableKeyguard" msgid="3605253559020928505">"మీ స్క్రీన్ లాక్‌ను నిలిపివేయడం"</string>
     <string name="permdesc_disableKeyguard" msgid="3223710003098573038">"కీలాక్ మరియు ఏదైనా అనుబంధించబడిన పాస్‌వర్డ్ భద్రతను నిలిపివేయడానికి యాప్‌ను అనుమతిస్తుంది. ఉదాహరణకు, ఇన్‌కమింగ్ ఫోన్ కాల్ వస్తున్నప్పుడు ఫోన్ కీలాక్‌ను నిలిపివేస్తుంది, ఆపై కాల్ ముగిసిన తర్వాత కీలాక్‌ను మళ్లీ ప్రారంభిస్తుంది."</string>
-    <string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"స్క్రీన్ లాక్ సంక్లిష్టత అభ్యర్థన"</string>
+    <string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"స్క్రీన్ లాక్ సంక్లిష్టత రిక్వెస్ట్‌"</string>
     <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"ఇది మీ స్క్రీన్ లాక్ పాస్‌వర్డ్‌ సంక్లిష్టత స్థాయి (తీవ్రంగా ఉండాలా, ఓ మోస్తరుగా ఉండాలా, తక్కువ తీవ్రంగా ఉండాలా లేదా అస్సలు తీవ్రత ఉండకూడదా) తెలుసుకోవడానికి యాప్‌ను అనుమతిస్తుంది, అంటే పొడుగు ఎంత ఉండాలి, ఏ రకమైన స్క్రీన్ లాక్ పధ్ధతి అనుసరించాలో సూచిస్తుంది. అలాగే, స్క్రీన్ లాక్‌ పాస్‌వర్డ్‌ సంక్లిష్టతను ఏ స్థాయికి సెట్ చేసుకుంటే బాగుంటుందో కూడా వినియోగదారులకు యాప్ సూచించగలదు, కానీ వినియోగదారులు నిరభ్యంతరంగా ఆ సూచనలను పట్టించుకోకుండా వారి ఇష్టం మేరకు చక్కగా సెట్ చేసుకోవచ్చు. ఇంకో ముఖ్య విషయం, స్క్రీన్ లాక్‌ అన్నది సాదా వచన రూపంలో నిల్వ చేయబడదు, కనుక ఖచ్చితమైన పాస్‌వర్డ్‌ ఏమిటనేది యాప్‌కు తెలియదు."</string>
     <string name="permlab_useBiometric" msgid="6314741124749633786">"బయోమెట్రిక్ హార్డ్‌వేర్‌ని ఉపయోగించు"</string>
     <string name="permdesc_useBiometric" msgid="7502858732677143410">"ప్రమాణీకరణ కోసం బయోమెట్రిక్ హార్డ్‌వేర్‌ను ఉపయోగించడానికి యాప్‌ని అనుమతిస్తుంది"</string>
@@ -555,11 +555,11 @@
     <string name="permdesc_manageFingerprint" msgid="2025616816437339865">"వినియోగం కోసం వేలిముద్ర టెంప్లేట్‌లను జోడించే, తొలగించే పద్ధతులను అమలు చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_useFingerprint" msgid="1001421069766751922">"వేలిముద్ర హార్డ్‌వేర్‌ని ఉపయోగించడానికి అనుమతి"</string>
     <string name="permdesc_useFingerprint" msgid="412463055059323742">"ప్రామాణీకరణ కోసం వేలిముద్ర హార్డ్‌వేర్‌ను ఉపయోగించడానికి యాప్‌ను అనుమతిస్తుంది"</string>
-    <string name="permlab_audioWrite" msgid="8501705294265669405">"మీ సంగీత సేకరణను సవరించండి"</string>
+    <string name="permlab_audioWrite" msgid="8501705294265669405">"మీ సంగీత సేకరణను ఎడిట్ చేయండి"</string>
     <string name="permdesc_audioWrite" msgid="8057399517013412431">"మీ సంగీత సేకరణని సవరించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
-    <string name="permlab_videoWrite" msgid="5940738769586451318">"మీ వీడియో సేకరణను సవరించండి"</string>
+    <string name="permlab_videoWrite" msgid="5940738769586451318">"మీ వీడియో సేకరణను ఎడిట్ చేయండి"</string>
     <string name="permdesc_videoWrite" msgid="6124731210613317051">"మీ వీడియో సేకరణను సవరించడానికి యాప్‌ని అనుమతిస్తుంది."</string>
-    <string name="permlab_imagesWrite" msgid="1774555086984985578">"మీ ఫోటో సేకరణను సవరించండి"</string>
+    <string name="permlab_imagesWrite" msgid="1774555086984985578">"మీ ఫోటో సేకరణను ఎడిట్ చేయండి"</string>
     <string name="permdesc_imagesWrite" msgid="5195054463269193317">"మీ ఫోటో సేకరణను సవరించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_mediaLocation" msgid="7368098373378598066">"మీ మీడియా సేకరణ నుండి స్థానాలను చదవండి"</string>
     <string name="permdesc_mediaLocation" msgid="597912899423578138">"మీ మీడియా సేకరణ నుండి స్థానాలను చదవడానికి యాప్‌ను అనుమతిస్తుంది."</string>
@@ -593,9 +593,9 @@
     <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"వేలిముద్ర హార్డ్‌వేర్ అందుబాటులో లేదు."</string>
     <string name="fingerprint_error_no_space" msgid="7285481581905967580">"వేలిముద్రను సెటప్ చేయడం సాధ్యం కాదు"</string>
     <string name="fingerprint_error_timeout" msgid="2946635815726054226">"వేలిముద్ర గడువు సమయం చేరుకుంది. మళ్లీ ప్రయత్నించండి."</string>
-    <string name="fingerprint_error_canceled" msgid="540026881380070750">"వేలిముద్ర కార్యకలాపం రద్దయింది."</string>
-    <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"వేలిముద్ర చర్యని వినియోగదారు రద్దు చేసారు."</string>
-    <string name="fingerprint_error_lockout" msgid="7853461265604738671">"చాలా ఎక్కువ ప్రయత్నాలు చేసారు. తర్వాత మళ్లీ ప్రయత్నించండి."</string>
+    <string name="fingerprint_error_canceled" msgid="540026881380070750">"వేలిముద్ర యాక్టివిటీ రద్దయింది."</string>
+    <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"వేలిముద్ర చర్యని వినియోగదారు రద్దు చేశారు."</string>
+    <string name="fingerprint_error_lockout" msgid="7853461265604738671">"చాలా ఎక్కువ ప్రయత్నాలు చేశారు. తర్వాత మళ్లీ ప్రయత్నించండి."</string>
     <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"అనేకసార్లు ప్రయత్నించారు. వేలిముద్ర సెన్సార్ నిలిపివేయబడింది."</string>
     <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"మళ్లీ ప్రయత్నించండి."</string>
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"వేలిముద్రలు నమోదు చేయబడలేదు."</string>
@@ -645,9 +645,9 @@
     <string name="face_error_hw_not_available" msgid="5085202213036026288">"ముఖం ధృవీకరించలేరు. హార్డ్‌వేర్ అందుబాటులో లేదు."</string>
     <string name="face_error_timeout" msgid="2598544068593889762">"ఫేస్ అన్‌లాక్‌ను మళ్లీ ట్రై చేయండి"</string>
     <string name="face_error_no_space" msgid="5649264057026021723">"కొత్త ముఖం డేటాను నిల్వ చేయడం కాదు. మొదట పాతది తొలిగించండి."</string>
-    <string name="face_error_canceled" msgid="2164434737103802131">"ముఖ కార్యకలాపం రద్దయింది."</string>
+    <string name="face_error_canceled" msgid="2164434737103802131">"ముఖ యాక్టివిటీ రద్దయింది."</string>
     <string name="face_error_user_canceled" msgid="5766472033202928373">"ఫేస్ అన్‌లాక్‌ను యూజర్ రద్దు చేశారు"</string>
-    <string name="face_error_lockout" msgid="7864408714994529437">"చాలా ఎక్కువ ప్రయత్నాలు చేసారు. తర్వాత మళ్లీ ప్రయత్నించండి."</string>
+    <string name="face_error_lockout" msgid="7864408714994529437">"చాలా ఎక్కువ ప్రయత్నాలు చేశారు. తర్వాత మళ్లీ ప్రయత్నించండి."</string>
     <string name="face_error_lockout_permanent" msgid="3277134834042995260">"చాలా ఎక్కువ సార్లు ప్రయత్నించారు. ఫేస్ అన్‌లాక్ డిజేబుల్ చేయబడింది."</string>
     <string name="face_error_lockout_screen_lock" msgid="5062609811636860928">"చాలా ఎక్కువ సార్లు ప్రయత్నించారు. బదులుగా స్క్రీన్ లాక్‌ను ఎంటర్ చేయండి."</string>
     <string name="face_error_unable_to_process" msgid="5723292697366130070">"ముఖం ధృవీకరించలేకపోయింది. మళ్లీ ప్రయత్నించండి."</string>
@@ -663,17 +663,17 @@
   </string-array>
     <string name="face_icon_content_description" msgid="465030547475916280">"ముఖ చిహ్నం"</string>
     <string name="permlab_readSyncSettings" msgid="6250532864893156277">"సింక్ సెట్టింగ్‌లను చదవగలగడం"</string>
-    <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"ఖాతా యొక్క సమకాలీకరణ సెట్టింగ్‌లను చదవడానికి యాప్‌ను అనుమతిస్తుంది. ఉదాహరణకు, వ్యక్తుల యాప్‌ ఖాతాతో సమకాలీకరించబడాలా లేదా అనే విషయాన్ని ఇది నిశ్చయించవచ్చు."</string>
+    <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"ఖాతా యొక్క సింక్‌ సెట్టింగ్‌లను చదవడానికి యాప్‌ను అనుమతిస్తుంది. ఉదాహరణకు, వ్యక్తుల యాప్‌ ఖాతాతో సమకాలీకరించబడాలా లేదా అనే విషయాన్ని ఇది నిశ్చయించవచ్చు."</string>
     <string name="permlab_writeSyncSettings" msgid="6583154300780427399">"\'సింక్\'ను ఆన్, ఆఫ్‌ల మధ్య టోగుల్ చేయడం"</string>
-    <string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"ఖాతా యొక్క సమకాలీకరణ సెట్టింగ్‌లను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. ఉదాహరణకు, ఇది ఒక ఖాతాతో వ్యక్తుల యాప్ యొక్క సమకాలీకరణను ప్రారంభించడానికి ఉపయోగించబడవచ్చు."</string>
+    <string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"ఖాతా యొక్క సింక్‌ సెట్టింగ్‌లను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. ఉదాహరణకు, ఇది ఒక ఖాతాతో వ్యక్తుల యాప్ యొక్క సింక్‌ను ప్రారంభించడానికి ఉపయోగించబడవచ్చు."</string>
     <string name="permlab_readSyncStats" msgid="3747407238320105332">"సింక్ గణాంకాలను చదవగలగడం"</string>
-    <string name="permdesc_readSyncStats" msgid="3867809926567379434">"ఖాతా యొక్క సమకాలీకరణ గణాంకాలను అలాగే సమకాలీకరణ ఈవెంట్‌ల చరిత్రను మరియు ఎంత డేటా సమకాలీకరించబడింది అనేవాటిని చదవడానికి యాప్‌ను అనుమతిస్తుంది."</string>
+    <string name="permdesc_readSyncStats" msgid="3867809926567379434">"ఖాతా యొక్క సింక్‌ గణాంకాలను అలాగే సింక్‌ ఈవెంట్‌ల చరిత్రను మరియు ఎంత డేటా సమకాలీకరించబడింది అనేవాటిని చదవడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_sdcardRead" msgid="5791467020950064920">"మీ షేర్ చేసిన నిల్వ యొక్క కంటెంట్‌లను చదువుతుంది"</string>
     <string name="permdesc_sdcardRead" msgid="6872973242228240382">"మీ షేర్ చేసిన నిల్వ యొక్క కంటెంట్‌లను చదవడానికి యాప్‌ను అనుమతిస్తుంది."</string>
-    <string name="permlab_sdcardWrite" msgid="4863021819671416668">"మీ షేర్ చేసిన నిల్వ యొక్క కంటెంట్‌లను సవరించండి లేదా తొలగించండి"</string>
+    <string name="permlab_sdcardWrite" msgid="4863021819671416668">"మీ షేర్ చేసిన నిల్వ యొక్క కంటెంట్‌లను ఎడిట్ చేయండి లేదా తొలగించండి"</string>
     <string name="permdesc_sdcardWrite" msgid="8376047679331387102">"మీ షేర్ చేసిన నిల్వ యొక్క కంటెంట్‌లను రాయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
-    <string name="permlab_use_sip" msgid="8250774565189337477">"SIP కాల్‌లను చేయడానికి/స్వీకరించడానికి"</string>
-    <string name="permdesc_use_sip" msgid="3590270893253204451">"SIP కాల్‌లను చేయడానికి మరియు స్వీకరించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
+    <string name="permlab_use_sip" msgid="8250774565189337477">"SIP కాల్స్‌ను చేయడానికి/స్వీకరించడానికి"</string>
+    <string name="permdesc_use_sip" msgid="3590270893253204451">"SIP కాల్స్‌ను చేయడానికి మరియు స్వీకరించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_register_sim_subscription" msgid="1653054249287576161">"కొత్త టెలికామ్ SIM కనెక్షన్‌లను నమోదు చేయడం"</string>
     <string name="permdesc_register_sim_subscription" msgid="4183858662792232464">"కొత్త టెలికామ్ SIM కనెక్షన్‌లను నమోదు చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_register_call_provider" msgid="6135073566140050702">"కొత్త టెలికామ్ కనెక్షన్‌లను నమోదు చేయడం"</string>
@@ -683,7 +683,7 @@
     <string name="permlab_bind_incall_service" msgid="5990625112603493016">"ఇన్-కాల్ స్క్రీన్‌తో పరస్పర చర్య చేయడం"</string>
     <string name="permdesc_bind_incall_service" msgid="4124917526967765162">"వినియోగదారునికి ఇన్-కాల్ స్క్రీన్ ఎప్పుడు, ఎలా కనిపించాలనే దాన్ని నియంత్రించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_bind_connection_service" msgid="5409268245525024736">"టెలిఫోన్ సేవలతో పరస్పర చర్య చేయడం"</string>
-    <string name="permdesc_bind_connection_service" msgid="6261796725253264518">"కాల్‌లు చేయడం/స్వీకరించడం కోసం టెలిఫోన్ సేవలతో పరస్పర చర్య చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
+    <string name="permdesc_bind_connection_service" msgid="6261796725253264518">"కాల్స్‌ చేయడం/స్వీకరించడం కోసం టెలిఫోన్ సేవలతో పరస్పర చర్య చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_control_incall_experience" msgid="6436863486094352987">"ఇన్-కాల్ వినియోగదారు అనుభవాన్ని అందించడం"</string>
     <string name="permdesc_control_incall_experience" msgid="5896723643771737534">"ఇన్-కాల్ వినియోగదారుని అనుభవాన్ని అందించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_readNetworkUsageHistory" msgid="8470402862501573795">"చారిత్రక నెట్‌వర్క్ వినియోగాన్ని చదవడం"</string>
@@ -693,29 +693,29 @@
     <string name="permlab_modifyNetworkAccounting" msgid="7448790834938749041">"నెట్‌వర్క్ వినియోగ అకౌంటింగ్‌ను సవరించడం"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5076042642247205390">"యాప్‌లలో నెట్‌వర్క్ వినియోగం ఎలా గణించాలనే దాన్ని సవరించడానికి యాప్‌ను అనుమతిస్తుంది. సాధారణ యాప్‌ల ద్వారా ఉపయోగించడానికి ఉద్దేశించినది కాదు."</string>
     <string name="permlab_accessNotifications" msgid="7130360248191984741">"నోటిఫికేషన్‌లను యాక్సెస్ చేయడం"</string>
-    <string name="permdesc_accessNotifications" msgid="761730149268789668">"నోటిఫికేషన్‌లను, ఇతర అనువర్తనాల ద్వారా పోస్ట్ చేయబడిన వాటిని తిరిగి పొందడానికి, పరిశీలించడానికి మరియు క్లియర్ చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
+    <string name="permdesc_accessNotifications" msgid="761730149268789668">"నోటిఫికేషన్‌లను, ఇతర యాప్‌ల ద్వారా పోస్ట్ చేయబడిన వాటిని తిరిగి పొందడానికి, పరిశీలించడానికి మరియు క్లియర్ చేయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_bindNotificationListenerService" msgid="5848096702733262458">"నోటిఫికేషన్ పరిశీలన సేవకు అనుబంధించడం"</string>
-    <string name="permdesc_bindNotificationListenerService" msgid="4970553694467137126">"నోటిఫికేషన్ పరిశీలన సేవ యొక్క అగ్ర-స్థాయి ఇంటర్‌ఫేస్‌కు అనుబంధించడానికి హోల్డర్‌ను అనుమతిస్తుంది. సాధారణ అనువర్తనాల కోసం ఎప్పటికీ అవసరం ఉండకూడదు."</string>
+    <string name="permdesc_bindNotificationListenerService" msgid="4970553694467137126">"నోటిఫికేషన్ పరిశీలన సేవ యొక్క అగ్ర-స్థాయి ఇంటర్‌ఫేస్‌కు అనుబంధించడానికి హోల్డర్‌ను అనుమతిస్తుంది. సాధారణ యాప్‌ల కోసం ఎప్పటికీ అవసరం ఉండకూడదు."</string>
     <string name="permlab_bindConditionProviderService" msgid="5245421224814878483">"షరతు ప్రదాత సేవకు అనుబంధించడం"</string>
-    <string name="permdesc_bindConditionProviderService" msgid="6106018791256120258">"షరతు ప్రదాత సేవ యొక్క అగ్ర-స్థాయి ఇంటర్‌ఫేస్‌కు అనుబంధించడానికి హోల్డర్‌ను అనుమతిస్తుంది. సాధారణ అనువర్తనాలకు ఎప్పటికీ అవసరం ఉండదు."</string>
+    <string name="permdesc_bindConditionProviderService" msgid="6106018791256120258">"షరతు ప్రదాత సేవ యొక్క అగ్ర-స్థాయి ఇంటర్‌ఫేస్‌కు అనుబంధించడానికి హోల్డర్‌ను అనుమతిస్తుంది. సాధారణ యాప్‌లకు ఎప్పటికీ అవసరం ఉండదు."</string>
     <string name="permlab_bindDreamService" msgid="4776175992848982706">"డ్రీమ్ సేవ‌కి అనుబంధించడం"</string>
-    <string name="permdesc_bindDreamService" msgid="9129615743300572973">"డ్రీమ్ సేవ యొక్క అగ్ర-స్థాయి ఇంటర్‌ఫేస్‌కు అనుబంధించడానికి హోల్డర్‌ను అనుమతిస్తుంది. సాధారణ అనువర్తనాలకు ఎప్పటికీ అవసరం ఉండదు."</string>
+    <string name="permdesc_bindDreamService" msgid="9129615743300572973">"డ్రీమ్ సేవ యొక్క అగ్ర-స్థాయి ఇంటర్‌ఫేస్‌కు అనుబంధించడానికి హోల్డర్‌ను అనుమతిస్తుంది. సాధారణ యాప్‌లకు ఎప్పటికీ అవసరం ఉండదు."</string>
     <string name="permlab_invokeCarrierSetup" msgid="5098810760209818140">"క్యారియర్ అందించిన కాన్ఫిగరేషన్ యాప్‌ను అభ్యర్థించడం"</string>
-    <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"క్యారియర్ అందించిన కాన్ఫిగరేషన్ యాప్‌ను అభ్యర్థించడానికి హోల్డర్‌ను అనుమతిస్తుంది. సాధారణ అనువర్తనాల కోసం ఎప్పటికీ అవసరం ఉండకూడదు."</string>
+    <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"క్యారియర్ అందించిన కాన్ఫిగరేషన్ యాప్‌ను అభ్యర్థించడానికి హోల్డర్‌ను అనుమతిస్తుంది. సాధారణ యాప్‌ల కోసం ఎప్పటికీ అవసరం ఉండకూడదు."</string>
     <string name="permlab_accessNetworkConditions" msgid="1270732533356286514">"నెట్‌వర్క్ పరిస్థితులపై పరిశీలనల గురించి తెలుసుకోవడం"</string>
-    <string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"నెట్‌వర్క్ పరిస్థితులపై పరిశీలనల గురించి తెలుసుకోవడానికి యాప్‌ను అనుమతిస్తుంది. సాధారణ అనువర్తనాలకు ఎప్పటికీ అవసరం ఉండకూడదు."</string>
+    <string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"నెట్‌వర్క్ పరిస్థితులపై పరిశీలనల గురించి తెలుసుకోవడానికి యాప్‌ను అనుమతిస్తుంది. సాధారణ యాప్‌లకు ఎప్పటికీ అవసరం ఉండకూడదు."</string>
     <string name="permlab_setInputCalibration" msgid="932069700285223434">"ఇన్‌పుట్ పరికరం క్రమాంకనాన్ని మార్చండి"</string>
-    <string name="permdesc_setInputCalibration" msgid="2937872391426631726">"టచ్ స్క్రీన్ యొక్క క్రమాంకన పరామితులను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. సాధారణ అనువర్తనాలకు ఎప్పటికీ అవసరం ఉండదు."</string>
+    <string name="permdesc_setInputCalibration" msgid="2937872391426631726">"టచ్ స్క్రీన్ యొక్క క్రమాంకన పరామితులను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. సాధారణ యాప్‌లకు ఎప్పటికీ అవసరం ఉండదు."</string>
     <string name="permlab_accessDrmCertificates" msgid="6473765454472436597">"DRM ప్రమాణపత్రాలను యాక్సెస్ చేయడం"</string>
-    <string name="permdesc_accessDrmCertificates" msgid="6983139753493781941">"DRM ప్రమాణపత్రాలను కేటాయించడానికి మరియు ఉపయోగించడానికి యాప్‌ను అనుమతిస్తుంది. సాధారణ అనువర్తనాలకు ఎప్పటికీ అవసరం ఉండదు."</string>
+    <string name="permdesc_accessDrmCertificates" msgid="6983139753493781941">"DRM ప్రమాణపత్రాలను కేటాయించడానికి మరియు ఉపయోగించడానికి యాప్‌ను అనుమతిస్తుంది. సాధారణ యాప్‌లకు ఎప్పటికీ అవసరం ఉండదు."</string>
     <string name="permlab_handoverStatus" msgid="7620438488137057281">"Android Beam బదిలీ స్థితిని స్వీకరించడం"</string>
     <string name="permdesc_handoverStatus" msgid="3842269451732571070">"ప్రస్తుత Android Beam బదిలీలకు సంబంధించిన సమాచారాన్ని స్వీకరించడానికి ఈ యాప్‌ను అనుమతిస్తుంది"</string>
     <string name="permlab_removeDrmCertificates" msgid="710576248717404416">"DRM ప్రమాణపత్రాలను తీసివేయడం"</string>
-    <string name="permdesc_removeDrmCertificates" msgid="4068445390318355716">"DRM ప్రమాణపత్రాలను తీసివేయడానికి యాప్‌ను అనుమతిస్తుంది. సాధారణ అనువర్తనాలకు ఎప్పటికీ అవసరం ఉండదు."</string>
+    <string name="permdesc_removeDrmCertificates" msgid="4068445390318355716">"DRM ప్రమాణపత్రాలను తీసివేయడానికి యాప్‌ను అనుమతిస్తుంది. సాధారణ యాప్‌లకు ఎప్పటికీ అవసరం ఉండదు."</string>
     <string name="permlab_bindCarrierMessagingService" msgid="3363450860593096967">"క్యారియర్ సందేశ సేవకు అనుబంధించడం"</string>
-    <string name="permdesc_bindCarrierMessagingService" msgid="6316457028173478345">"క్యారియర్ సందేశ సేవ యొక్క అగ్ర-స్థాయి ఇంటర్‌ఫేస్‌కు అనుబంధించడానికి హోల్డర్‌ను అనుమతిస్తుంది. సాధారణ అనువర్తనాలకు ఎప్పటికీ అవసరం ఉండదు."</string>
+    <string name="permdesc_bindCarrierMessagingService" msgid="6316457028173478345">"క్యారియర్ మెసేజింగ్ సర్వీస్‌ యొక్క అగ్ర-స్థాయి ఇంటర్‌ఫేస్‌కు అనుబంధించడానికి హోల్డర్‌ను అనుమతిస్తుంది. సాధారణ యాప్‌లకు ఎప్పటికీ అవసరం ఉండదు."</string>
     <string name="permlab_bindCarrierServices" msgid="2395596978626237474">"క్యారియర్ సేవలకు అనుబంధించడం"</string>
-    <string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"క్యారియర్ సేవలకు అనుబంధించడానికి హోల్డర్‌ను అనుమతిస్తుంది. సాధారణ అనువర్తనాలకు ఎప్పటికీ అవసరం ఉండదు."</string>
+    <string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"క్యారియర్ సేవలకు అనుబంధించడానికి హోల్డర్‌ను అనుమతిస్తుంది. సాధారణ యాప్‌లకు ఎప్పటికీ అవసరం ఉండదు."</string>
     <string name="permlab_access_notification_policy" msgid="5524112842876975537">"అంతరాయం కలిగించవద్దును యాక్సెస్ చేయడం"</string>
     <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"అంతరాయం కలిగించవద్దు ఎంపిక కాన్ఫిగరేషన్ చదవడానికి మరియు వ్రాయడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"వీక్షణ అనుమతి వినియోగాన్ని ప్రారంభించండి"</string>
@@ -911,21 +911,21 @@
     <string name="lockscreen_sim_locked_message" msgid="3160196135801185938">"సిమ్ కార్డు లాక్ చేయబడింది."</string>
     <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="2286497117428409709">"సిమ్ కార్డు‌ను అన్‌లాక్ చేస్తోంది…"</string>
     <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"మీరు మీ అన్‌లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
-    <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"మీరు మీ పాస్‌వర్డ్‌ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేసారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
-    <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"మీరు మీ పిన్‌ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేసారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
+    <string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"మీరు మీ పాస్‌వర్డ్‌ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేశారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
+    <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"మీరు మీ పిన్‌ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేశారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="3069635524964070596">"మీరు మీ అన్‌లాక్ నమూనాని <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విజయవంతం కాని ప్రయత్నాల తర్వాత, మీరు మీ Google సైన్ఇన్‌ను ఉపయోగించి మీ టాబ్లెట్‌ను అన్‌లాక్ చేయడానికి అడగబడతారు.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"మీరు మీ అన్‌లాక్ నమూనాని <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, మీరు మీ Google సైన్ఇన్‌ను ఉపయోగించి మీ Android TV పరికరాన్ని అన్‌లాక్ చేయాల్సిందిగా మీకు తెలపబడుతుంది.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
     <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="5691623136957148335">"మీరు మీ అన్‌లాక్ నమూనాని <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విజయవంతం కాని ప్రయత్నాల తర్వాత, మీరు మీ Google సైన్ఇన్‌ను ఉపయోగించి మీ ఫోన్‌ను అన్‌లాక్ చేయడానికి అడగబడతారు.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
     <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="7914445759242151426">"మీరు టాబ్లెట్‌ను అన్‌లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా ప్రయత్నించారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> వైఫల్య ప్రయత్నాల తర్వాత, టాబ్లెట్ ఫ్యాక్టరీ డిఫాల్ట్‌కు రీసెట్ చేయబడుతుంది మరియు మొత్తం వినియోగదారు డేటాను కోల్పోవడం సంభవిస్తుంది."</string>
-    <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="4275591249631864248">"మీరు మీ Android TV పరికరాన్ని అన్‌లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు విఫల ప్రయత్నాలు చేసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, మీ Android TV పరికరం ఫ్యాక్టరీ డిఫాల్ట్‌కి రీసెట్ చేయబడుతుంది, అలాగే వినియోగదారు డేటా మొత్తాన్ని కోల్పోతారు."</string>
+    <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="4275591249631864248">"మీరు మీ Android TV పరికరాన్ని అన్‌లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు విఫల ప్రయత్నాలు చేశారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, మీ Android TV పరికరం ఫ్యాక్టరీ డిఫాల్ట్‌కి రీసెట్ చేయబడుతుంది, అలాగే వినియోగదారు డేటా మొత్తాన్ని కోల్పోతారు."</string>
     <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="1166532464798446579">"మీరు ఫోన్‌ను అన్‌లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా ప్రయత్నించారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> వైఫల్య ప్రయత్నాల తర్వాత, ఫోన్ ఫ్యాక్టరీ డిఫాల్ట్‌కు రీసెట్ చేయబడుతుంది మరియు మొత్తం వినియోగదారు డేటాను కోల్పోవడం సంభవిస్తుంది."</string>
     <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="8682445539263683414">"మీరు టాబ్లెట్‌ను అన్‌లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> సార్లు తప్పుగా ప్రయత్నించారు. టాబ్లెట్ ఇప్పుడు ఫ్యాక్టరీ డిఫాల్ట్‌కు రీసెట్ చేయబడుతుంది."</string>
-    <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="2205435033340091883">"మీరు మీ Android TV పరికరాన్ని అన్‌లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> సార్లు విఫల ప్రయత్నాలు చేసారు. మీ Android TV పరికరం ఇప్పుడు ఫ్యాక్టరీ రీసెట్ చేయబడుతుంది."</string>
+    <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="2205435033340091883">"మీరు మీ Android TV పరికరాన్ని అన్‌లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> సార్లు విఫల ప్రయత్నాలు చేశారు. మీ Android TV పరికరం ఇప్పుడు ఫ్యాక్టరీ రీసెట్ చేయబడుతుంది."</string>
     <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="2203704707679895487">"మీరు ఫోన్‌ను అన్‌లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> సార్లు తప్పుగా ప్రయత్నించారు. ఫోన్ ఇప్పుడు ఫ్యాక్టరీ డిఫాల్ట్‌కు రీసెట్ చేయబడుతుంది."</string>
     <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6807200118164539589">"<xliff:g id="NUMBER">%d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
     <string name="lockscreen_forgot_pattern_button_text" msgid="8362442730606839031">"నమూనాను మర్చిపోయారా?"</string>
     <string name="lockscreen_glogin_forgot_pattern" msgid="9218940117797602518">"ఖాతా అన్‌లాక్"</string>
-    <string name="lockscreen_glogin_too_many_attempts" msgid="3775904917743034195">"చాలా ఎక్కువ ఆకృతి ప్రయత్నాలు చేసారు"</string>
+    <string name="lockscreen_glogin_too_many_attempts" msgid="3775904917743034195">"చాలా ఎక్కువ ఆకృతి ప్రయత్నాలు చేశారు"</string>
     <string name="lockscreen_glogin_instructions" msgid="4695162942525531700">"అన్‌లాక్ చేయడానికి, మీ Google ఖాతాతో సైన్ ఇన్ చేయండి."</string>
     <string name="lockscreen_glogin_username_hint" msgid="6916101478673157045">"వినియోగదారు పేరు (ఇమెయిల్)"</string>
     <string name="lockscreen_glogin_password_hint" msgid="3031027901286812848">"పాస్‌వర్డ్"</string>
@@ -1012,7 +1012,7 @@
     <string name="permlab_setAlarm" msgid="1158001610254173567">"అలారం సెట్ చేయడం"</string>
     <string name="permdesc_setAlarm" msgid="2185033720060109640">"ఇన్‌స్టాల్ చేయబడిన అలారం గడియారం యాప్‌లో అలారంను సెట్ చేయడానికి యాప్‌ను అనుమతిస్తుంది. కొన్ని అలారం గల గడియారం యాప్‌లు ఈ ఫీచర్‌ను అమలు చేయకపోవచ్చు."</string>
     <string name="permlab_addVoicemail" msgid="4770245808840814471">"వాయిస్ మెయిల్‌ను జోడించడం"</string>
-    <string name="permdesc_addVoicemail" msgid="5470312139820074324">"మీ వాయిస్ మెయిల్ ఇన్‌బాక్స్‌కి సందేశాలను జోడించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
+    <string name="permdesc_addVoicemail" msgid="5470312139820074324">"మీ వాయిస్ మెయిల్ ఇన్‌బాక్స్‌కి మెసేజ్‌లను జోడించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="8605631647492879449">"బ్రౌజర్ భౌగోళిక స్థానం అనుమతులను సవరించడం"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="5817346421222227772">"బ్రౌజర్ యొక్క భౌగోళిక స్థానం అనుమతులను సవరించడానికి యాప్‌ను అనుమతిస్తుంది. హానికరమైన యాప్‌లు ఏకపక్ష వెబ్ సైట్‌లకు స్థాన సమాచారాన్ని అనుమతించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
     <string name="save_password_message" msgid="2146409467245462965">"మీరు బ్రౌజర్ ఈ పాస్‌వర్డ్‌ను గుర్తుపెట్టుకోవాలని కోరుకుంటున్నారా?"</string>
@@ -1041,13 +1041,13 @@
     <string name="search_go" msgid="2141477624421347086">"సెర్చ్"</string>
     <string name="search_hint" msgid="455364685740251925">"వెతుకు..."</string>
     <string name="searchview_description_search" msgid="1045552007537359343">"సెర్చ్"</string>
-    <string name="searchview_description_query" msgid="7430242366971716338">"ప్రశ్నను శోధించండి"</string>
+    <string name="searchview_description_query" msgid="7430242366971716338">"ప్రశ్నను వెతకండి"</string>
     <string name="searchview_description_clear" msgid="1989371719192982900">"ప్రశ్నను క్లియర్ చేయి"</string>
     <string name="searchview_description_submit" msgid="6771060386117334686">"ప్రశ్నని సమర్పించండి"</string>
     <string name="searchview_description_voice" msgid="42360159504884679">"వాయిస్ శోధన"</string>
     <string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"తాకడం ద్వారా విశ్లేషణను ప్రారంభించాలా?"</string>
-    <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> తాకడం ద్వారా విశ్లేషణను ప్రారంభించాలనుకుంటోంది. తాకడం ద్వారా విశ్లేషణను ఆన్ చేసినప్పుడు, మీరు మీ వేలి క్రింద ఉన్నవాటి యొక్క వివరణలను వినవచ్చు లేదా చూడవచ్చు లేదా టాబ్లెట్‌తో పరస్పర చర్య చేయడానికి సంజ్ఞలు చేయవచ్చు."</string>
-    <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> తాకడం ద్వారా విశ్లేషణను ప్రారంభించాలనుకుంటోంది. తాకడం ద్వారా విశ్లేషణ ఆన్ చేయబడినప్పుడు, మీరు మీ వేలి క్రింద ఉన్నవాటి యొక్క వివరణలను వినవచ్చు లేదా చూడవచ్చు లేదా ఫోన్‌తో పరస్పర చర్య చేయడానికి సంజ్ఞలు చేయవచ్చు."</string>
+    <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> తాకడం ద్వారా విశ్లేషణను ప్రారంభించాలనుకుంటోంది. తాకడం ద్వారా విశ్లేషణను ఆన్ చేసినప్పుడు, మీరు మీ వేలి కింద ఉన్నవాటి యొక్క వివరణలను వినవచ్చు లేదా చూడవచ్చు లేదా టాబ్లెట్‌తో పరస్పర చర్య చేయడానికి సంజ్ఞలు చేయవచ్చు."</string>
+    <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> తాకడం ద్వారా విశ్లేషణను ప్రారంభించాలనుకుంటోంది. తాకడం ద్వారా విశ్లేషణ ఆన్ చేయబడినప్పుడు, మీరు మీ వేలి కింద ఉన్నవాటి యొక్క వివరణలను వినవచ్చు లేదా చూడవచ్చు లేదా ఫోన్‌తో పరస్పర చర్య చేయడానికి సంజ్ఞలు చేయవచ్చు."</string>
     <string name="oneMonthDurationPast" msgid="4538030857114635777">"1 నెల క్రితం"</string>
     <string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"1 నెలకు ముందు"</string>
     <plurals name="last_num_days" formatted="false" msgid="687443109145393632">
@@ -1258,7 +1258,7 @@
     <string name="app_upgrading_toast" msgid="1016267296049455585">"<xliff:g id="APPLICATION">%1$s</xliff:g>ని అప్‌గ్రేడ్ చేస్తోంది…"</string>
     <string name="android_upgrading_apk" msgid="1339564803894466737">"<xliff:g id="NUMBER_1">%2$d</xliff:g>లో <xliff:g id="NUMBER_0">%1$d</xliff:g> యాప్‌ను అనుకూలీకరిస్తోంది."</string>
     <string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g>ని సిద్ధం చేస్తోంది."</string>
-    <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"అనువర్తనాలను ప్రారంభిస్తోంది."</string>
+    <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"యాప్‌లను ప్రారంభిస్తోంది."</string>
     <string name="android_upgrading_complete" msgid="409800058018374746">"బూట్‌ను ముగిస్తోంది."</string>
     <string name="fp_enrollment_powerbutton_intent_title" msgid="3385634173366119903">"స్క్రీన్‌ను ఆఫ్ చేయాలా?"</string>
     <string name="fp_enrollment_powerbutton_intent_message" msgid="6582149052513682522">"మీ వేలిముద్రను సెటప్ చేస్తున్నప్పుడు, మీరు పవర్ బటన్‌ను నొక్కారు.\n\nఇది సాధారణంగా మీ స్క్రీన్‌ను ఆఫ్ చేస్తుంది."</string>
@@ -1325,11 +1325,11 @@
     <string name="accept" msgid="5447154347815825107">"ఆమోదిస్తున్నాను"</string>
     <string name="decline" msgid="6490507610282145874">"తిరస్కరిస్తున్నాను"</string>
     <string name="select_character" msgid="3352797107930786979">"అక్షరాన్ని చొప్పించండి"</string>
-    <string name="sms_control_title" msgid="4748684259903148341">"SMS సందేశాలు పంపుతోంది"</string>
-    <string name="sms_control_message" msgid="6574313876316388239">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; పెద్ద సంఖ్యలో SMS సందేశాలను పంపుతోంది. సందేశాలను పంపడం కొనసాగించడానికి మీరు ఈ యాప్‌ను అనుమతించాలనుకుంటున్నారా?"</string>
+    <string name="sms_control_title" msgid="4748684259903148341">"SMS మెసేజ్‌లు పంపుతోంది"</string>
+    <string name="sms_control_message" msgid="6574313876316388239">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; పెద్ద సంఖ్యలో SMS మెసేజ్‌లను పంపుతోంది. మెసేజ్‌లను పంపడం కొనసాగించడానికి మీరు ఈ యాప్‌ను అనుమతించాలనుకుంటున్నారా?"</string>
     <string name="sms_control_yes" msgid="4858845109269524622">"అనుమతిస్తున్నాను"</string>
     <string name="sms_control_no" msgid="4845717880040355570">"తిరస్కరిస్తున్నాను"</string>
-    <string name="sms_short_code_confirm_message" msgid="1385416688897538724">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ఒక సందేశాన్ని &lt;b&gt;<xliff:g id="DEST_ADDRESS">%2$s</xliff:g>&lt;/b&gt;కి పంపాలనుకుంటోంది."</string>
+    <string name="sms_short_code_confirm_message" msgid="1385416688897538724">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ఒక మెసేజ్‌ను &lt;b&gt;<xliff:g id="DEST_ADDRESS">%2$s</xliff:g>&lt;/b&gt;కి పంపాలనుకుంటోంది."</string>
     <string name="sms_short_code_details" msgid="2723725738333388351">"దీని వలన మీ మొబైల్ ఖాతాకు "<b>"ఛార్జీలు విధించబడవచ్చు"</b>"."</string>
     <string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"దీని వలన మీ మొబైల్ ఖాతాకు ఛార్జీలు విధించబడవచ్చు."</b></string>
     <string name="sms_short_code_confirm_allow" msgid="920477594325526691">"పంపు"</string>
@@ -1384,10 +1384,10 @@
     <string name="usb_contaminant_detected_message" msgid="7346100585390795743">"USB పోర్ట్ ఆటోమేటిక్‌గా నిలిపివేయబడింది. మరింత తెలుసుకోవడానికి నొక్కండి."</string>
     <string name="usb_contaminant_not_detected_title" msgid="2651167729563264053">"USB పోర్ట్‌ను ఉపయోగించడం సురక్షితం"</string>
     <string name="usb_contaminant_not_detected_message" msgid="892863190942660462">"ఫోన్ ఇకపై ద్రవ లేదా వ్యర్థ పదార్థాలను గుర్తించదు."</string>
-    <string name="taking_remote_bugreport_notification_title" msgid="1582531382166919850">"బగ్ నివేదికను తీస్తోంది…"</string>
-    <string name="share_remote_bugreport_notification_title" msgid="6708897723753334999">"బగ్ నివేదికను భాగస్వామ్యం చేయాలా?"</string>
-    <string name="sharing_remote_bugreport_notification_title" msgid="3077385149217638550">"బగ్ నివేదికను భాగస్వామ్యం చేస్తోంది..."</string>
-    <string name="share_remote_bugreport_notification_message_finished" msgid="7325635795739260135">"మీ నిర్వాహకులు ఈ పరికరం సమస్యకు పరిష్కారాన్ని కనుగొనడంలో సహాయం కోసం బగ్ నివేదికను అభ్యర్థించారు. యాప్‌లు మరియు డేటా భాగస్వామ్యం చేయబడవచ్చు."</string>
+    <string name="taking_remote_bugreport_notification_title" msgid="1582531382166919850">"బగ్ రిపోర్ట్‌ను తీస్తోంది…"</string>
+    <string name="share_remote_bugreport_notification_title" msgid="6708897723753334999">"బగ్ రిపోర్ట్‌ను భాగస్వామ్యం చేయాలా?"</string>
+    <string name="sharing_remote_bugreport_notification_title" msgid="3077385149217638550">"బగ్ రిపోర్ట్‌ను భాగస్వామ్యం చేస్తోంది..."</string>
+    <string name="share_remote_bugreport_notification_message_finished" msgid="7325635795739260135">"మీ నిర్వాహకులు ఈ పరికరం సమస్యకు పరిష్కారాన్ని కనుగొనడంలో సహాయం కోసం బగ్ రిపోర్ట్‌ను అభ్యర్థించారు. యాప్‌లు మరియు డేటా భాగస్వామ్యం చేయబడవచ్చు."</string>
     <string name="share_remote_bugreport_action" msgid="7630880678785123682">"షేర్ చేయి"</string>
     <string name="decline_remote_bugreport_action" msgid="4040894777519784346">"తిరస్కరిస్తున్నాను"</string>
     <string name="select_input_method" msgid="3971267998568587025">"ఇన్‌పుట్ పద్ధతిని ఎంచుకోండి"</string>
@@ -1474,10 +1474,10 @@
     <string name="ime_action_previous" msgid="6548799326860401611">"మునుపటి"</string>
     <string name="ime_action_default" msgid="8265027027659800121">"అమలు చేయి"</string>
     <string name="dial_number_using" msgid="6060769078933953531">"<xliff:g id="NUMBER">%s</xliff:g>ని ఉపయోగించి\nనంబర్ డయల్ చేయండి"</string>
-    <string name="create_contact_using" msgid="6200708808003692594">"<xliff:g id="NUMBER">%s</xliff:g>ని ఉపయోగించి\nపరిచయాన్ని సృష్టించండి"</string>
-    <string name="grant_credentials_permission_message_header" msgid="5365733888842570481">"క్రింది ఒకటి లేదా అంతకంటే ఎక్కువ యాప్‌లు మీ ఖాతాను యాక్సెస్ చేయడానికి ఇప్పుడు మరియు భవిష్యత్తులో అనుమతిని అభ్యర్థించవచ్చు."</string>
-    <string name="grant_credentials_permission_message_footer" msgid="1886710210516246461">"మీరు ఈ అభ్యర్థనను అనుమతించాలనుకుంటున్నారా?"</string>
-    <string name="grant_permissions_header_text" msgid="3420736827804657201">"యాక్సెస్ అభ్యర్థన"</string>
+    <string name="create_contact_using" msgid="6200708808003692594">"<xliff:g id="NUMBER">%s</xliff:g>ని ఉపయోగించి\nకాంటాక్ట్‌ను క్రియేట్ చేయండి"</string>
+    <string name="grant_credentials_permission_message_header" msgid="5365733888842570481">"కింది ఒకటి లేదా అంతకంటే ఎక్కువ యాప్‌లు మీ ఖాతాను యాక్సెస్ చేయడానికి ఇప్పుడు మరియు భవిష్యత్తులో అనుమతిని అభ్యర్థించవచ్చు."</string>
+    <string name="grant_credentials_permission_message_footer" msgid="1886710210516246461">"మీరు ఈ రిక్వెస్ట్‌ను అనుమతించాలనుకుంటున్నారా?"</string>
+    <string name="grant_permissions_header_text" msgid="3420736827804657201">"యాక్సెస్ రిక్వెస్ట్‌"</string>
     <string name="allow" msgid="6195617008611933762">"అనుమతించండి"</string>
     <string name="deny" msgid="6632259981847676572">"తిరస్కరించండి"</string>
     <string name="permission_request_notification_title" msgid="1810025922441048273">"అనుమతి అభ్యర్థించబడింది"</string>
@@ -1486,7 +1486,7 @@
     <string name="forward_intent_to_owner" msgid="4620359037192871015">"మీరు మీ కార్యాలయ ప్రొఫైల్‌కు వెలుపల ఈ యాప్‌ను ఉపయోగిస్తున్నారు"</string>
     <string name="forward_intent_to_work" msgid="3620262405636021151">"మీరు మీ కార్యాలయ ప్రొఫైల్‌లో ఈ యాప్‌ను ఉపయోగిస్తున్నారు"</string>
     <string name="input_method_binding_label" msgid="1166731601721983656">"ఇన్‌పుట్ పద్ధతి"</string>
-    <string name="sync_binding_label" msgid="469249309424662147">"సమకాలీకరణ"</string>
+    <string name="sync_binding_label" msgid="469249309424662147">"సింక్‌"</string>
     <string name="accessibility_binding_label" msgid="1974602776545801715">"యాక్సెసిబిలిటీ"</string>
     <string name="wallpaper_binding_label" msgid="1197440498000786738">"వాల్‌పేపర్"</string>
     <string name="chooser_wallpaper" msgid="3082405680079923708">"వాల్‌పేపర్‌ను మార్చండి"</string>
@@ -1525,8 +1525,8 @@
     <string name="websearch" msgid="5624340204512793290">"వెబ్ శోధన"</string>
     <string name="find_next" msgid="5341217051549648153">"తదుపరిదాన్ని కనుగొను"</string>
     <string name="find_previous" msgid="4405898398141275532">"మునుపటిదాన్ని కనుగొను"</string>
-    <string name="gpsNotifTicker" msgid="3207361857637620780">"<xliff:g id="NAME">%s</xliff:g> నుండి స్థాన అభ్యర్థన"</string>
-    <string name="gpsNotifTitle" msgid="1590033371665669570">"స్థాన అభ్యర్థన"</string>
+    <string name="gpsNotifTicker" msgid="3207361857637620780">"<xliff:g id="NAME">%s</xliff:g> నుండి స్థాన రిక్వెస్ట్‌"</string>
+    <string name="gpsNotifTitle" msgid="1590033371665669570">"స్థాన రిక్వెస్ట్‌"</string>
     <string name="gpsNotifMessage" msgid="7346649122793758032">"<xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>) ద్వారా అభ్యర్థించబడింది"</string>
     <string name="gpsVerifYes" msgid="3719843080744112940">"అవును"</string>
     <string name="gpsVerifNo" msgid="1671201856091564741">"కాదు"</string>
@@ -1664,7 +1664,7 @@
     <string name="kg_invalid_sim_puk_hint" msgid="2539364558870734339">"PUK కోడ్ 8 సంఖ్యలు ఉండాలి."</string>
     <string name="kg_invalid_puk" msgid="4809502818518963344">"సరైన PUK కోడ్‌ను మళ్లీ నమోదు చేయండి. పునరావృత ప్రయత్నాల వలన సిమ్ శాశ్వతంగా నిలిపివేయబడుతుంది."</string>
     <string name="kg_invalid_confirm_pin_hint" product="default" msgid="4705368340409816254">"పిన్‌ కోడ్‌లు సరిపోలలేదు"</string>
-    <string name="kg_login_too_many_attempts" msgid="699292728290654121">"చాలా ఎక్కువ ఆకృతి ప్రయత్నాలు చేసారు"</string>
+    <string name="kg_login_too_many_attempts" msgid="699292728290654121">"చాలా ఎక్కువ ఆకృతి ప్రయత్నాలు చేశారు"</string>
     <string name="kg_login_instructions" msgid="3619844310339066827">"అన్‌లాక్ చేయడానికి, మీ Google ఖాతాతో సైన్ ఇన్ చేయండి."</string>
     <string name="kg_login_username_hint" msgid="1765453775467133251">"వినియోగదారు పేరు (ఇమెయిల్)"</string>
     <string name="kg_login_password_hint" msgid="3330530727273164402">"పాస్‌వర్డ్"</string>
@@ -1672,15 +1672,15 @@
     <string name="kg_login_invalid_input" msgid="8292367491901220210">"చెల్లని వినియోగదారు పేరు లేదా పాస్‌వర్డ్."</string>
     <string name="kg_login_account_recovery_hint" msgid="4892466171043541248">"మీ వినియోగదారు పేరు లేదా పాస్‌వర్డ్‌ను మర్చిపోయారా?\n"<b>"google.com/accounts/recovery"</b>"ని సందర్శించండి."</string>
     <string name="kg_login_checking_password" msgid="4676010303243317253">"ఖాతాను తనిఖీ చేస్తోంది…"</string>
-    <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="23741434207544038">"మీరు మీ పిన్‌ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేసారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
-    <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="3328686432962224215">"మీరు మీ పాస్‌వర్డ్‌ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేసారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
+    <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="23741434207544038">"మీరు మీ పిన్‌ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేశారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
+    <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="3328686432962224215">"మీరు మీ పాస్‌వర్డ్‌ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేశారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
     <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="7357404233979139075">"మీరు మీ అన్‌లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
-    <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="3479940221343361587">"మీరు టాబ్లెట్‌ను అన్‌లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> చెల్లని ప్రయత్నాలు చేసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, టాబ్లెట్ ఫ్యాక్టరీ డిఫాల్ట్‌కు రీసెట్ చేయబడుతుంది మరియు మొత్తం వినియోగదారు డేటాను కోల్పోవడం సంభవిస్తుంది."</string>
-    <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="9064457748587850217">"మీరు మీ Android TV పరికరాన్ని అన్‌లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు విఫల ప్రయత్నాలు చేసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, మీ Android TV పరికరం ఫ్యాక్టరీ డిఫాల్ట్‌కి రీసెట్ చేయబడుతుంది, అలాగే వినియోగదారు డేటా మొత్తాన్ని కోల్పోతారు."</string>
-    <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="5955398963754432548">"మీరు ఫోన్‌ను అన్‌లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> చెల్లని ప్రయత్నాలు చేసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, ఫోన్ ఫ్యాక్టరీ డిఫాల్ట్‌కు రీసెట్ చేయబడుతుంది మరియు మొత్తం వినియోగదారు డేటాను కోల్పోవడం సంభవిస్తుంది."</string>
-    <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2299099385175083308">"మీరు టాబ్లెట్‌ను అన్‌లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> చెల్లని ప్రయత్నాలు చేసారు. టాబ్లెట్ ఇప్పుడు ఫ్యాక్టరీ డిఫాల్ట్‌కు రీసెట్ చేయబడుతుంది."</string>
-    <string name="kg_failed_attempts_now_wiping" product="tv" msgid="5045460916106267585">"మీరు మీ Android TV పరికరాన్ని అన్‌లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> సార్లు విఫల ప్రయత్నాలు చేసారు. మీ Android TV పరికరం ఇప్పుడు ఫ్యాక్టరీ రీసెట్ చేయబడుతుంది."</string>
-    <string name="kg_failed_attempts_now_wiping" product="default" msgid="5043730590446071189">"మీరు ఫోన్‌ను అన్‌లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> చెల్లని ప్రయత్నాలు చేసారు. ఫోన్ ఇప్పుడు ఫ్యాక్టరీ డిఫాల్ట్‌కు రీసెట్ చేయబడుతుంది."</string>
+    <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="3479940221343361587">"మీరు టాబ్లెట్‌ను అన్‌లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> చెల్లని ప్రయత్నాలు చేశారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, టాబ్లెట్ ఫ్యాక్టరీ డిఫాల్ట్‌కు రీసెట్ చేయబడుతుంది మరియు మొత్తం వినియోగదారు డేటాను కోల్పోవడం సంభవిస్తుంది."</string>
+    <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="9064457748587850217">"మీరు మీ Android TV పరికరాన్ని అన్‌లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు విఫల ప్రయత్నాలు చేశారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, మీ Android TV పరికరం ఫ్యాక్టరీ డిఫాల్ట్‌కి రీసెట్ చేయబడుతుంది, అలాగే వినియోగదారు డేటా మొత్తాన్ని కోల్పోతారు."</string>
+    <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="5955398963754432548">"మీరు ఫోన్‌ను అన్‌లాక్ చేయడానికి <xliff:g id="NUMBER_0">%1$d</xliff:g> చెల్లని ప్రయత్నాలు చేశారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, ఫోన్ ఫ్యాక్టరీ డిఫాల్ట్‌కు రీసెట్ చేయబడుతుంది మరియు మొత్తం వినియోగదారు డేటాను కోల్పోవడం సంభవిస్తుంది."</string>
+    <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2299099385175083308">"మీరు టాబ్లెట్‌ను అన్‌లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> చెల్లని ప్రయత్నాలు చేశారు. టాబ్లెట్ ఇప్పుడు ఫ్యాక్టరీ డిఫాల్ట్‌కు రీసెట్ చేయబడుతుంది."</string>
+    <string name="kg_failed_attempts_now_wiping" product="tv" msgid="5045460916106267585">"మీరు మీ Android TV పరికరాన్ని అన్‌లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> సార్లు విఫల ప్రయత్నాలు చేశారు. మీ Android TV పరికరం ఇప్పుడు ఫ్యాక్టరీ రీసెట్ చేయబడుతుంది."</string>
+    <string name="kg_failed_attempts_now_wiping" product="default" msgid="5043730590446071189">"మీరు ఫోన్‌ను అన్‌లాక్ చేయడానికి <xliff:g id="NUMBER">%d</xliff:g> చెల్లని ప్రయత్నాలు చేశారు. ఫోన్ ఇప్పుడు ఫ్యాక్టరీ డిఫాల్ట్‌కు రీసెట్ చేయబడుతుంది."</string>
     <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="7086799295109717623">"మీరు మీ అన్‌లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, ఇమెయిల్ ఖాతాను ఉపయోగించి మీ టాబ్లెట్‌ను అన్‌లాక్ చేయాల్సిందిగా మిమ్మల్ని అడుగుతారు.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
     <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"మీరు మీ అన్‌లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీశారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత మీ Android TV పరికరాన్ని ఇమెయిల్ ఖాతా ద్వారా అన్‌లాక్ చేయాల్సిందిగా మిమ్మల్ని కోరడం జరుగుతుంది.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"మీరు మీ అన్‌లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. మరో <xliff:g id="NUMBER_1">%2$d</xliff:g> విఫల ప్రయత్నాల తర్వాత, ఇమెయిల్ ఖాతాను ఉపయోగించి మీ ఫోన్‌ను అన్‌లాక్ చేయాల్సిందిగా మిమ్మల్ని అడుగుతారు.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
@@ -1713,8 +1713,8 @@
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ఆఫ్ చేయబడింది"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"షార్ట్‌కట్‌లను ఎడిట్ చేయి"</string>
     <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"పూర్తయింది"</string>
-    <string name="disable_accessibility_shortcut" msgid="5806091378745232383">"సత్వరమార్గాన్ని ఆఫ్ చేయి"</string>
-    <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"సత్వరమార్గాన్ని ఉపయోగించు"</string>
+    <string name="disable_accessibility_shortcut" msgid="5806091378745232383">"షార్ట్‌కట్‌ను ఆఫ్ చేయి"</string>
+    <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"షార్ట్‌కట్‌ను ఉపయోగించు"</string>
     <string name="color_inversion_feature_name" msgid="326050048927789012">"కలర్ మార్పిడి"</string>
     <string name="color_correction_feature_name" msgid="3655077237805422597">"కలర్ సరిచేయడం"</string>
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"వన్-హ్యాండెడ్ మోడ్"</string>
@@ -1869,7 +1869,7 @@
     <string name="lock_to_app_unlock_pin" msgid="3890940811866290782">"అన్‌పిన్ చేయడానికి ముందు పిన్‌ కోసం అడుగు"</string>
     <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"అన్‌పిన్ చేయడానికి ముందు అన్‌లాక్ ఆకృతి కోసం అడుగు"</string>
     <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"అన్‌పిన్ చేయడానికి ముందు పాస్‌వర్డ్ కోసం అడుగు"</string>
-    <string name="package_installed_device_owner" msgid="7035926868974878525">"మీ నిర్వాహకులు ఇన్‌స్టాల్ చేసారు"</string>
+    <string name="package_installed_device_owner" msgid="7035926868974878525">"మీ నిర్వాహకులు ఇన్‌స్టాల్ చేశారు"</string>
     <string name="package_updated_device_owner" msgid="7560272363805506941">"మీ నిర్వాహకులు నవీకరించారు"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"మీ నిర్వాహకులు తొలగించారు"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"సరే"</string>
@@ -1926,14 +1926,14 @@
     <string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> కొన్ని ధ్వనులను మ్యూట్ చేస్తోంది"</string>
     <string name="system_error_wipe_data" msgid="5910572292172208493">"మీ పరికరంతో అంతర్గత సమస్య ఏర్పడింది మరియు మీరు ఫ్యాక్టరీ డేటా రీసెట్ చేసే వరకు అస్థిరంగా ఉంటుంది."</string>
     <string name="system_error_manufacturer" msgid="703545241070116315">"మీ పరికరంతో అంతర్గత సమస్య ఏర్పడింది. వివరాల కోసం మీ తయారీదారుని సంప్రదించండి."</string>
-    <string name="stk_cc_ussd_to_dial" msgid="3139884150741157610">"USSD అభ్యర్థన సాధారణ కాల్‌కు మార్చబడింది"</string>
-    <string name="stk_cc_ussd_to_ss" msgid="4826846653052609738">"USSD అభ్యర్థన SS అభ్యర్థనకు మార్చబడింది"</string>
-    <string name="stk_cc_ussd_to_ussd" msgid="8343001461299302472">"కొత్త USSD అభ్యర్థనకు మార్చబడింది"</string>
-    <string name="stk_cc_ussd_to_dial_video" msgid="429118590323618623">"USSD అభ్యర్థన వీడియో కాల్‌కు మార్చబడింది"</string>
-    <string name="stk_cc_ss_to_dial" msgid="4087396658768717077">"SS అభ్యర్థన సాధారణ కాల్‌కి మార్చబడింది"</string>
-    <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS అభ్యర్థన వీడియో కాల్‌కి మార్చబడింది"</string>
-    <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS అభ్యర్థన USSD అభ్యర్థనకు మార్చబడింది"</string>
-    <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"కొత్త SS అభ్యర్థనకు మార్చబడింది"</string>
+    <string name="stk_cc_ussd_to_dial" msgid="3139884150741157610">"USSD రిక్వెస్ట్‌ సాధారణ కాల్‌కు మార్చబడింది"</string>
+    <string name="stk_cc_ussd_to_ss" msgid="4826846653052609738">"USSD రిక్వెస్ట్‌ SS రిక్వెస్ట్‌కు మార్చబడింది"</string>
+    <string name="stk_cc_ussd_to_ussd" msgid="8343001461299302472">"కొత్త USSD రిక్వెస్ట్‌కు మార్చబడింది"</string>
+    <string name="stk_cc_ussd_to_dial_video" msgid="429118590323618623">"USSD రిక్వెస్ట్‌ వీడియో కాల్‌కు మార్చబడింది"</string>
+    <string name="stk_cc_ss_to_dial" msgid="4087396658768717077">"SS రిక్వెస్ట్‌ సాధారణ కాల్‌కి మార్చబడింది"</string>
+    <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"SS రిక్వెస్ట్‌ వీడియో కాల్‌కి మార్చబడింది"</string>
+    <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"SS రిక్వెస్ట్‌ USSD రిక్వెస్ట్‌కు మార్చబడింది"</string>
+    <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"కొత్త SS రిక్వెస్ట్‌కు మార్చబడింది"</string>
     <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"ఫిషింగ్ అలర్ట్"</string>
     <string name="notification_work_profile_content_description" msgid="5296477955677725799">"ఆఫీస్ ప్రొఫైల్‌"</string>
     <string name="notification_alerted_content_description" msgid="6139691253611265992">"హెచ్చరించబడింది"</string>
@@ -1961,7 +1961,7 @@
       <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> ఎంచుకోబడింది</item>
     </plurals>
     <string name="default_notification_channel_label" msgid="3697928973567217330">"వర్గీకరించబడలేదు"</string>
-    <string name="importance_from_user" msgid="2782756722448800447">"మీరు ఈ నోటిఫికేషన్‌ల ప్రాముఖ్యతను సెట్ చేసారు."</string>
+    <string name="importance_from_user" msgid="2782756722448800447">"మీరు ఈ నోటిఫికేషన్‌ల ప్రాముఖ్యతను సెట్ చేశారు."</string>
     <string name="importance_from_person" msgid="4235804979664465383">"ఇందులో పేర్కొనబడిన వ్యక్తులను బట్టి ఇది చాలా ముఖ్యమైనది."</string>
     <string name="notification_history_title_placeholder" msgid="7748630986182249599">"అనుకూల యాప్ నోటిఫికేషన్"</string>
     <string name="user_creation_account_exists" msgid="2239146360099708035">"<xliff:g id="ACCOUNT">%2$s</xliff:g>తో కొత్త వినియోగదారుని సృష్టించడానికి <xliff:g id="APP">%1$s</xliff:g>ను అనుమతించాలా (ఈ ఖాతాతో ఇప్పటికే ఒక వినియోగదారు ఉన్నారు) ?"</string>
@@ -1984,13 +1984,13 @@
     <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ప్రస్తుతం అందుబాటులో లేదు."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"ఈ యాప్ పాత వెర్షన్ Android కోసం రూపొందించబడింది మరియు అది సరిగ్గా పని చేయకపోవచ్చు. అప్‌డేట్‌ల కోసం తనిఖీ చేయడానికి ప్రయత్నించండి లేదా డెవలపర్‌ని సంప్రదించండి."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"అప్‌డేట్ కోసం తనిఖీ చేయండి"</string>
-    <string name="new_sms_notification_title" msgid="6528758221319927107">"మీకు కొత్త సందేశాలు ఉన్నాయి"</string>
+    <string name="new_sms_notification_title" msgid="6528758221319927107">"మీకు కొత్త మెసేజ్‌లు ఉన్నాయి"</string>
     <string name="new_sms_notification_content" msgid="3197949934153460639">"వీక్షించడానికి SMS యాప్‌ను తెరవండి"</string>
     <string name="profile_encrypted_title" msgid="9001208667521266472">"కొంత ఫంక్షనాలిటీ పరిమితం కావచ్చు"</string>
     <string name="profile_encrypted_detail" msgid="5279730442756849055">"కార్యాలయ ప్రొఫైల్ లాక్ అయింది"</string>
     <string name="profile_encrypted_message" msgid="1128512616293157802">"కార్యాలయ ప్రొఫైల్ అన్‌లాక్ చేయుటకు నొక్కండి"</string>
     <string name="usb_mtp_launch_notification_title" msgid="774319638256707227">"<xliff:g id="PRODUCT_NAME">%1$s</xliff:g>కి కనెక్ట్ చేయబడింది"</string>
-    <string name="usb_mtp_launch_notification_description" msgid="6942535713629852684">"ఫైల్‌లను వీక్షించడానికి నొక్కండి"</string>
+    <string name="usb_mtp_launch_notification_description" msgid="6942535713629852684">"ఫైళ్లను వీక్షించడానికి నొక్కండి"</string>
     <string name="pin_target" msgid="8036028973110156895">"పిన్ చేయి"</string>
     <string name="pin_specific_target" msgid="7824671240625957415">"<xliff:g id="LABEL">%1$s</xliff:g>ను పిన్ చేయండి"</string>
     <string name="unpin_target" msgid="3963318576590204447">"అన్‌‌పిన్‌ ‌చేయి"</string>
@@ -2041,20 +2041,20 @@
     <string name="autofill_save_notnow" msgid="2853932672029024195">"ఇప్పుడు కాదు"</string>
     <string name="autofill_save_never" msgid="6821841919831402526">"ఎప్పుడూ వద్దు"</string>
     <string name="autofill_update_yes" msgid="4608662968996874445">"అప్‌డేట్ చేయి"</string>
-    <string name="autofill_continue_yes" msgid="7914985605534510385">"కొనసాగించు"</string>
+    <string name="autofill_continue_yes" msgid="7914985605534510385">"కొనసాగించండి"</string>
     <string name="autofill_save_type_password" msgid="5624528786144539944">"పాస్‌వర్డ్"</string>
-    <string name="autofill_save_type_address" msgid="3111006395818252885">"చిరునామా"</string>
+    <string name="autofill_save_type_address" msgid="3111006395818252885">"అడ్రస్‌"</string>
     <string name="autofill_save_type_credit_card" msgid="3583795235862046693">"క్రెడిట్ కార్డ్"</string>
     <string name="autofill_save_type_debit_card" msgid="3169397504133097468">"డెబిట్ కార్డ్"</string>
     <string name="autofill_save_type_payment_card" msgid="6555012156728690856">"చెల్లింపు కార్డ్"</string>
     <string name="autofill_save_type_generic_card" msgid="1019367283921448608">"కార్డ్"</string>
     <string name="autofill_save_type_username" msgid="1018816929884640882">"వినియోగదారు పేరు"</string>
-    <string name="autofill_save_type_email_address" msgid="1303262336895591924">"ఇమెయిల్ చిరునామా"</string>
+    <string name="autofill_save_type_email_address" msgid="1303262336895591924">"ఇమెయిల్ అడ్రస్‌"</string>
     <string name="etws_primary_default_message_earthquake" msgid="8401079517718280669">"ప్రశాంతంగా ఉండండి మరియు దగ్గర్లో తలదాచుకోండి."</string>
     <string name="etws_primary_default_message_tsunami" msgid="5828171463387976279">"వెంటనే తీర ప్రాంతాలు మరియు నదీ పరీవాహక ప్రాంతాలను ఖాళీ చేసి మెట్ట ప్రాంతాలకు తరలి వెళ్లండి."</string>
     <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="4888224011071875068">"ప్రశాంతంగా ఉండండి మరియు దగ్గర్లో తలదాచుకోండి."</string>
-    <string name="etws_primary_default_message_test" msgid="4583367373909549421">"అత్యవసర సందేశాల పరీక్ష"</string>
-    <string name="notification_reply_button_accessibility" msgid="5235776156579456126">"ప్రత్యుత్తరం పంపండి"</string>
+    <string name="etws_primary_default_message_test" msgid="4583367373909549421">"అత్యవసర మెసేజ్‌ల పరీక్ష"</string>
+    <string name="notification_reply_button_accessibility" msgid="5235776156579456126">"రిప్లయి పంపండి"</string>
     <string name="etws_primary_default_message_others" msgid="7958161706019130739"></string>
     <string name="mmcc_authentication_reject" msgid="4891965994643876369">"వాయిస్ కోసం SIM అనుమతించబడదు"</string>
     <string name="mmcc_imsi_unknown_in_hlr" msgid="227760698553988751">"వాయిస్ కోసం SIM సదుపాయం లేదు"</string>
@@ -2066,18 +2066,18 @@
     <string name="mmcc_illegal_me_msim_template" msgid="4802735138861422802">"SIM <xliff:g id="SIMNUMBER">%d</xliff:g> అనుమతించబడదు"</string>
     <string name="popup_window_default_title" msgid="6907717596694826919">"పాప్అప్ విండో"</string>
     <string name="slice_more_content" msgid="3377367737876888459">"+ <xliff:g id="NUMBER">%1$d</xliff:g>"</string>
-    <string name="shortcut_restored_on_lower_version" msgid="9206301954024286063">"యాప్ వెర్షన్ డౌన్‌గ్రేడ్ చేయబడింది లేదా ఈ సత్వరమార్గంతో అనుకూలంగా లేదు"</string>
-    <string name="shortcut_restore_not_supported" msgid="4763198938588468400">"బ్యాకప్ మరియు పునరుద్ధరణకు యాప్ మద్దతు ఇవ్వని కారణంగా సత్వరమార్గాన్ని పునరుద్ధరించడం సాధ్యపడలేదు"</string>
-    <string name="shortcut_restore_signature_mismatch" msgid="579345304221605479">"యాప్ సంతకం సరిపోలని కారణంగా సత్వరమార్గాన్ని పునరుద్ధరించడం సాధ్యపడలేదు"</string>
-    <string name="shortcut_restore_unknown_issue" msgid="2478146134395982154">"సత్వరమార్గాన్ని పునరుద్ధరించడం సాధ్యపడలేదు"</string>
+    <string name="shortcut_restored_on_lower_version" msgid="9206301954024286063">"యాప్ వెర్షన్ డౌన్‌గ్రేడ్ చేయబడింది లేదా ఈ షార్ట్‌కట్‌తో అనుకూలంగా లేదు"</string>
+    <string name="shortcut_restore_not_supported" msgid="4763198938588468400">"బ్యాకప్ మరియు పునరుద్ధరణకు యాప్ మద్దతు ఇవ్వని కారణంగా షార్ట్‌కట్‌ను పునరుద్ధరించడం సాధ్యపడలేదు"</string>
+    <string name="shortcut_restore_signature_mismatch" msgid="579345304221605479">"యాప్ సంతకం సరిపోలని కారణంగా షార్ట్‌కట్‌ను పునరుద్ధరించడం సాధ్యపడలేదు"</string>
+    <string name="shortcut_restore_unknown_issue" msgid="2478146134395982154">"షార్ట్‌కట్‌ను పునరుద్ధరించడం సాధ్యపడలేదు"</string>
     <string name="shortcut_disabled_reason_unknown" msgid="753074793553599166">"షార్ట్‌కట్ నిలిపివేయబడింది"</string>
     <string name="harmful_app_warning_uninstall" msgid="6472912975664191772">"అన్ఇన్‌స్టాల్ చేయండి"</string>
     <string name="harmful_app_warning_open_anyway" msgid="5963657791740211807">"ఏదేమైనా తెరువు"</string>
     <string name="harmful_app_warning_title" msgid="8794823880881113856">"హానికరమైన యాప్ గుర్తించబడింది"</string>
     <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> <xliff:g id="APP_2">%2$s</xliff:g> స్లైస్‌లను చూపించాలనుకుంటోంది"</string>
     <string name="screenshot_edit" msgid="7408934887203689207">"ఎడిట్ చేయండి"</string>
-    <string name="volume_dialog_ringer_guidance_vibrate" msgid="2055927873175228519">"కాల్‌లు మరియు నోటిఫికేషన్‌లు వైబ్రేట్ అవుతాయి"</string>
-    <string name="volume_dialog_ringer_guidance_silent" msgid="1011246774949993783">"కాల్‌లు మరియు నోటిఫికేషన్‌లు మ్యూట్ చేయబడతాయి"</string>
+    <string name="volume_dialog_ringer_guidance_vibrate" msgid="2055927873175228519">"కాల్స్‌ మరియు నోటిఫికేషన్‌లు వైబ్రేట్ అవుతాయి"</string>
+    <string name="volume_dialog_ringer_guidance_silent" msgid="1011246774949993783">"కాల్స్‌ మరియు నోటిఫికేషన్‌లు మ్యూట్ చేయబడతాయి"</string>
     <string name="notification_channel_system_changes" msgid="2462010596920209678">"సిస్టమ్ మార్పులు"</string>
     <string name="notification_channel_do_not_disturb" msgid="7832584281883687653">"అంతరాయం కలిగించవద్దు"</string>
     <string name="zen_upgrade_notification_visd_title" msgid="2001148984371968620">"కొత్తది: అంతరాయం కలిగించవద్దు నోటిఫికేషన్‌లను దాస్తోంది"</string>
@@ -2099,7 +2099,7 @@
     <string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"సరే"</string>
     <string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"ఆఫ్ చేయండి"</string>
     <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"మరింత తెలుసుకోండి"</string>
-    <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Android 12లో Android అనుకూల నోటిఫికేషన్‌లను, మెరుగైన నోటిఫికేషన్‌లు భర్తీ చేశాయి. సూచించిన చర్యలు, రిప్లయిలను ఈ ఫీచర్ చూపించి, మీ నోటిఫికేషన్‌లను ఆర్గనైజ్ చేస్తుంది.\n\nకాంటాక్ట్ పేర్లు, మెసేజ్‌లు లాంటి వ్యక్తిగత సమాచారంతో సహా నోటిఫికేషన్ కంటెంట్‌ను మెరుగైన నోటిఫికేషన్‌లు యాక్సెస్ చేయవచ్చు. ఫోన్ కాల్‌లకు సమాధానమివ్వడం, \'అంతరాయం కలిగించవద్దు\' ఆప్షన్‌ను కంట్రోల్ చేయడం లాంటి నోటిఫికేషన్‌లను విస్మరించడం లేదా ప్రతిస్పందించడం కూడా ఈ ఫీచర్ చేయగలదు."</string>
+    <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Android 12లో Android అనుకూల నోటిఫికేషన్‌లను, మెరుగైన నోటిఫికేషన్‌లు భర్తీ చేశాయి. సూచించిన చర్యలు, రిప్లయిలను ఈ ఫీచర్ చూపించి, మీ నోటిఫికేషన్‌లను ఆర్గనైజ్ చేస్తుంది.\n\nకాంటాక్ట్ పేర్లు, మెసేజ్‌లు లాంటి వ్యక్తిగత సమాచారంతో సహా నోటిఫికేషన్ కంటెంట్‌ను మెరుగైన నోటిఫికేషన్‌లు యాక్సెస్ చేయవచ్చు. ఫోన్ కాల్స్‌కు సమాధానమివ్వడం, \'అంతరాయం కలిగించవద్దు\' ఆప్షన్‌ను కంట్రోల్ చేయడం లాంటి నోటిఫికేషన్‌లను విస్మరించడం లేదా ప్రతిస్పందించడం కూడా ఈ ఫీచర్ చేయగలదు."</string>
     <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"రొటీన్ మోడ్ సమాచార నోటిఫికేషన్"</string>
     <string name="dynamic_mode_notification_title" msgid="9205715501274608016">"మామూలుగా ఛార్జ్ చేసేలోపు బ్యాటరీ ఖాళీ కావచ్చు"</string>
     <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"బ్యాటరీ జీవితకాలాన్ని పెంచడానికి బ్యాటరీ సేవర్ యాక్టివేట్ చేయబడింది"</string>
@@ -2129,11 +2129,11 @@
     <string name="bluetooth_airplane_mode_toast" msgid="2066399056595768554">"విమానం మోడ్‌లో బ్లూటూత్ ఆన్‌లో ఉంటుంది"</string>
     <string name="car_loading_profile" msgid="8219978381196748070">"లోడవుతోంది"</string>
     <plurals name="file_count" formatted="false" msgid="7063513834724389247">
-      <item quantity="other"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> ఫైల్‌లు</item>
+      <item quantity="other"><xliff:g id="FILE_NAME_2">%s</xliff:g> + <xliff:g id="COUNT_3">%d</xliff:g> ఫైళ్లు</item>
       <item quantity="one"><xliff:g id="FILE_NAME_0">%s</xliff:g> + <xliff:g id="COUNT_1">%d</xliff:g> ఫైల్</item>
     </plurals>
     <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"ఎవరికి షేర్ చేయాలనే దానికి సంబంధించి సిఫార్సులేవీ లేవు"</string>
-    <string name="chooser_all_apps_button_label" msgid="3230427756238666328">"యాప్‌ల జాబితా"</string>
+    <string name="chooser_all_apps_button_label" msgid="3230427756238666328">"యాప్‌ల లిస్ట్‌"</string>
     <string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"ఈ యాప్‌కు రికార్డ్ చేసే అనుమతి మంజూరు కాలేదు, అయినా ఈ USB పరికరం ద్వారా ఆడియోను క్యాప్చర్ చేయగలదు."</string>
     <string name="accessibility_system_action_home_label" msgid="3234748160850301870">"హోమ్"</string>
     <string name="accessibility_system_action_back_label" msgid="4205361367345537608">"వెనుకకు"</string>
@@ -2225,17 +2225,17 @@
     <string name="PERSOSUBSTATE_RUIM_CORPORATE_PUK_IN_PROGRESS" msgid="2695664012344346788">"PUK అన్‌లాక్‌ను అభ్యర్థిస్తోంది…"</string>
     <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK_IN_PROGRESS" msgid="2695678959963807782">"PUK అన్‌లాక్‌ను అభ్యర్థిస్తోంది…"</string>
     <string name="PERSOSUBSTATE_RUIM_RUIM_PUK_IN_PROGRESS" msgid="1230605365926493599">"PUK అన్‌లాక్‌ను అభ్యర్థిస్తోంది…"</string>
-    <string name="PERSOSUBSTATE_SIM_NETWORK_ERROR" msgid="1924844017037151535">"SIM నెట్‌వర్క్ అన్‌లాక్ అభ్యర్థన విఫలమైంది."</string>
-    <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_ERROR" msgid="3372797822292089708">"SIM నెట్‌వర్క్ సబ్‌సెట్ అన్‌లాక్ అభ్యర్థన విఫలమైంది."</string>
-    <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_ERROR" msgid="1878443146720411381">"SIM సర్వీస్ ప్రొవైడర్ అన్‌లాక్ అభ్యర్థన విఫలమైంది."</string>
-    <string name="PERSOSUBSTATE_SIM_CORPORATE_ERROR" msgid="7664778312218023192">"SIM కార్పొరేట్ అన్‌లాక్ అభ్యర్థన విఫలమైంది."</string>
-    <string name="PERSOSUBSTATE_SIM_SIM_ERROR" msgid="2472944311643350302">"SIM అన్‌లాక్‌ అభ్యర్థన విఫలమైంది."</string>
-    <string name="PERSOSUBSTATE_RUIM_NETWORK1_ERROR" msgid="828089694480999120">"RUIM నెట్‌వర్క్ అన్‌లాక్ అభ్యర్థన విఫలమైంది."</string>
-    <string name="PERSOSUBSTATE_RUIM_NETWORK2_ERROR" msgid="17619001007092511">"RUIM నెట్‌వర్క్2 అన్‌లాక్ అభ్యర్థన విఫలమైంది."</string>
-    <string name="PERSOSUBSTATE_RUIM_HRPD_ERROR" msgid="807214229604353614">"RUIM Hrpd అన్‌లాక్ అభ్యర్థన విఫలమైంది."</string>
-    <string name="PERSOSUBSTATE_RUIM_CORPORATE_ERROR" msgid="8644184447744175747">"RUIM కార్పొరేట్ అన్‌లాక్ అభ్యర్థన విఫలమైంది."</string>
-    <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_ERROR" msgid="3801002648649640407">"RUIM సర్వీస్ ప్రొవైడర్ అన్‌లాక్ అభ్యర్థన విఫలమైంది."</string>
-    <string name="PERSOSUBSTATE_RUIM_RUIM_ERROR" msgid="707397021218680753">"RUIM అన్‌లాక్ అభ్యర్థన విఫలమైంది."</string>
+    <string name="PERSOSUBSTATE_SIM_NETWORK_ERROR" msgid="1924844017037151535">"SIM నెట్‌వర్క్ అన్‌లాక్ రిక్వెస్ట్‌ విఫలమైంది."</string>
+    <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_ERROR" msgid="3372797822292089708">"SIM నెట్‌వర్క్ సబ్‌సెట్ అన్‌లాక్ రిక్వెస్ట్‌ విఫలమైంది."</string>
+    <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_ERROR" msgid="1878443146720411381">"SIM సర్వీస్ ప్రొవైడర్ అన్‌లాక్ రిక్వెస్ట్‌ విఫలమైంది."</string>
+    <string name="PERSOSUBSTATE_SIM_CORPORATE_ERROR" msgid="7664778312218023192">"SIM కార్పొరేట్ అన్‌లాక్ రిక్వెస్ట్‌ విఫలమైంది."</string>
+    <string name="PERSOSUBSTATE_SIM_SIM_ERROR" msgid="2472944311643350302">"SIM అన్‌లాక్‌ రిక్వెస్ట్‌ విఫలమైంది."</string>
+    <string name="PERSOSUBSTATE_RUIM_NETWORK1_ERROR" msgid="828089694480999120">"RUIM నెట్‌వర్క్ అన్‌లాక్ రిక్వెస్ట్‌ విఫలమైంది."</string>
+    <string name="PERSOSUBSTATE_RUIM_NETWORK2_ERROR" msgid="17619001007092511">"RUIM నెట్‌వర్క్2 అన్‌లాక్ రిక్వెస్ట్‌ విఫలమైంది."</string>
+    <string name="PERSOSUBSTATE_RUIM_HRPD_ERROR" msgid="807214229604353614">"RUIM Hrpd అన్‌లాక్ రిక్వెస్ట్‌ విఫలమైంది."</string>
+    <string name="PERSOSUBSTATE_RUIM_CORPORATE_ERROR" msgid="8644184447744175747">"RUIM కార్పొరేట్ అన్‌లాక్ రిక్వెస్ట్‌ విఫలమైంది."</string>
+    <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_ERROR" msgid="3801002648649640407">"RUIM సర్వీస్ ప్రొవైడర్ అన్‌లాక్ రిక్వెస్ట్‌ విఫలమైంది."</string>
+    <string name="PERSOSUBSTATE_RUIM_RUIM_ERROR" msgid="707397021218680753">"RUIM అన్‌లాక్ రిక్వెస్ట్‌ విఫలమైంది."</string>
     <string name="PERSOSUBSTATE_SIM_NETWORK_PUK_ERROR" msgid="894358680773257820">"PUK అన్‌లాక్ విజయవంతం కాలేదు."</string>
     <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_ERROR" msgid="352466878146726991">"PUK అన్‌లాక్ విజయవంతం కాలేదు."</string>
     <string name="PERSOSUBSTATE_SIM_CORPORATE_PUK_ERROR" msgid="7353389721907138671">"PUK అన్‌లాక్ విజయవంతం కాలేదు."</string>
@@ -2247,11 +2247,11 @@
     <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK_ERROR" msgid="5178635064113393143">"PUK అన్‌లాక్ విజయవంతం కాలేదు."</string>
     <string name="PERSOSUBSTATE_RUIM_RUIM_PUK_ERROR" msgid="5391587926974531008">"PUK అన్‌లాక్ విజయవంతం కాలేదు."</string>
     <string name="PERSOSUBSTATE_RUIM_CORPORATE_PUK_ERROR" msgid="4895494864493315868">"PUK అన్‌లాక్ విజయవంతం కాలేదు."</string>
-    <string name="PERSOSUBSTATE_SIM_SPN_ERROR" msgid="9017576601595353649">"SPN అన్‌లాక్ అభ్యర్థన విఫలమైంది."</string>
-    <string name="PERSOSUBSTATE_SIM_SP_EHPLMN_ERROR" msgid="1116993930995545742">"SP Equivalent Home PLMN అన్‌లాక్ అభ్యర్థన విఫలమైంది."</string>
-    <string name="PERSOSUBSTATE_SIM_ICCID_ERROR" msgid="7559167306794441462">"ICCID అన్‌లాక్ అభ్యర్థన విఫలమైంది."</string>
-    <string name="PERSOSUBSTATE_SIM_IMPI_ERROR" msgid="2782926139511136588">"IMPI నెట్‌వర్క్ అన్‌లాక్ అభ్యర్థన విఫలమైంది."</string>
-    <string name="PERSOSUBSTATE_SIM_NS_SP_ERROR" msgid="1890493954453456758">"నెట్‌వర్క్ సబ్‌సెట్ సర్వీస్ ప్రొవైడర్ అన్‌లాక్ అభ్యర్థన విఫలమైంది."</string>
+    <string name="PERSOSUBSTATE_SIM_SPN_ERROR" msgid="9017576601595353649">"SPN అన్‌లాక్ రిక్వెస్ట్‌ విఫలమైంది."</string>
+    <string name="PERSOSUBSTATE_SIM_SP_EHPLMN_ERROR" msgid="1116993930995545742">"SP Equivalent Home PLMN అన్‌లాక్ రిక్వెస్ట్‌ విఫలమైంది."</string>
+    <string name="PERSOSUBSTATE_SIM_ICCID_ERROR" msgid="7559167306794441462">"ICCID అన్‌లాక్ రిక్వెస్ట్‌ విఫలమైంది."</string>
+    <string name="PERSOSUBSTATE_SIM_IMPI_ERROR" msgid="2782926139511136588">"IMPI నెట్‌వర్క్ అన్‌లాక్ రిక్వెస్ట్‌ విఫలమైంది."</string>
+    <string name="PERSOSUBSTATE_SIM_NS_SP_ERROR" msgid="1890493954453456758">"నెట్‌వర్క్ సబ్‌సెట్ సర్వీస్ ప్రొవైడర్ అన్‌లాక్ రిక్వెస్ట్‌ విఫలమైంది."</string>
     <string name="PERSOSUBSTATE_SIM_NETWORK_SUCCESS" msgid="4886243367747126325">"SIM నెట్‌వర్క్ అన్‌లాక్ విజయవంతమైంది."</string>
     <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_SUCCESS" msgid="4053809277733513987">"SIM నెట్‌వర్క్ సబ్‌సెట్ అన్‌లాక్ విజయవంతమైంది."</string>
     <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_SUCCESS" msgid="8249342930499801740">"SIM సర్వీస్ ప్రొవైడర్ అన్‌లాక్ విజయవంతమైంది."</string>
@@ -2259,9 +2259,9 @@
     <string name="PERSOSUBSTATE_SIM_SIM_SUCCESS" msgid="6975608174152828954">"SIM అన్‌లాక్‌ విజయవంతమైంది."</string>
     <string name="PERSOSUBSTATE_RUIM_NETWORK1_SUCCESS" msgid="2846699261330463192">"RUIM నెట్‌వర్క్1 అన్‌లాక్ విజయవంతమైంది."</string>
     <string name="PERSOSUBSTATE_RUIM_NETWORK2_SUCCESS" msgid="5335414726057102801">"RUIM నెట్‌వర్క్2 అన్‌లాక్ విజయవంతమైంది."</string>
-    <string name="PERSOSUBSTATE_RUIM_HRPD_SUCCESS" msgid="8868100318474971969">"RUIM Hrpd అన్‌లాక్ అభ్యర్థన విజయవంతమైంది."</string>
+    <string name="PERSOSUBSTATE_RUIM_HRPD_SUCCESS" msgid="8868100318474971969">"RUIM Hrpd అన్‌లాక్ రిక్వెస్ట్‌ విజయవంతమైంది."</string>
     <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_SUCCESS" msgid="6020936629725666932">"RUIM సర్వీస్ ప్రొవైడర్ అన్‌లాక్ విజయవంతమైంది."</string>
-    <string name="PERSOSUBSTATE_RUIM_CORPORATE_SUCCESS" msgid="6944873647584595489">"RUIM కార్పొరేట్ అన్‌లాక్ అభ్యర్థన విజయవంతమైంది."</string>
+    <string name="PERSOSUBSTATE_RUIM_CORPORATE_SUCCESS" msgid="6944873647584595489">"RUIM కార్పొరేట్ అన్‌లాక్ రిక్వెస్ట్‌ విజయవంతమైంది."</string>
     <string name="PERSOSUBSTATE_RUIM_RUIM_SUCCESS" msgid="2526483514124121988">"RUIM అన్‌లాక్ విజయవంతమైంది."</string>
     <string name="PERSOSUBSTATE_SIM_NETWORK_PUK_SUCCESS" msgid="7662200333621664621">"PUK అన్‌లాక్ విజయవంతమైంది."</string>
     <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_SUCCESS" msgid="2861223407953766632">"PUK అన్‌లాక్ విజయవంతమైంది."</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 3f9cee1..2936a08 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1949,7 +1949,7 @@
     <string name="maximize_button_text" msgid="4258922519914732645">"بڑا کریں"</string>
     <string name="close_button_text" msgid="10603510034455258">"بند کریں"</string>
     <string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
-    <string name="call_notification_answer_action" msgid="5999246836247132937">"جواب"</string>
+    <string name="call_notification_answer_action" msgid="5999246836247132937">"جواب دیں"</string>
     <string name="call_notification_answer_video_action" msgid="2086030940195382249">"ویڈیو"</string>
     <string name="call_notification_decline_action" msgid="3700345945214000726">"مسترد کریں"</string>
     <string name="call_notification_hang_up_action" msgid="9130720590159188131">"منقطع کر دیں"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 68a5ce7..cc38de2 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -327,17 +327,17 @@
     <string name="permgrouplab_sensors" msgid="9134046949784064495">"身体传感器"</string>
     <string name="permgroupdesc_sensors" msgid="2610631290633747752">"访问与您的生命体征相关的传感器数据"</string>
     <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"检索窗口内容"</string>
-    <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"检测您正访问的窗口的内容。"</string>
+    <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"检测您与之互动的窗口的内容。"</string>
     <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"启用触摸浏览"</string>
     <string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"设备将大声读出您点按的内容,同时您可以通过手势来浏览屏幕。"</string>
     <string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"监测您输入的文字"</string>
-    <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"包含个人数据,例如信用卡号和密码。"</string>
-    <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"控制显示内容放大功能"</string>
-    <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"控制显示内容的缩放级别和位置。"</string>
+    <string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"包括信用卡号和密码等个人数据。"</string>
+    <string name="capability_title_canControlMagnification" msgid="7701572187333415795">"控制屏幕内容放大功能"</string>
+    <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"控制屏幕内容的缩放级别和位置。"</string>
     <string name="capability_title_canPerformGestures" msgid="9106545062106728987">"执行手势"</string>
     <string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"可执行点按、滑动、双指张合等手势。"</string>
     <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"指纹手势"</string>
-    <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"可以捕获在设备指纹传感器上执行的手势。"</string>
+    <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"可以捕捉在设备指纹传感器上执行的手势。"</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"截取屏幕截图"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"可截取显示画面的屏幕截图。"</string>
     <string name="permlab_statusBar" msgid="8798267849526214017">"停用或修改状态栏"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 798e06c..1c02c6b 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -246,7 +246,7 @@
     <string name="global_action_power_off" msgid="4404936470711393203">"關機"</string>
     <string name="global_action_power_options" msgid="1185286119330160073">"電源"</string>
     <string name="global_action_restart" msgid="4678451019561687074">"重新啟動"</string>
-    <string name="global_action_emergency" msgid="1387617624177105088">"緊急電話"</string>
+    <string name="global_action_emergency" msgid="1387617624177105088">"緊急撥號"</string>
     <string name="global_action_bug_report" msgid="5127867163044170003">"錯誤報告"</string>
     <string name="global_action_logout" msgid="6093581310002476511">"結束"</string>
     <string name="global_action_screenshot" msgid="2610053466156478564">"螢幕截圖"</string>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 55ed83b..b191584 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -248,37 +248,37 @@
     <color name="system_accent1_0">#ffffff</color>
     <!-- Shade of the accent system color at 99% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent1_10">#F1FFFC</color>
+    <color name="system_accent1_10">#F9FCFF</color>
     <!-- Shade of the accent system color at 95% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent1_50">#9CFFF2</color>
+    <color name="system_accent1_50">#E0F3FF</color>
     <!-- Shade of the accent system color at 90% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent1_100">#8DF5E3</color>
+    <color name="system_accent1_100">#C1E8FF</color>
     <!-- Shade of the accent system color at 80% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent1_200">#71D8C7</color>
+    <color name="system_accent1_200">#76D1FF</color>
     <!-- Shade of the accent system color at 70% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent1_300">#53BCAC</color>
+    <color name="system_accent1_300">#4BB6E8</color>
     <!-- Shade of the accent system color at 60% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent1_400">#34A192</color>
+    <color name="system_accent1_400">#219BCC</color>
     <!-- Shade of the accent system color at 49% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent1_500">#008375</color>
+    <color name="system_accent1_500">#007FAC</color>
     <!-- Shade of the accent system color at 40% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent1_600">#006C5F</color>
+    <color name="system_accent1_600">#00668B</color>
     <!-- Shade of the accent system color at 30% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent1_700">#005747</color>
+    <color name="system_accent1_700">#004C69</color>
     <!-- Shade of the accent system color at 20% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent1_800">#003E31</color>
+    <color name="system_accent1_800">#003549</color>
     <!-- Shade of the accent system color at 10% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent1_900">#002214</color>
+    <color name="system_accent1_900">#001E2C</color>
     <!-- Darkest shade of the accent color used by the system. Black.
      This value can be overlaid at runtime by OverlayManager RROs. -->
     <color name="system_accent1_1000">#000000</color>
@@ -288,37 +288,37 @@
     <color name="system_accent2_0">#ffffff</color>
     <!-- Shade of the secondary accent system color at 99% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent2_10">#F0FFFC</color>
+    <color name="system_accent2_10">#F9FCFF</color>
     <!-- Shade of the secondary accent system color at 95% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent2_50">#CDFAF1</color>
+    <color name="system_accent2_50">#E0F3FF</color>
     <!-- Shade of the secondary accent system color at 90% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent2_100">#BFEBE3</color>
+    <color name="system_accent2_100">#D1E5F4</color>
     <!-- Shade of the secondary accent system color at 80% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent2_200">#A4CFC7</color>
+    <color name="system_accent2_200">#B5CAD7</color>
     <!-- Shade of the secondary accent system color at 70% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent2_300">#89B4AC</color>
+    <color name="system_accent2_300">#9AAEBB</color>
     <!-- Shade of the secondary accent system color at 60% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent2_400">#6F9991</color>
+    <color name="system_accent2_400">#8094A0</color>
     <!-- Shade of the secondary accent system color at 49% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent2_500">#537C75</color>
+    <color name="system_accent2_500">#657985</color>
     <!-- Shade of the secondary accent system color at 40% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent2_600">#3D665F</color>
+    <color name="system_accent2_600">#4E616C</color>
     <!-- Shade of the secondary accent system color at 30% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent2_700">#254E47</color>
+    <color name="system_accent2_700">#374955</color>
     <!-- Shade of the secondary accent system color at 20% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent2_800">#0C3731</color>
+    <color name="system_accent2_800">#20333D</color>
     <!-- Shade of the secondary accent system color at 10% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent2_900">#00211C</color>
+    <color name="system_accent2_900">#091E28</color>
     <!-- Darkest shade of the secondary accent color used by the system. Black.
      This value can be overlaid at runtime by OverlayManager RROs. -->
     <color name="system_accent2_1000">#000000</color>
@@ -331,34 +331,34 @@
     <color name="system_accent3_10">#FFFBFF</color>
     <!-- Shade of the tertiary accent system color at 95% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent3_50">#F9EAFF</color>
+    <color name="system_accent3_50">#F5EEFF</color>
     <!-- Shade of the tertiary accent system color at 90% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent3_100">#ECDBFF</color>
+    <color name="system_accent3_100">#E6DEFF</color>
     <!-- Shade of the tertiary accent system color at 80% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent3_200">#CFBFEB</color>
+    <color name="system_accent3_200">#CAC1EA</color>
     <!-- Shade of the tertiary accent system color at 70% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent3_300">#B3A4CF</color>
+    <color name="system_accent3_300">#AEA6CE</color>
     <!-- Shade of the tertiary accent system color at 60% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent3_400">#988AB3</color>
+    <color name="system_accent3_400">#938CB1</color>
     <!-- Shade of the tertiary accent system color at 49% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent3_500">#7B6E96</color>
+    <color name="system_accent3_500">#787296</color>
     <!-- Shade of the tertiary accent system color at 40% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent3_600">#64587F</color>
+    <color name="system_accent3_600">#605A7C</color>
     <!-- Shade of the tertiary accent system color at 30% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent3_700">#4C4165</color>
+    <color name="system_accent3_700">#484264</color>
     <!-- Shade of the tertiary accent system color at 20% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent3_800">#352B4D</color>
+    <color name="system_accent3_800">#322C4C</color>
     <!-- Shade of the tertiary accent system color at 10% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_accent3_900">#1E1636</color>
+    <color name="system_accent3_900">#1D1736</color>
     <!-- Darkest shade of the tertiary accent color used by the system. Black.
      This value can be overlaid at runtime by OverlayManager RROs. -->
     <color name="system_accent3_1000">#000000</color>
@@ -368,37 +368,37 @@
     <color name="system_neutral1_0">#ffffff</color>
     <!-- Shade of the neutral system color at 99% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral1_10">#fbfbfb</color>
+    <color name="system_neutral1_10">#FCFCFF</color>
     <!-- Shade of the neutral system color at 95% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral1_50">#f0f0f0</color>
+    <color name="system_neutral1_50">#F0F0F3</color>
     <!-- Shade of the neutral system color at 90% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral1_100">#e2e2e2</color>
+    <color name="system_neutral1_100">#E1E3E5</color>
     <!-- Shade of the neutral system color at 80% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral1_200">#c6c6c6</color>
+    <color name="system_neutral1_200">#C5C7C9</color>
     <!-- Shade of the neutral system color at 70% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral1_300">#ababab</color>
+    <color name="system_neutral1_300">#AAABAE</color>
     <!-- Shade of the neutral system color at 60% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral1_400">#909090</color>
+    <color name="system_neutral1_400">#8F9193</color>
     <!-- Shade of the neutral system color at 49% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral1_500">#757575</color>
+    <color name="system_neutral1_500">#747679</color>
     <!-- Shade of the neutral system color at 40% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral1_600">#5e5e5e</color>
+    <color name="system_neutral1_600">#5C5F61</color>
     <!-- Shade of the neutral system color at 30% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral1_700">#464646</color>
+    <color name="system_neutral1_700">#454749</color>
     <!-- Shade of the neutral system color at 20% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral1_800">#303030</color>
+    <color name="system_neutral1_800">#2E3133</color>
     <!-- Shade of the neutral system color at 10% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral1_900">#1b1b1b</color>
+    <color name="system_neutral1_900">#191C1E</color>
     <!-- Darkest shade of the neutral color used by the system. Black.
      This value can be overlaid at runtime by OverlayManager RROs. -->
     <color name="system_neutral1_1000">#000000</color>
@@ -408,37 +408,37 @@
     <color name="system_neutral2_0">#ffffff</color>
     <!-- Shade of the secondary neutral system color at 99% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral2_10">#fbfbfb</color>
+    <color name="system_neutral2_10">#F9FCFF</color>
     <!-- Shade of the secondary neutral system color at 95% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral2_50">#f0f0f0</color>
+    <color name="system_neutral2_50">#EBF1F8</color>
     <!-- Shade of the secondary neutral system color at 90% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral2_100">#e2e2e2</color>
+    <color name="system_neutral2_100">#DCE3E9</color>
     <!-- Shade of the secondary neutral system color at 80% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral2_200">#c6c6c6</color>
+    <color name="system_neutral2_200">#C0C7CD</color>
     <!-- Shade of the secondary neutral system color at 70% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral2_300">#ababab</color>
+    <color name="system_neutral2_300">#A5ACB2</color>
     <!-- Shade of the secondary neutral system color at 60% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral2_400">#909090</color>
+    <color name="system_neutral2_400">#8A9297</color>
     <!-- Shade of the secondary neutral system color at 49% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral2_500">#757575</color>
+    <color name="system_neutral2_500">#70777C</color>
     <!-- Shade of the secondary neutral system color at 40% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral2_600">#5e5e5e</color>
+    <color name="system_neutral2_600">#585F65</color>
     <!-- Shade of the secondary neutral system color at 30% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral2_700">#464646</color>
+    <color name="system_neutral2_700">#40484D</color>
     <!-- Shade of the secondary neutral system color at 20% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral2_800">#303030</color>
+    <color name="system_neutral2_800">#2A3136</color>
     <!-- Shade of the secondary neutral system color at 10% lightness.
      This value can be overlaid at runtime by OverlayManager RROs. -->
-    <color name="system_neutral2_900">#1b1b1b</color>
+    <color name="system_neutral2_900">#161C20</color>
     <!-- Darkest shade of the secondary neutral color used by the system. Black.
      This value can be overlaid at runtime by OverlayManager RROs. -->
     <color name="system_neutral2_1000">#000000</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1586f16..a4954e3 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -666,6 +666,17 @@
          display is powered on at the same time. -->
     <bool name="config_supportsConcurrentInternalDisplays">true</bool>
 
+    <!-- Map of DeviceState to rotation lock setting. Each entry must be in the format
+         "key:value", for example: "0:1".
+          The keys are device states, and the values are one of
+          Settings.Secure.DeviceStateRotationLockSetting.
+          Any device state that doesn't have a default set here will be treated as
+          DEVICE_STATE_ROTATION_LOCK_IGNORED meaning it will not have its own rotation lock setting.
+          If this map is missing, the feature is disabled and only one global rotation lock setting
+           will apply, regardless of device state. -->
+    <string-array name="config_perDeviceStateRotationLockDefaults" />
+
+
     <!-- Desk dock behavior -->
 
     <!-- The number of degrees to rotate the display when the device is in a desk dock.
@@ -960,6 +971,20 @@
     -->
     <integer name="config_longPressOnPowerBehavior">5</integer>
 
+    <!-- The time in milliseconds after which a press on power button is considered "long". -->
+    <integer name="config_longPressOnPowerDurationMs">500</integer>
+
+    <!-- The possible UI options to be surfaced for configuring long press power on duration
+         action. Value set in config_longPressOnPowerDurationMs should be one of the available
+         options to allow users to restore default. -->
+    <integer-array name="config_longPressOnPowerDurationSettings">
+        <item>250</item>
+        <item>350</item>
+        <item>500</item>
+        <item>650</item>
+        <item>750</item>
+    </integer-array>
+
     <!-- Whether the setting to change long press on power behaviour from default to assistant (5)
          is available in Settings.
      -->
@@ -4837,9 +4862,8 @@
             - Option 3 is selected for R.integer.config_letterboxBackgroundType and blur requested
             but isn't supported on the device or both dark scrim alpha and blur radius aren't
             provided.
-        Defaults to black if not specified.
      -->
-    <color name="config_letterboxBackgroundColor">#000</color>
+    <color name="config_letterboxBackgroundColor">@android:color/system_neutral2_500</color>
 
     <!-- Horizonal position of a center of the letterboxed app window.
         0 corresponds to the left side of the screen and 1 to the right side. If given value < 0
@@ -5123,4 +5147,10 @@
         <item>@array/config_mainBuiltInDisplayWaterfallCutout</item>
         <item>@array/config_secondaryBuiltInDisplayWaterfallCutout</item>
     </array>
+
+    <!-- Whether the airplane mode should be reset when device boots in non-safemode after exiting
+         from safemode.
+         This flag should be enabled only when the product does not have any UI to toggle airplane
+         mode like automotive devices.-->
+    <bool name="config_autoResetAirplaneMode">false</bool>
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 7be9c7b..e8bb606 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -237,6 +237,9 @@
          value is calculated in ConversationLayout#updateActionListPadding() -->
     <dimen name="notification_actions_padding_start">36dp</dimen>
 
+    <!-- The max width of a priority action button when it is collapsed to just the icon. -->
+    <dimen name="notification_actions_collapsed_priority_width">60dp</dimen>
+
     <!-- The start padding to optionally use (e.g. if there's extra space) for CallStyle
          notification actions.
          this = conversation_content_start (80dp) - button inset (4dp) - action padding (12dp) -->
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index c4838b8..84f82fd 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -254,6 +254,15 @@
   <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_IME_ENTER}. -->
   <item type="id" name="accessibilityActionImeEnter" />
 
+  <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_DRAG_START}. -->
+  <item type="id" name="accessibilityActionDragStart" />
+
+  <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_DRAG_DROP}. -->
+  <item type="id" name="accessibilityActionDragDrop" />
+
+  <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_DRAG_CANCEL}. -->
+  <item type="id" name="accessibilityActionDragCancel" />
+
   <!-- View tag for remote views to store the index of the next child when adding nested remote views dynamically. -->
   <item type="id" name="remote_views_next_child" />
 
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index eb4919b..b57055c 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3224,6 +3224,9 @@
   </staging-public-group>
 
   <staging-public-group type="id" first-id="0x01fe0000">
+    <public name="accessibilityActionDragStart" />
+    <public name="accessibilityActionDragDrop" />
+    <public name="accessibilityActionDragCancel" />
   </staging-public-group>
 
   <staging-public-group type="style" first-id="0x01fd0000">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index cd590cbb..77a45f9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -439,6 +439,8 @@
   <java-symbol type="integer" name="config_extraFreeKbytesAbsolute" />
   <java-symbol type="integer" name="config_immersive_mode_confirmation_panic" />
   <java-symbol type="integer" name="config_longPressOnPowerBehavior" />
+  <java-symbol type="integer" name="config_longPressOnPowerDurationMs" />
+  <java-symbol type="array" name="config_longPressOnPowerDurationSettings" />
   <java-symbol type="bool" name="config_longPressOnPowerForAssistantSettingAvailable" />
   <java-symbol type="integer" name="config_veryLongPressOnPowerBehavior" />
   <java-symbol type="integer" name="config_veryLongPressTimeout" />
@@ -2217,6 +2219,7 @@
   <java-symbol type="string" name="config_primaryLocationTimeZoneProviderPackageName" />
   <java-symbol type="bool" name="config_enableSecondaryLocationTimeZoneProvider" />
   <java-symbol type="string" name="config_secondaryLocationTimeZoneProviderPackageName" />
+  <java-symbol type="bool" name="config_autoResetAirplaneMode" />
 
   <java-symbol type="layout" name="resolver_list" />
   <java-symbol type="id" name="resolver_list" />
@@ -3183,6 +3186,7 @@
 
   <java-symbol type="id" name="notification_action_list_margin_target" />
   <java-symbol type="dimen" name="notification_actions_padding_start"/>
+  <java-symbol type="dimen" name="notification_actions_collapsed_priority_width"/>
   <java-symbol type="dimen" name="notification_action_disabled_alpha" />
   <java-symbol type="id" name="tag_margin_end_when_icon_visible" />
   <java-symbol type="id" name="tag_margin_end_when_icon_gone" />
@@ -3838,6 +3842,8 @@
   <java-symbol type="string" name="config_foldedArea" />
   <java-symbol type="bool" name="config_supportsConcurrentInternalDisplays" />
   <java-symbol type="bool" name="config_unfoldTransitionEnabled" />
+  <java-symbol type="array" name="config_perDeviceStateRotationLockDefaults" />
+
 
   <java-symbol type="array" name="config_disableApksUnlessMatchedSku_apk_list" />
   <java-symbol type="array" name="config_disableApkUnlessMatchedSku_skus_list" />
diff --git a/core/tests/BroadcastRadioTests/OWNERS b/core/tests/BroadcastRadioTests/OWNERS
index ea4421e..3e360e7 100644
--- a/core/tests/BroadcastRadioTests/OWNERS
+++ b/core/tests/BroadcastRadioTests/OWNERS
@@ -1,2 +1,3 @@
+keunyoung@google.com
+oscarazu@google.com
 twasilczyk@google.com
-randolphs@google.com
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
index 7c6271c..c194989 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
@@ -65,6 +65,7 @@
     @Mock ITunerSession mHalTunerSessionMock;
     private android.hardware.radio.ITunerCallback[] mAidlTunerCallbackMocks;
 
+    private final Object mLock = new Object();
     // RadioModule under test
     private RadioModule mRadioModule;
 
@@ -96,7 +97,7 @@
 
         mRadioModule = new RadioModule(mBroadcastRadioMock, new RadioManager.ModuleProperties(0, "",
                   0, "", "", "", "", 0, 0, false, false, null, false, new int[] {}, new int[] {},
-                  null, null));
+                  null, null), mLock);
 
         doAnswer((Answer) invocation -> {
             mHalTunerCallback = (ITunerCallback) invocation.getArguments()[0];
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index cd07d46..685671b 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -399,6 +399,8 @@
             assertEquals(cDay.getSecondaryTextColor(), cNight.getSecondaryTextColor());
             assertEquals(cDay.getPrimaryAccentColor(), cNight.getPrimaryAccentColor());
             assertEquals(cDay.getSecondaryAccentColor(), cNight.getSecondaryAccentColor());
+            assertEquals(cDay.getTertiaryAccentColor(), cNight.getTertiaryAccentColor());
+            assertEquals(cDay.getOnAccentTextColor(), cNight.getOnAccentTextColor());
             assertEquals(cDay.getProtectionColor(), cNight.getProtectionColor());
             assertEquals(cDay.getContrastColor(), cNight.getContrastColor());
             assertEquals(cDay.getRippleAlpha(), cNight.getRippleAlpha());
@@ -413,20 +415,26 @@
         assertThat(c.getSecondaryTextColor()).isNotEqualTo(Notification.COLOR_INVALID);
         assertThat(c.getPrimaryAccentColor()).isNotEqualTo(Notification.COLOR_INVALID);
         assertThat(c.getSecondaryAccentColor()).isNotEqualTo(Notification.COLOR_INVALID);
+        assertThat(c.getTertiaryAccentColor()).isNotEqualTo(Notification.COLOR_INVALID);
+        assertThat(c.getOnAccentTextColor()).isNotEqualTo(Notification.COLOR_INVALID);
         assertThat(c.getErrorColor()).isNotEqualTo(Notification.COLOR_INVALID);
         assertThat(c.getContrastColor()).isNotEqualTo(Notification.COLOR_INVALID);
         assertThat(c.getRippleAlpha()).isAtLeast(0x00);
         assertThat(c.getRippleAlpha()).isAtMost(0xff);
 
-        // Assert that various colors have sufficient contrast
+        // Assert that various colors have sufficient contrast with the background
         assertContrastIsAtLeast(c.getPrimaryTextColor(), c.getBackgroundColor(), 4.5);
         assertContrastIsAtLeast(c.getSecondaryTextColor(), c.getBackgroundColor(), 4.5);
         assertContrastIsAtLeast(c.getPrimaryAccentColor(), c.getBackgroundColor(), 4.5);
         assertContrastIsAtLeast(c.getErrorColor(), c.getBackgroundColor(), 4.5);
         assertContrastIsAtLeast(c.getContrastColor(), c.getBackgroundColor(), 4.5);
 
-        // This accent color is only used for emphasized buttons
+        // These colors are only used for emphasized buttons; they do not need contrast
         assertContrastIsAtLeast(c.getSecondaryAccentColor(), c.getBackgroundColor(), 1);
+        assertContrastIsAtLeast(c.getTertiaryAccentColor(), c.getBackgroundColor(), 1);
+
+        // The text that is used within the accent color DOES need to have contrast
+        assertContrastIsAtLeast(c.getOnAccentTextColor(), c.getTertiaryAccentColor(), 4.5);
     }
 
     private void assertContrastIsAtLeast(int foreground, int background, double minContrast) {
diff --git a/core/tests/coretests/src/android/app/backup/FullBackupTest.java b/core/tests/coretests/src/android/app/backup/FullBackupTest.java
index 08edb4e..bc92da9 100644
--- a/core/tests/coretests/src/android/app/backup/FullBackupTest.java
+++ b/core/tests/coretests/src/android/app/backup/FullBackupTest.java
@@ -16,6 +16,8 @@
 
 package android.app.backup;
 
+import static android.app.backup.FullBackup.ConfigSection.CLOUD_BACKUP;
+
 import android.app.backup.FullBackup.BackupScheme.PathWithRequiredFlags;
 import android.content.Context;
 import android.test.AndroidTestCase;
@@ -414,6 +416,37 @@
         assertNull("Didn't throw away invalid \"..\" path.", fileDomainIncludes);
     }
 
+    public void testParseNewBackupSchemeFromXml_emptyCloudSectionIsRespected() throws Exception {
+        mXpp.setInput(new StringReader(
+                "<data-extraction-rules>" +
+                        "<cloud-backup>" +
+                        "</cloud-backup>" +
+                        "</data-extraction-rules>"));
+
+        FullBackup.BackupScheme backupScheme = FullBackup.getBackupSchemeForTest(mContext);
+        boolean result = backupScheme.parseNewBackupSchemeFromXmlLocked(mXpp, CLOUD_BACKUP,
+                excludesSet, includeMap);
+
+        assertTrue(result);
+    }
+
+    public void testParseNewBackupSchemeFromXml_emptyCloudSectionWithEncryptionFlagIsRespected()
+            throws Exception {
+        mXpp.setInput(new StringReader(
+                "<data-extraction-rules>" +
+                        "<cloud-backup disableIfNoEncryptionCapabilities=\"true\">" +
+                        "</cloud-backup>" +
+                        "</data-extraction-rules>"));
+
+        FullBackup.BackupScheme backupScheme = FullBackup.getBackupSchemeForTest(mContext);
+        boolean result = backupScheme.parseNewBackupSchemeFromXmlLocked(mXpp, CLOUD_BACKUP,
+                excludesSet, includeMap);
+
+        assertTrue(result);
+        assertEquals(backupScheme.getRequiredTransportFlags(),
+                BackupAgent.FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED);
+    }
+
     public void testDoubleDotInPath_isIgnored() throws Exception {
         mXpp.setInput(new StringReader(
                 "<full-backup-content>" +
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index 9915e38..50639be 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -156,7 +156,8 @@
                 .setProcState(procState).setState(bundle).setPersistentState(persistableBundle)
                 .setPendingResults(resultInfoList()).setPendingNewIntents(referrerIntentList())
                 .setIsForward(true).setAssistToken(assistToken)
-                .setShareableActivityToken(shareableActivityToken).build();
+                .setShareableActivityToken(shareableActivityToken)
+                .setTaskFragmentToken(new Binder()).build();
 
         LaunchActivityItem emptyItem = new LaunchActivityItemBuilder().build();
         LaunchActivityItem item = itemSupplier.get();
diff --git a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
index 75da0bf..1173c92 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
@@ -112,6 +112,7 @@
         private IBinder mShareableActivityToken;
         private FixedRotationAdjustments mFixedRotationAdjustments;
         private boolean mLaunchedFromBubble;
+        private IBinder mTaskFragmentToken;
 
         LaunchActivityItemBuilder setIntent(Intent intent) {
             mIntent = intent;
@@ -213,13 +214,18 @@
             return this;
         }
 
+        LaunchActivityItemBuilder setTaskFragmentToken(IBinder taskFragmentToken) {
+            mTaskFragmentToken = taskFragmentToken;
+            return this;
+        }
+
         LaunchActivityItem build() {
             return LaunchActivityItem.obtain(mIntent, mIdent, mInfo,
                     mCurConfig, mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor,
                     mProcState, mState, mPersistentState, mPendingResults, mPendingNewIntents,
                     mActivityOptions, mIsForward, mProfilerInfo, mAssistToken,
                     null /* activityClientController */, mFixedRotationAdjustments,
-                    mShareableActivityToken, mLaunchedFromBubble);
+                    mShareableActivityToken, mLaunchedFromBubble, mTaskFragmentToken);
         }
     }
 }
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index df0c64c..98c9afd 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -209,6 +209,7 @@
                 .setPendingNewIntents(referrerIntentList()).setIsForward(true)
                 .setAssistToken(new Binder()).setFixedRotationAdjustments(fixedRotationAdjustments)
                 .setShareableActivityToken(new Binder())
+                .setTaskFragmentToken(new Binder())
                 .build();
 
         writeAndPrepareForReading(item);
diff --git a/core/tests/coretests/src/android/view/WindowInfoTest.java b/core/tests/coretests/src/android/view/WindowInfoTest.java
index 05e8bd8..0a99b08 100644
--- a/core/tests/coretests/src/android/view/WindowInfoTest.java
+++ b/core/tests/coretests/src/android/view/WindowInfoTest.java
@@ -25,6 +25,7 @@
 import static org.junit.Assert.fail;
 import static org.mockito.Mockito.mock;
 
+import android.app.ActivityTaskManager;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.platform.test.annotations.Presubmit;
@@ -83,6 +84,7 @@
         assertEquals(0, w.layer);
         assertEquals(AccessibilityNodeInfo.UNDEFINED_NODE_ID, w.accessibilityIdOfAnchor);
         assertEquals(Display.INVALID_DISPLAY, w.displayId);
+        assertEquals(ActivityTaskManager.INVALID_TASK_ID, w.taskId);
         assertNull(w.title);
         assertNull(w.token);
         assertNull(w.childTokens);
@@ -123,6 +125,7 @@
         windowInfo.displayId = 2;
         windowInfo.layer = 3;
         windowInfo.accessibilityIdOfAnchor = 4L;
+        windowInfo.taskId = 5;
         windowInfo.title = "title";
         windowInfo.token = mock(IBinder.class);
         windowInfo.childTokens = new ArrayList<>();
diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
index 96b4316..7cd8197 100644
--- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
@@ -23,6 +23,7 @@
 import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
 import static com.android.internal.jank.FrameTracker.ViewRootWrapper;
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_WALLPAPER_TRANSITION;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -50,6 +51,7 @@
 import com.android.internal.jank.FrameTracker.ChoreographerWrapper;
 import com.android.internal.jank.FrameTracker.FrameMetricsWrapper;
 import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper;
+import com.android.internal.jank.InteractionJankMonitor.Configuration;
 import com.android.internal.jank.InteractionJankMonitor.Session;
 
 import org.junit.Before;
@@ -69,7 +71,6 @@
     public ActivityTestRule<ViewAttachTestActivity> mRule =
             new ActivityTestRule<>(ViewAttachTestActivity.class);
 
-    private FrameTracker mTracker;
     private ThreadedRendererWrapper mRenderer;
     private FrameMetricsWrapper mWrapper;
     private SurfaceControlWrapper mSurfaceControlWrapper;
@@ -85,7 +86,6 @@
         View view = mActivity.getWindow().getDecorView();
         assertThat(view.isAttachedToWindow()).isTrue();
 
-        Handler handler = mRule.getActivity().getMainThreadHandler();
         mWrapper = Mockito.spy(new FrameMetricsWrapper());
         mRenderer = Mockito.spy(new ThreadedRendererWrapper(view.getThreadedRenderer()));
         doNothing().when(mRenderer).addObserver(any());
@@ -103,229 +103,355 @@
                 mListenerCapture.capture());
 
         mChoreographer = mock(ChoreographerWrapper.class);
+    }
 
-        Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX);
-        mTracker = Mockito.spy(
+    private FrameTracker spyFrameTracker(int cuj, String postfix, boolean surfaceOnly) {
+        Handler handler = mRule.getActivity().getMainThreadHandler();
+        Session session = new Session(cuj, postfix);
+        Configuration config = mock(Configuration.class);
+        when(config.isSurfaceOnly()).thenReturn(surfaceOnly);
+        when(config.getSurfaceControl()).thenReturn(mSurfaceControl);
+        FrameTracker frameTracker = Mockito.spy(
                 new FrameTracker(session, handler, mRenderer, mViewRootWrapper,
                         mSurfaceControlWrapper, mChoreographer, mWrapper,
-                        /*traceThresholdMissedFrames=*/ 1, /*traceThresholdFrameTimeMillis=*/ -1,
-                        null));
-        doNothing().when(mTracker).triggerPerfetto();
-        doNothing().when(mTracker).postTraceStartMarker();
+                        /* traceThresholdMissedFrames= */ 1,
+                        /* traceThresholdFrameTimeMillis= */ -1,
+                        /* FrameTrackerListener= */ null, config));
+        doNothing().when(frameTracker).triggerPerfetto();
+        doNothing().when(frameTracker).postTraceStartMarker();
+        return frameTracker;
     }
 
     @Test
     public void testOnlyFirstWindowFrameOverThreshold() {
+        FrameTracker tracker = spyFrameTracker(
+                CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+
         // Just provide current timestamp anytime mWrapper asked for VSYNC_TIMESTAMP
         when(mWrapper.getMetric(FrameMetrics.VSYNC_TIMESTAMP))
                 .then(unusedInvocation -> System.nanoTime());
 
         when(mChoreographer.getVsyncId()).thenReturn(100L);
-        mTracker.begin();
+        tracker.begin();
         verify(mRenderer, only()).addObserver(any());
 
         // send first frame with a long duration - should not be taken into account
-        sendFirstWindowFrame(100, JANK_APP_DEADLINE_MISSED, 100L);
+        sendFirstWindowFrame(tracker, 100, JANK_APP_DEADLINE_MISSED, 100L);
 
         // send another frame with a short duration - should not be considered janky
-        sendFirstWindowFrame(5, JANK_NONE, 101L);
+        sendFirstWindowFrame(tracker, 5, JANK_NONE, 101L);
 
         // end the trace session, the last janky frame is after the end() so is discarded.
         when(mChoreographer.getVsyncId()).thenReturn(102L);
-        mTracker.end(FrameTracker.REASON_END_NORMAL);
-        sendFrame(5, JANK_NONE, 102L);
-        sendFrame(500, JANK_APP_DEADLINE_MISSED, 103L);
+        tracker.end(FrameTracker.REASON_END_NORMAL);
+        sendFrame(tracker, 5, JANK_NONE, 102L);
+        sendFrame(tracker, 500, JANK_APP_DEADLINE_MISSED, 103L);
 
-        verify(mTracker).removeObservers();
-        verify(mTracker, never()).triggerPerfetto();
+        verify(tracker).removeObservers();
+        verify(tracker, never()).triggerPerfetto();
     }
 
     @Test
     public void testSfJank() {
+        FrameTracker tracker = spyFrameTracker(
+                CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+
         when(mChoreographer.getVsyncId()).thenReturn(100L);
-        mTracker.begin();
+        tracker.begin();
         verify(mRenderer, only()).addObserver(any());
 
         // send first frame - not janky
-        sendFrame(4, JANK_NONE, 100L);
+        sendFrame(tracker, 4, JANK_NONE, 100L);
 
         // send another frame - should be considered janky
-        sendFrame(40, JANK_SURFACEFLINGER_DEADLINE_MISSED, 101L);
+        sendFrame(tracker, 40, JANK_SURFACEFLINGER_DEADLINE_MISSED, 101L);
 
         // end the trace session
         when(mChoreographer.getVsyncId()).thenReturn(102L);
-        mTracker.end(FrameTracker.REASON_END_NORMAL);
-        sendFrame(4, JANK_NONE, 102L);
+        tracker.end(FrameTracker.REASON_END_NORMAL);
+        sendFrame(tracker, 4, JANK_NONE, 102L);
 
-        verify(mTracker).removeObservers();
+        verify(tracker).removeObservers();
 
         // We detected a janky frame - trigger Perfetto
-        verify(mTracker).triggerPerfetto();
+        verify(tracker).triggerPerfetto();
     }
 
     @Test
     public void testFirstFrameJankyNoTrigger() {
+        FrameTracker tracker = spyFrameTracker(
+                CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+
         when(mChoreographer.getVsyncId()).thenReturn(100L);
-        mTracker.begin();
+        tracker.begin();
         verify(mRenderer, only()).addObserver(any());
 
         // send first frame - janky
-        sendFrame(40, JANK_APP_DEADLINE_MISSED, 100L);
+        sendFrame(tracker, 40, JANK_APP_DEADLINE_MISSED, 100L);
 
         // send another frame - not jank
-        sendFrame(4, JANK_NONE, 101L);
+        sendFrame(tracker, 4, JANK_NONE, 101L);
 
         // end the trace session
         when(mChoreographer.getVsyncId()).thenReturn(102L);
-        mTracker.end(FrameTracker.REASON_END_NORMAL);
-        sendFrame(4, JANK_NONE, 102L);
+        tracker.end(FrameTracker.REASON_END_NORMAL);
+        sendFrame(tracker, 4, JANK_NONE, 102L);
 
-        verify(mTracker).removeObservers();
+        verify(tracker).removeObservers();
 
         // We detected a janky frame - trigger Perfetto
-        verify(mTracker, never()).triggerPerfetto();
+        verify(tracker, never()).triggerPerfetto();
     }
 
     @Test
     public void testOtherFrameOverThreshold() {
+        FrameTracker tracker = spyFrameTracker(
+                CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+
         when(mChoreographer.getVsyncId()).thenReturn(100L);
-        mTracker.begin();
+        tracker.begin();
         verify(mRenderer, only()).addObserver(any());
 
         // send first frame - not janky
-        sendFrame(4, JANK_NONE, 100L);
+        sendFrame(tracker, 4, JANK_NONE, 100L);
 
         // send another frame - should be considered janky
-        sendFrame(40, JANK_APP_DEADLINE_MISSED, 101L);
+        sendFrame(tracker, 40, JANK_APP_DEADLINE_MISSED, 101L);
 
         // end the trace session
         when(mChoreographer.getVsyncId()).thenReturn(102L);
-        mTracker.end(FrameTracker.REASON_END_NORMAL);
-        sendFrame(4, JANK_NONE, 102L);
+        tracker.end(FrameTracker.REASON_END_NORMAL);
+        sendFrame(tracker, 4, JANK_NONE, 102L);
 
-        verify(mTracker).removeObservers();
+        verify(tracker).removeObservers();
 
         // We detected a janky frame - trigger Perfetto
-        verify(mTracker).triggerPerfetto();
+        verify(tracker).triggerPerfetto();
     }
 
     @Test
     public void testLastFrameOverThresholdBeforeEnd() {
+        FrameTracker tracker = spyFrameTracker(
+                CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+
         when(mChoreographer.getVsyncId()).thenReturn(100L);
-        mTracker.begin();
+        tracker.begin();
         verify(mRenderer, only()).addObserver(any());
 
         // send first frame - not janky
-        sendFrame(4, JANK_NONE, 100L);
+        sendFrame(tracker, 4, JANK_NONE, 100L);
 
         // send another frame - not janky
-        sendFrame(4, JANK_NONE, 101L);
+        sendFrame(tracker, 4, JANK_NONE, 101L);
 
         // end the trace session, simulate one more valid callback came after the end call.
         when(mChoreographer.getVsyncId()).thenReturn(102L);
-        mTracker.end(FrameTracker.REASON_END_NORMAL);
-        sendFrame(50, JANK_APP_DEADLINE_MISSED, 102L);
+        tracker.end(FrameTracker.REASON_END_NORMAL);
+        sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 102L);
 
         // One more callback with VSYNC after the end() vsync id.
-        sendFrame(4, JANK_NONE, 103L);
+        sendFrame(tracker, 4, JANK_NONE, 103L);
 
-        verify(mTracker).removeObservers();
+        verify(tracker).removeObservers();
 
         // We detected a janky frame - trigger Perfetto
-        verify(mTracker).triggerPerfetto();
+        verify(tracker).triggerPerfetto();
     }
 
     @Test
     public void testBeginCancel() {
+        FrameTracker tracker = spyFrameTracker(
+                CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+
         when(mChoreographer.getVsyncId()).thenReturn(100L);
-        mTracker.begin();
+        tracker.begin();
         verify(mRenderer).addObserver(any());
 
         // First frame - not janky
-        sendFrame(4, JANK_NONE, 100L);
+        sendFrame(tracker, 4, JANK_NONE, 100L);
 
         // normal frame - not janky
-        sendFrame(4, JANK_NONE, 101L);
+        sendFrame(tracker, 4, JANK_NONE, 101L);
 
         // a janky frame
-        sendFrame(50, JANK_APP_DEADLINE_MISSED, 102L);
+        sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 102L);
 
-        mTracker.cancel(FrameTracker.REASON_CANCEL_NORMAL);
-        verify(mTracker).removeObservers();
+        tracker.cancel(FrameTracker.REASON_CANCEL_NORMAL);
+        verify(tracker).removeObservers();
         // Since the tracker has been cancelled, shouldn't trigger perfetto.
-        verify(mTracker, never()).triggerPerfetto();
+        verify(tracker, never()).triggerPerfetto();
     }
 
     @Test
     public void testCancelIfEndVsyncIdEqualsToBeginVsyncId() {
+        FrameTracker tracker = spyFrameTracker(
+                CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+
         when(mChoreographer.getVsyncId()).thenReturn(100L);
-        mTracker.begin();
+        tracker.begin();
         verify(mRenderer, only()).addObserver(any());
 
         // end the trace session
         when(mChoreographer.getVsyncId()).thenReturn(101L);
-        mTracker.end(FrameTracker.REASON_END_NORMAL);
+        tracker.end(FrameTracker.REASON_END_NORMAL);
 
         // Since the begin vsync id (101) equals to the end vsync id (101), will be treat as cancel.
-        verify(mTracker).cancel(FrameTracker.REASON_CANCEL_SAME_VSYNC);
+        verify(tracker).cancel(FrameTracker.REASON_CANCEL_SAME_VSYNC);
 
         // Observers should be removed in this case, or FrameTracker object will be leaked.
-        verify(mTracker).removeObservers();
+        verify(tracker).removeObservers();
 
         // Should never trigger Perfetto since it is a cancel.
-        verify(mTracker, never()).triggerPerfetto();
+        verify(tracker, never()).triggerPerfetto();
     }
 
     @Test
     public void testCancelIfEndVsyncIdLessThanBeginVsyncId() {
+        FrameTracker tracker = spyFrameTracker(
+                CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+
         when(mChoreographer.getVsyncId()).thenReturn(100L);
-        mTracker.begin();
+        tracker.begin();
         verify(mRenderer, only()).addObserver(any());
 
         // end the trace session at the same vsync id, end vsync id will less than the begin one.
         // Because the begin vsync id is supposed to the next frame,
-        mTracker.end(FrameTracker.REASON_END_NORMAL);
+        tracker.end(FrameTracker.REASON_END_NORMAL);
 
         // The begin vsync id (101) is larger than the end one (100), will be treat as cancel.
-        verify(mTracker).cancel(FrameTracker.REASON_CANCEL_SAME_VSYNC);
+        verify(tracker).cancel(FrameTracker.REASON_CANCEL_SAME_VSYNC);
 
         // Observers should be removed in this case, or FrameTracker object will be leaked.
-        verify(mTracker).removeObservers();
+        verify(tracker).removeObservers();
 
         // Should never trigger Perfetto since it is a cancel.
-        verify(mTracker, never()).triggerPerfetto();
+        verify(tracker, never()).triggerPerfetto();
     }
 
     @Test
     public void testCancelWhenSessionNeverBegun() {
-        mTracker.cancel(FrameTracker.REASON_CANCEL_NORMAL);
-        verify(mTracker).removeObservers();
+        FrameTracker tracker = spyFrameTracker(
+                CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+
+        tracker.cancel(FrameTracker.REASON_CANCEL_NORMAL);
+        verify(tracker).removeObservers();
     }
 
     @Test
     public void testEndWhenSessionNeverBegun() {
-        mTracker.end(FrameTracker.REASON_END_NORMAL);
-        verify(mTracker).removeObservers();
+        FrameTracker tracker = spyFrameTracker(
+                CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX, /* surfaceOnly= */ false);
+
+        tracker.end(FrameTracker.REASON_END_NORMAL);
+        verify(tracker).removeObservers();
     }
 
-    private void sendFirstWindowFrame(long durationMillis,
+    @Test
+    public void testSurfaceOnlyOtherFrameJanky() {
+        FrameTracker tracker = spyFrameTracker(
+                CUJ_WALLPAPER_TRANSITION, CUJ_POSTFIX, /* surfaceOnly= */ true);
+
+        when(mChoreographer.getVsyncId()).thenReturn(100L);
+        tracker.begin();
+        verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());
+
+        // First frame - not janky
+        sendFrame(tracker, JANK_NONE, 100L);
+        // normal frame - not janky
+        sendFrame(tracker, JANK_NONE, 101L);
+        // a janky frame
+        sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 102L);
+
+        when(mChoreographer.getVsyncId()).thenReturn(102L);
+        tracker.end(FrameTracker.REASON_CANCEL_NORMAL);
+
+        // an extra frame to trigger finish
+        sendFrame(tracker, JANK_NONE, 103L);
+
+        verify(mSurfaceControlWrapper).removeJankStatsListener(any());
+        verify(tracker).triggerPerfetto();
+    }
+
+    @Test
+    public void testSurfaceOnlyFirstFrameJanky() {
+        FrameTracker tracker = spyFrameTracker(
+                CUJ_WALLPAPER_TRANSITION, CUJ_POSTFIX, /* surfaceOnly= */ true);
+
+        when(mChoreographer.getVsyncId()).thenReturn(100L);
+        tracker.begin();
+        verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());
+
+        // First frame - janky
+        sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 100L);
+        // normal frame - not janky
+        sendFrame(tracker, JANK_NONE, 101L);
+        // normal frame - not janky
+        sendFrame(tracker, JANK_NONE, 102L);
+
+        when(mChoreographer.getVsyncId()).thenReturn(102L);
+        tracker.end(FrameTracker.REASON_CANCEL_NORMAL);
+
+        // an extra frame to trigger finish
+        sendFrame(tracker, JANK_NONE, 103L);
+
+        verify(mSurfaceControlWrapper).removeJankStatsListener(any());
+        verify(tracker, never()).triggerPerfetto();
+    }
+
+    @Test
+    public void testSurfaceOnlyLastFrameJanky() {
+        FrameTracker tracker = spyFrameTracker(
+                CUJ_WALLPAPER_TRANSITION, CUJ_POSTFIX, /* surfaceOnly= */ true);
+
+        when(mChoreographer.getVsyncId()).thenReturn(100L);
+        tracker.begin();
+        verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());
+
+        // First frame - not janky
+        sendFrame(tracker, JANK_NONE, 100L);
+        // normal frame - not janky
+        sendFrame(tracker, JANK_NONE, 101L);
+        // normal frame - not janky
+        sendFrame(tracker, JANK_NONE, 102L);
+
+        when(mChoreographer.getVsyncId()).thenReturn(102L);
+        tracker.end(FrameTracker.REASON_CANCEL_NORMAL);
+
+        // janky frame, should be ignored, trigger finish
+        sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 103L);
+
+        verify(mSurfaceControlWrapper).removeJankStatsListener(any());
+        verify(tracker, never()).triggerPerfetto();
+    }
+
+    private void sendFirstWindowFrame(FrameTracker tracker, long durationMillis,
             @JankType int jankType, long vsyncId) {
-        sendFrame(durationMillis, jankType, vsyncId, true /* firstWindowFrame */);
+        sendFrame(tracker, durationMillis, jankType, vsyncId, /* firstWindowFrame= */ true);
     }
 
-    private void sendFrame(long durationMillis,
+    private void sendFrame(FrameTracker tracker, long durationMillis,
             @JankType int jankType, long vsyncId) {
-        sendFrame(durationMillis, jankType, vsyncId, false /* firstWindowFrame */);
+        sendFrame(tracker, durationMillis, jankType, vsyncId, /* firstWindowFrame= */ false);
     }
 
-    private void sendFrame(long durationMillis,
+    /**
+     * Used for surface only test.
+     */
+    private void sendFrame(FrameTracker tracker, @JankType int jankType, long vsyncId) {
+        sendFrame(tracker, /* durationMillis= */ -1,
+                jankType, vsyncId, /* firstWindowFrame= */ false);
+    }
+
+    private void sendFrame(FrameTracker tracker, long durationMillis,
             @JankType int jankType, long vsyncId, boolean firstWindowFrame) {
-        when(mWrapper.getTiming()).thenReturn(new long[] { 0, vsyncId });
-        doReturn(firstWindowFrame ? 1L : 0L).when(mWrapper)
-                .getMetric(FrameMetrics.FIRST_DRAW_FRAME);
-        doReturn(TimeUnit.MILLISECONDS.toNanos(durationMillis))
-                .when(mWrapper).getMetric(FrameMetrics.TOTAL_DURATION);
-        mTracker.onFrameMetricsAvailable(0);
+        if (!tracker.mSurfaceOnly) {
+            when(mWrapper.getTiming()).thenReturn(new long[]{0, vsyncId});
+            doReturn(firstWindowFrame ? 1L : 0L).when(mWrapper)
+                    .getMetric(FrameMetrics.FIRST_DRAW_FRAME);
+            doReturn(TimeUnit.MILLISECONDS.toNanos(durationMillis))
+                    .when(mWrapper).getMetric(FrameMetrics.TOTAL_DURATION);
+            tracker.onFrameMetricsAvailable(0);
+        }
         mListenerCapture.getValue().onJankDataAvailable(new JankData[] {
                 new JankData(vsyncId, jankType)
         });
diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
index c153b38..d7a5e26 100644
--- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
@@ -16,8 +16,6 @@
 
 package com.android.internal.jank;
 
-import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
-import static com.android.internal.jank.FrameTracker.ViewRootWrapper;
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_TO_STATSD_INTERACTION_TYPE;
 
@@ -25,17 +23,17 @@
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.os.Handler;
 import android.os.HandlerThread;
-import android.os.Message;
 import android.provider.DeviceConfig;
 import android.view.View;
 import android.view.ViewAttachTestActivity;
@@ -43,8 +41,12 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.rule.ActivityTestRule;
 
+import com.android.internal.jank.FrameTracker.ChoreographerWrapper;
 import com.android.internal.jank.FrameTracker.FrameMetricsWrapper;
+import com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
 import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper;
+import com.android.internal.jank.FrameTracker.ViewRootWrapper;
+import com.android.internal.jank.InteractionJankMonitor.Configuration;
 import com.android.internal.jank.InteractionJankMonitor.Session;
 
 import org.junit.Before;
@@ -92,12 +94,15 @@
         verify(mWorker).start();
 
         Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX);
+        Configuration config = mock(Configuration.class);
+        when(config.isSurfaceOnly()).thenReturn(false);
         FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(),
                 new ThreadedRendererWrapper(mView.getThreadedRenderer()),
-                new ViewRootWrapper(mView.getViewRootImpl()), new SurfaceControlWrapper(),
-                mock(FrameTracker.ChoreographerWrapper.class),
-                new FrameMetricsWrapper(), /*traceThresholdMissedFrames=*/ 1,
-                /*traceThresholdFrameTimeMillis=*/ -1, null));
+                new ViewRootWrapper(mView.getViewRootImpl()),
+                new SurfaceControlWrapper(), mock(ChoreographerWrapper.class),
+                new FrameMetricsWrapper(),
+                /* traceThresholdMissedFrames= */ 1, /* traceThresholdFrameTimeMillis= */ -1,
+                /* FrameTrackerListener */ null, config));
         doReturn(tracker).when(monitor).createFrameTracker(any(), any());
         doNothing().when(tracker).triggerPerfetto();
         doNothing().when(tracker).postTraceStartMarker();
@@ -138,28 +143,30 @@
     public void testBeginCancel() {
         InteractionJankMonitor monitor = spy(new InteractionJankMonitor(mWorker));
 
-        ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
+        ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
 
         Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX);
+        Configuration config = mock(Configuration.class);
+        when(config.isSurfaceOnly()).thenReturn(false);
         FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(),
                 new ThreadedRendererWrapper(mView.getThreadedRenderer()),
-                new ViewRootWrapper(mView.getViewRootImpl()), new SurfaceControlWrapper(),
-                mock(FrameTracker.ChoreographerWrapper.class),
-                new FrameMetricsWrapper(), /*traceThresholdMissedFrames=*/ 1,
-                /*traceThresholdFrameTimeMillis=*/ -1, null));
+                new ViewRootWrapper(mView.getViewRootImpl()),
+                new SurfaceControlWrapper(), mock(FrameTracker.ChoreographerWrapper.class),
+                new FrameMetricsWrapper(),
+                /* traceThresholdMissedFrames= */ 1, /* traceThresholdFrameTimeMillis= */ -1,
+                /* FrameTrackerListener */ null, config));
         doReturn(tracker).when(monitor).createFrameTracker(any(), any());
         doNothing().when(tracker).triggerPerfetto();
         doNothing().when(tracker).postTraceStartMarker();
 
         assertThat(monitor.begin(mView, session.getCuj())).isTrue();
         verify(tracker).begin();
-        verify(mWorker.getThreadHandler(), atLeastOnce()).sendMessageAtTime(captor.capture(),
-                anyLong());
-        Runnable runnable = captor.getValue().getCallback();
+        verify(monitor).scheduleTimeoutAction(anyInt(), anyLong(), captor.capture());
+        Runnable runnable = captor.getValue();
         assertThat(runnable).isNotNull();
         mWorker.getThreadHandler().removeCallbacks(runnable);
         runnable.run();
-        verify(tracker).cancel(FrameTracker.REASON_CANCEL_NORMAL);
+        verify(tracker).cancel(FrameTracker.REASON_CANCEL_TIMEOUT);
     }
 
     @Test
diff --git a/core/tests/coretests/src/com/android/internal/os/ProcLocksReaderTest.java b/core/tests/coretests/src/com/android/internal/os/ProcLocksReaderTest.java
new file mode 100644
index 0000000..d800c2c
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/ProcLocksReaderTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.internal.os;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.FileUtils;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.nio.file.Files;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ProcLocksReaderTest {
+    private File mProcDirectory;
+
+    @Before
+    public void setUp() {
+        Context context = InstrumentationRegistry.getContext();
+        mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        FileUtils.deleteContents(mProcDirectory);
+    }
+
+    @Test
+    public void testRunSimpleLocks() throws Exception {
+        String simpleLocks =
+                "1: POSIX  ADVISORY  READ  18403 fd:09:9070 1073741826 1073742335\n" +
+                "2: POSIX  ADVISORY  WRITE 18292 fd:09:34062 0 EOF\n";
+        assertFalse(runHasFileLocks(simpleLocks, 18402));
+        assertFalse(runHasFileLocks(simpleLocks, 18404));
+        assertTrue(runHasFileLocks(simpleLocks, 18403));
+        assertTrue(runHasFileLocks(simpleLocks, 18292));
+    }
+
+    @Test
+    public void testRunBlockedLocks() throws Exception {
+        String blockedLocks =
+                "1: POSIX  ADVISORY  READ  18403 fd:09:9070 1073741826 1073742335\n" +
+                "2: POSIX  ADVISORY  WRITE 18292 fd:09:34062 0 EOF\n" +
+                "2: -> POSIX  ADVISORY  WRITE 18291 fd:09:34062 0 EOF\n" +
+                "2: -> POSIX  ADVISORY  WRITE 18293 fd:09:34062 0 EOF\n" +
+                "3: POSIX  ADVISORY  READ  3888 fd:09:13992 128 128\n" +
+                "4: POSIX  ADVISORY  READ  3888 fd:09:14230 1073741826 1073742335\n";
+        assertFalse(runHasFileLocks(blockedLocks, 18402));
+        assertFalse(runHasFileLocks(blockedLocks, 18404));
+        assertTrue(runHasFileLocks(blockedLocks, 18403));
+        assertTrue(runHasFileLocks(blockedLocks, 18292));
+
+        assertFalse(runHasFileLocks(blockedLocks, 18291));
+        assertFalse(runHasFileLocks(blockedLocks, 18293));
+        assertTrue(runHasFileLocks(blockedLocks, 3888));
+    }
+
+    private boolean runHasFileLocks(String fileContents, int pid) throws Exception {
+        File tempFile = File.createTempFile("locks", null, mProcDirectory);
+        Files.write(tempFile.toPath(), fileContents.getBytes());
+        boolean result = new ProcLocksReader(tempFile.toString()).hasFileLocks(pid);
+        Files.delete(tempFile.toPath());
+        return result;
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/util/ContrastColorUtilTest.java b/core/tests/coretests/src/com/android/internal/util/ContrastColorUtilTest.java
new file mode 100644
index 0000000..9da720c
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/ContrastColorUtilTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+package com.android.internal.util;
+
+import static androidx.core.graphics.ColorUtils.calculateContrast;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.graphics.Color;
+
+import androidx.test.filters.SmallTest;
+
+import junit.framework.TestCase;
+
+public class ContrastColorUtilTest extends TestCase {
+
+    @SmallTest
+    public void testEnsureTextContrastAgainstDark() {
+        int darkBg = 0xFF35302A;
+
+        int blueContrastColor = ContrastColorUtil.ensureTextContrast(Color.BLUE, darkBg, true);
+        assertContrastIsWithinRange(blueContrastColor, darkBg, 4.5, 4.75);
+
+        int redContrastColor = ContrastColorUtil.ensureTextContrast(Color.RED, darkBg, true);
+        assertContrastIsWithinRange(redContrastColor, darkBg, 4.5, 4.75);
+
+        final int darkGreen = 0xff008800;
+        int greenContrastColor = ContrastColorUtil.ensureTextContrast(darkGreen, darkBg, true);
+        assertContrastIsWithinRange(greenContrastColor, darkBg, 4.5, 4.75);
+
+        int grayContrastColor = ContrastColorUtil.ensureTextContrast(Color.DKGRAY, darkBg, true);
+        assertContrastIsWithinRange(grayContrastColor, darkBg, 4.5, 4.75);
+
+        int selfContrastColor = ContrastColorUtil.ensureTextContrast(darkBg, darkBg, true);
+        assertContrastIsWithinRange(selfContrastColor, darkBg, 4.5, 4.75);
+    }
+
+    @SmallTest
+    public void testEnsureTextContrastAgainstLight() {
+        int lightBg = 0xFFFFF8F2;
+
+        final int lightBlue = 0xff8888ff;
+        int blueContrastColor = ContrastColorUtil.ensureTextContrast(lightBlue, lightBg, false);
+        assertContrastIsWithinRange(blueContrastColor, lightBg, 4.5, 4.75);
+
+        int redContrastColor = ContrastColorUtil.ensureTextContrast(Color.RED, lightBg, false);
+        assertContrastIsWithinRange(redContrastColor, lightBg, 4.5, 4.75);
+
+        int greenContrastColor = ContrastColorUtil.ensureTextContrast(Color.GREEN, lightBg, false);
+        assertContrastIsWithinRange(greenContrastColor, lightBg, 4.5, 4.75);
+
+        int grayContrastColor = ContrastColorUtil.ensureTextContrast(Color.LTGRAY, lightBg, false);
+        assertContrastIsWithinRange(grayContrastColor, lightBg, 4.5, 4.75);
+
+        int selfContrastColor = ContrastColorUtil.ensureTextContrast(lightBg, lightBg, false);
+        assertContrastIsWithinRange(selfContrastColor, lightBg, 4.5, 4.75);
+    }
+
+    private void assertContrastIsWithinRange(int foreground, int background,
+            double minContrast, double maxContrast) {
+        assertContrastIsAtLeast(foreground, background, minContrast);
+        assertContrastIsAtMost(foreground, background, maxContrast);
+    }
+
+    private void assertContrastIsAtLeast(int foreground, int background, double minContrast) {
+        try {
+            assertThat(calculateContrast(foreground, background)).isAtLeast(minContrast);
+        } catch (AssertionError e) {
+            throw new AssertionError(
+                    String.format("Insufficient contrast: foreground=#%08x background=#%08x",
+                            foreground, background), e);
+        }
+    }
+
+    private void assertContrastIsAtMost(int foreground, int background, double maxContrast) {
+        try {
+            assertThat(calculateContrast(foreground, background)).isAtMost(maxContrast);
+        } catch (AssertionError e) {
+            throw new AssertionError(
+                    String.format("Excessive contrast: foreground=#%08x background=#%08x",
+                            foreground, background), e);
+        }
+    }
+
+}
diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
index 269d842..516a5d2 100644
--- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
+++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
@@ -299,7 +299,7 @@
                     null /* activityOptions */, true /* isForward */, null /* profilerInfo */,
                     mThread /* client */, null /* asssitToken */,
                     null /* fixedRotationAdjustments */, null /* shareableActivityToken */,
-                    false /* launchedFromBubble */);
+                    false /* launchedFromBubble */, null /* taskfragmentToken */);
         }
 
         @Override
diff --git a/core/tests/mockingcoretests/src/android/window/SizeConfigurationBucketsTest.java b/core/tests/mockingcoretests/src/android/window/SizeConfigurationBucketsTest.java
new file mode 100644
index 0000000..fa4aa80
--- /dev/null
+++ b/core/tests/mockingcoretests/src/android/window/SizeConfigurationBucketsTest.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import static android.content.pm.ActivityInfo.CONFIG_LOCALE;
+import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
+import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
+import static android.content.pm.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+import static android.content.res.Configuration.SCREENLAYOUT_COMPAT_NEEDED;
+import static android.content.res.Configuration.SCREENLAYOUT_LAYOUTDIR_LTR;
+import static android.content.res.Configuration.SCREENLAYOUT_LAYOUTDIR_RTL;
+import static android.content.res.Configuration.SCREENLAYOUT_LAYOUTDIR_UNDEFINED;
+import static android.content.res.Configuration.SCREENLAYOUT_LONG_NO;
+import static android.content.res.Configuration.SCREENLAYOUT_LONG_YES;
+import static android.content.res.Configuration.SCREENLAYOUT_ROUND_NO;
+import static android.content.res.Configuration.SCREENLAYOUT_ROUND_UNDEFINED;
+import static android.content.res.Configuration.SCREENLAYOUT_ROUND_YES;
+import static android.content.res.Configuration.SCREENLAYOUT_SIZE_LARGE;
+import static android.content.res.Configuration.SCREENLAYOUT_SIZE_NORMAL;
+import static android.content.res.Configuration.SCREENLAYOUT_SIZE_SMALL;
+import static android.content.res.Configuration.SCREENLAYOUT_SIZE_XLARGE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.res.Configuration;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+/**
+ * Tests for {@link SizeConfigurationBuckets}
+ *
+ * Build/Install/Run:
+ *  atest FrameworksMockingCoreTests:SizeConfigurationBucketsTest
+ */
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class SizeConfigurationBucketsTest {
+
+    /**
+     * Tests that a change in any of the non-size-related screen layout fields results in
+     * {@link SizeConfigurationBuckets#areNonSizeLayoutFieldsUnchanged} returning false.
+     */
+    @Test
+    public void testNonSizeRelatedScreenLayoutFields() {
+        // Test layout direction
+        assertEquals(true, SizeConfigurationBuckets
+                .areNonSizeLayoutFieldsUnchanged(0, SCREENLAYOUT_LAYOUTDIR_UNDEFINED));
+        assertEquals(false, SizeConfigurationBuckets
+                .areNonSizeLayoutFieldsUnchanged(0, SCREENLAYOUT_LAYOUTDIR_LTR));
+        assertEquals(false, SizeConfigurationBuckets
+                .areNonSizeLayoutFieldsUnchanged(0, SCREENLAYOUT_LAYOUTDIR_RTL));
+
+        // Test layout roundness
+        assertEquals(true, SizeConfigurationBuckets
+                .areNonSizeLayoutFieldsUnchanged(0, SCREENLAYOUT_ROUND_UNDEFINED));
+        assertEquals(false, SizeConfigurationBuckets
+                .areNonSizeLayoutFieldsUnchanged(0, SCREENLAYOUT_ROUND_NO));
+        assertEquals(false, SizeConfigurationBuckets
+                .areNonSizeLayoutFieldsUnchanged(0, SCREENLAYOUT_ROUND_YES));
+
+        // Test layout compat needed
+        assertEquals(false, SizeConfigurationBuckets
+                .areNonSizeLayoutFieldsUnchanged(0, SCREENLAYOUT_COMPAT_NEEDED));
+    }
+
+    /**
+     * Tests that null size configuration buckets unflips the correct configuration flags.
+     */
+    @Test
+    public void testNullSizeConfigurationBuckets() {
+        // Check that all 3 size configurations are filtered out of the diff if the buckets are null
+        // and non-size attributes of screen layout are unchanged. Add a non-size related config
+        // change (i.e. CONFIG_LOCALE) to test that the diff is not set to zero.
+        final int diff = CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE | CONFIG_SCREEN_LAYOUT
+                | CONFIG_LOCALE;
+        final int filteredDiffNonSizeLayoutUnchanged = SizeConfigurationBuckets.filterDiff(diff,
+                Configuration.EMPTY, Configuration.EMPTY, null);
+        assertEquals(CONFIG_LOCALE, filteredDiffNonSizeLayoutUnchanged);
+
+        // Check that only screen size and smallest screen size are filtered out of the diff if the
+        // buckets are null and non-size attributes of screen layout are changed.
+        final Configuration newConfig = new Configuration();
+        newConfig.screenLayout |= SCREENLAYOUT_ROUND_YES;
+        final int filteredDiffNonSizeLayoutChanged = SizeConfigurationBuckets.filterDiff(diff,
+                Configuration.EMPTY, newConfig, null);
+        assertEquals(CONFIG_SCREEN_LAYOUT | CONFIG_LOCALE, filteredDiffNonSizeLayoutChanged);
+    }
+
+    /**
+     * Tests that {@link SizeConfigurationBuckets.crossesSizeThreshold()} correctly checks whether
+     * the bucket thresholds have or have not been crossed. This test includes boundary checks
+     * to ensure that arithmetic is inclusive and exclusive in the right places.
+     */
+    @Test
+    public void testCrossesSizeThreshold() {
+        final int[] thresholds = new int[] { 360, 600 };
+        final int nThresholds = thresholds.length;
+        for (int i = -1; i < nThresholds; i++) {
+            final int minValueInBucket = i < 0 ? 0 : thresholds[i];
+            final int maxValueInBucket = i < nThresholds - 1
+                    ? thresholds[i + 1] - 1 : Integer.MAX_VALUE;
+            final int bucketRange = maxValueInBucket - minValueInBucket;
+            // Set old value to 1/4 in between the two thresholds.
+            final int oldValue = (int) (minValueInBucket + bucketRange * 0.25);
+            // Test 3 values of new value spread across bucket range: minValueInBucket, bucket
+            // midpoint, and max value in bucket. In all 3 cases, the bucket has not changed so
+            // {@link SizeConfigurationBuckets#crossedSizeThreshold()} should return false.
+            checkCrossesSizeThreshold(thresholds, oldValue, minValueInBucket, false);
+            checkCrossesSizeThreshold(thresholds, oldValue,
+                    (int) (minValueInBucket + bucketRange * 0.5), false);
+            checkCrossesSizeThreshold(thresholds, oldValue, maxValueInBucket, false);
+            // Test 4 values of size spread outside of bucket range: more than 1 less than min
+            // value, 1 less than min value, 1 more than max value, and more than 1 more than max
+            // value. In all 4 cases, the bucket has changed so
+            // {@link SizeConfigurationBuckets#crossedSizeThreshold()} should return true.
+            // Only test less than min value if min value > 0.
+            if (minValueInBucket > 0) {
+                checkCrossesSizeThreshold(thresholds, oldValue, minValueInBucket - 20, true);
+                checkCrossesSizeThreshold(thresholds, oldValue, minValueInBucket - 1, true);
+            }
+            // Only test greater than max value if not in highest bucket.
+            if (i < nThresholds - 1) {
+                checkCrossesSizeThreshold(thresholds, oldValue, maxValueInBucket + 1, true);
+                checkCrossesSizeThreshold(thresholds, oldValue, maxValueInBucket + 20, true);
+            }
+        }
+    }
+
+    /**
+     * Tests that if screen layout size changed but did not cross a threshold, the filtered diff
+     * does not include screen layout.
+     */
+    @Test
+    public void testScreenLayoutFilteredIfSizeDidNotCrossThreshold() {
+        // Set only small and large sizes
+        final Configuration[] sizeConfigs = new Configuration[2];
+        sizeConfigs[0] = new Configuration();
+        sizeConfigs[0].screenLayout |= SCREENLAYOUT_SIZE_SMALL;
+        sizeConfigs[1] = new Configuration();
+        sizeConfigs[1].screenLayout |= SCREENLAYOUT_SIZE_LARGE;
+        final SizeConfigurationBuckets sizeBuckets = new SizeConfigurationBuckets(sizeConfigs);
+
+        // Change screen layout size from small to normal and check that screen layout flag is
+        // not part of the diff because a threshold was not crossed.
+        final int diff = CONFIG_SCREEN_LAYOUT;
+        final Configuration oldConfig = new Configuration();
+        oldConfig.screenLayout |= SCREENLAYOUT_SIZE_SMALL;
+        final Configuration newConfig = new Configuration();
+        newConfig.screenLayout |= SCREENLAYOUT_SIZE_NORMAL;
+        final int filteredDiff = SizeConfigurationBuckets.filterDiff(diff, oldConfig, newConfig,
+                sizeBuckets);
+        assertEquals(0, filteredDiff);
+
+        // If a non-size attribute of screen layout changed, then screen layout should not be
+        // filtered from the diff.
+        newConfig.screenLayout |= SCREENLAYOUT_ROUND_YES;
+        final int filteredDiffNonSizeLayoutChanged = SizeConfigurationBuckets.filterDiff(diff,
+                oldConfig, newConfig, sizeBuckets);
+        assertEquals(CONFIG_SCREEN_LAYOUT, filteredDiffNonSizeLayoutChanged);
+    }
+
+    /**
+     * Tests that if screen layout size changed and did cross a threshold, the filtered diff
+     * includes screen layout.
+     */
+    @Test
+    public void testScreenLayoutNotFilteredIfSizeCrossedThreshold() {
+        // Set only small and normal sizes
+        final Configuration[] sizeConfigs = new Configuration[2];
+        sizeConfigs[0] = new Configuration();
+        sizeConfigs[0].screenLayout |= SCREENLAYOUT_SIZE_SMALL;
+        sizeConfigs[1] = new Configuration();
+        sizeConfigs[1].screenLayout |= SCREENLAYOUT_SIZE_NORMAL;
+        final SizeConfigurationBuckets sizeBuckets = new SizeConfigurationBuckets(sizeConfigs);
+
+        // Change screen layout size from small to normal and check that screen layout flag is
+        // still part of the diff because a threshold was crossed.
+        final int diff = CONFIG_SCREEN_LAYOUT;
+        final Configuration oldConfig = new Configuration();
+        oldConfig.screenLayout |= SCREENLAYOUT_SIZE_SMALL;
+        final Configuration newConfig = new Configuration();
+        newConfig.screenLayout |= SCREENLAYOUT_SIZE_NORMAL;
+        final int filteredDiff = SizeConfigurationBuckets.filterDiff(diff, oldConfig, newConfig,
+                sizeBuckets);
+        assertEquals(CONFIG_SCREEN_LAYOUT, filteredDiff);
+    }
+
+    /**
+     * Tests that anytime screen layout size is decreased, the filtered diff still includes screen
+     * layout.
+     */
+    @Test
+    public void testScreenLayoutNotFilteredIfSizeDecreased() {
+        // The size thresholds can be anything, but can't be null
+        final int[] horizontalThresholds = new int[] { 360, 600 };
+        final SizeConfigurationBuckets sizeBuckets = new SizeConfigurationBuckets(
+                horizontalThresholds, null /* vertical */, null /* smallest */,
+                null /* screenLayoutSize */, false /* screenLayoutLongSet */);
+        final int[] sizeValuesInOrder = new int[] {
+                SCREENLAYOUT_SIZE_SMALL, SCREENLAYOUT_SIZE_NORMAL, SCREENLAYOUT_SIZE_LARGE,
+                SCREENLAYOUT_SIZE_XLARGE
+        };
+        final int nSizes = sizeValuesInOrder.length;
+        for (int larger = nSizes - 1; larger > 0; larger--) {
+            for (int smaller = larger - 1; smaller >= 0; smaller--) {
+                final Configuration oldConfig = new Configuration();
+                oldConfig.screenLayout |= sizeValuesInOrder[larger];
+                final Configuration newConfig = new Configuration();
+                newConfig.screenLayout |= sizeValuesInOrder[smaller];
+                assertTrue(String.format("oldSize=%d, newSize=%d", oldConfig.screenLayout,
+                        newConfig.screenLayout),
+                        sizeBuckets.crossesScreenLayoutSizeThreshold(oldConfig, newConfig));
+            }
+        }
+    }
+
+    /**
+     * Tests that if screen layout long changed but did not cross a threshold, the filtered diff
+     * does not include screen layout.
+     */
+    @Test
+    public void testScreenLayoutFilteredIfLongDidNotCrossThreshold() {
+        // Do not set any long threshold
+        final Configuration[] sizeConfigs = new Configuration[1];
+        sizeConfigs[0] = Configuration.EMPTY;
+        final SizeConfigurationBuckets sizeBuckets = new SizeConfigurationBuckets(sizeConfigs);
+
+        // Change screen layout long from not long to long and check that screen layout flag is
+        // not part of the diff because a threshold was not crossed.
+        final int diff = CONFIG_SCREEN_LAYOUT;
+        final Configuration oldConfig = new Configuration();
+        oldConfig.screenLayout |= SCREENLAYOUT_LONG_NO;
+        final Configuration newConfig = new Configuration();
+        newConfig.screenLayout |= SCREENLAYOUT_LONG_YES;
+        final int filteredDiff = SizeConfigurationBuckets.filterDiff(diff, oldConfig, newConfig,
+                sizeBuckets);
+        assertEquals(0, filteredDiff);
+
+        // If a non-size attribute of screen layout changed, then screen layout should not be
+        // filtered from the diff.
+        newConfig.screenLayout |= SCREENLAYOUT_ROUND_YES;
+        final int filteredDiffNonSizeLayoutChanged = SizeConfigurationBuckets.filterDiff(diff,
+                oldConfig, newConfig, sizeBuckets);
+        assertEquals(CONFIG_SCREEN_LAYOUT, filteredDiffNonSizeLayoutChanged);
+    }
+
+    /**
+     * Tests that if screen layout long changed and did cross a threshold, the filtered diff
+     * includes screen layout.
+     */
+    @Test
+    public void testScreenLayoutNotFilteredIfLongCrossedThreshold() {
+        // Set only small and normal sizes
+        final Configuration[] sizeConfigs = new Configuration[1];
+        sizeConfigs[0] = new Configuration();
+        sizeConfigs[0].screenLayout |= SCREENLAYOUT_LONG_NO;
+        final SizeConfigurationBuckets sizeBuckets = new SizeConfigurationBuckets(sizeConfigs);
+
+        // Change screen layout long from not long to long and check that screen layout flag is
+        // still part of the diff because a threshold was crossed.
+        final int diff = CONFIG_SCREEN_LAYOUT;
+        final Configuration oldConfig = new Configuration();
+        oldConfig.screenLayout |= SCREENLAYOUT_LONG_NO;
+        final Configuration newConfig = new Configuration();
+        newConfig.screenLayout |= SCREENLAYOUT_LONG_YES;
+        final int filteredDiff = SizeConfigurationBuckets.filterDiff(diff, oldConfig, newConfig,
+                sizeBuckets);
+        assertEquals(CONFIG_SCREEN_LAYOUT, filteredDiff);
+    }
+
+    /**
+     * Tests that horizontal buckets are correctly checked in
+     * {@link SizeConfigurationBuckets#filterDiff()}.
+     */
+    @Test
+    public void testHorizontalSizeThresholds() {
+        final int[] horizontalThresholds = new int[] { 360, 600 };
+        final SizeConfigurationBuckets sizeBuckets = new SizeConfigurationBuckets(
+                horizontalThresholds, null /* vertical */, null /* smallest */,
+                null /* screenLayoutSize */, false /* screenLayoutLongSet */);
+
+        final Configuration oldConfig = new Configuration();
+        final Configuration newConfig = new Configuration();
+
+        oldConfig.screenWidthDp = 480;
+        // Test that value within bucket filters out screen size config
+        newConfig.screenWidthDp = 520;
+        assertEquals(0, SizeConfigurationBuckets.filterDiff(CONFIG_SCREEN_SIZE, oldConfig,
+                newConfig, sizeBuckets));
+        // Test that value outside bucket does not filter out screen size config
+        newConfig.screenWidthDp = 640;
+        assertEquals(CONFIG_SCREEN_SIZE, SizeConfigurationBuckets.filterDiff(CONFIG_SCREEN_SIZE,
+                oldConfig, newConfig, sizeBuckets));
+    }
+
+    /**
+     * Tests that vertical buckets are correctly checked in
+     * {@link SizeConfigurationBuckets#filterDiff()}.
+     */
+    @Test
+    public void testVerticalSizeThresholds() {
+        final int[] verticalThresholds = new int[] { 360, 600 };
+        final SizeConfigurationBuckets sizeBuckets = new SizeConfigurationBuckets(
+                null, verticalThresholds /* vertical */, null /* smallest */,
+                null /* screenLayoutSize */, false /* screenLayoutLongSet */);
+
+        final Configuration oldConfig = new Configuration();
+        final Configuration newConfig = new Configuration();
+
+        oldConfig.screenHeightDp = 480;
+        // Test that value within bucket filters out screen size config
+        newConfig.screenHeightDp = 520;
+        assertEquals(0, SizeConfigurationBuckets.filterDiff(CONFIG_SCREEN_SIZE, oldConfig,
+                newConfig, sizeBuckets));
+        // Test that value outside bucket does not filter out screen size config
+        newConfig.screenHeightDp = 640;
+        assertEquals(CONFIG_SCREEN_SIZE, SizeConfigurationBuckets.filterDiff(CONFIG_SCREEN_SIZE,
+                oldConfig, newConfig, sizeBuckets));
+    }
+
+    /**
+     * Tests that smallest width buckets are correctly checked in
+     * {@link SizeConfigurationBuckets#filterDiff()}.
+     */
+    @Test
+    public void testSmallestWidthSizeThresholds() {
+        final int[] smallestWidthThresholds = new int[] { 360, 600 };
+        final SizeConfigurationBuckets sizeBuckets = new SizeConfigurationBuckets(
+                null, null /* vertical */, smallestWidthThresholds /* smallest */,
+                null /* screenLayoutSize */, false /* screenLayoutLongSet */);
+
+        final Configuration oldConfig = new Configuration();
+        final Configuration newConfig = new Configuration();
+
+        oldConfig.smallestScreenWidthDp = 480;
+        // Test that value within bucket filters out smallest screen size config
+        newConfig.smallestScreenWidthDp = 520;
+        assertEquals(0, SizeConfigurationBuckets.filterDiff(CONFIG_SMALLEST_SCREEN_SIZE, oldConfig,
+                newConfig, sizeBuckets));
+        // Test that value outside bucket does not filter out smallest screen size config
+        newConfig.smallestScreenWidthDp = 640;
+        assertEquals(CONFIG_SMALLEST_SCREEN_SIZE, SizeConfigurationBuckets.filterDiff(
+                CONFIG_SMALLEST_SCREEN_SIZE, oldConfig, newConfig, sizeBuckets));
+    }
+
+    private void checkCrossesSizeThreshold(int[] thresholds, int oldValue, int newValue,
+            boolean expected) {
+        final String errorString = String.format(
+                "thresholds=%s, oldValue=%d, newValue=%d, expected=%b", Arrays.toString(thresholds),
+                oldValue, newValue, expected);
+        final boolean actual = SizeConfigurationBuckets.crossesSizeThreshold(thresholds, oldValue,
+                newValue);
+        assertEquals(errorString, expected, actual);
+    }
+}
diff --git a/core/tests/utiltests/src/com/android/internal/util/ProcFileReaderTest.java b/core/tests/utiltests/src/com/android/internal/util/ProcFileReaderTest.java
index b6da195..0532628 100644
--- a/core/tests/utiltests/src/com/android/internal/util/ProcFileReaderTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/ProcFileReaderTest.java
@@ -166,6 +166,46 @@
         assertEquals(-1L, reader.nextOptionalLong(-1L));
     }
 
+    public void testInvalidLongs() throws Exception {
+        final ProcFileReader reader = buildReader("12: 34\n56 78@#\n");
+
+        assertEquals(12L, reader.nextLong(true));
+        assertEquals(34L, reader.nextLong(true));
+        reader.finishLine();
+        assertTrue(reader.hasMoreData());
+
+        assertEquals(56L, reader.nextLong(true));
+        assertEquals(78L, reader.nextLong(true));
+        reader.finishLine();
+        assertFalse(reader.hasMoreData());
+    }
+
+    public void testConsecutiveDelimiters() throws Exception {
+        final ProcFileReader reader = buildReader("1 2  3   4     5\n");
+
+        assertEquals(1L, reader.nextLong());
+        assertEquals(2L, reader.nextLong());
+        assertEquals(3L, reader.nextLong());
+        assertEquals(4L, reader.nextLong());
+        assertEquals(5L, reader.nextLong());
+        reader.finishLine();
+        assertFalse(reader.hasMoreData());
+    }
+
+    public void testIgnore() throws Exception {
+        final ProcFileReader reader = buildReader("a b c\n");
+
+        assertEquals("a", reader.nextString());
+        assertTrue(reader.hasMoreData());
+
+        reader.nextIgnored();
+        assertTrue(reader.hasMoreData());
+
+        assertEquals("c", reader.nextString());
+        reader.finishLine();
+        assertFalse(reader.hasMoreData());
+    }
+
     private static ProcFileReader buildReader(String string) throws IOException {
         return buildReader(string, 2048);
     }
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 8caff23..b49b289 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -2599,6 +2599,12 @@
       "group": "WM_DEBUG_CONFIGURATION",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "743418423": {
+      "message": "Sending TaskFragment error exception=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
+    },
     "744171317": {
       "message": "      SKIP: %s",
       "level": "VERBOSE",
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 6c1c2ee..36215ec 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -33,7 +33,7 @@
     private static native Surface nativeGetSurface(long ptr, boolean includeSurfaceControlHandle);
     private static native void nativeSetNextTransaction(long ptr, long transactionPtr);
     private static native void nativeUpdate(long ptr, long surfaceControl, long width, long height,
-            int format);
+            int format, long transactionPtr);
     private static native void nativeFlushShadowQueue(long ptr);
     private static native void nativeMergeWithNextTransaction(long ptr, long transactionPtr,
                                                               long frameNumber);
@@ -92,9 +92,15 @@
      * @param width The new width for the buffer.
      * @param height The new height for the buffer.
      * @param format The new format for the buffer.
+     * @param t Adds destination frame changes to the passed in transaction.
      */
+    public void update(SurfaceControl sc, int width, int height, @PixelFormat.Format int format,
+            SurfaceControl.Transaction t) {
+        nativeUpdate(mNativeObject, sc.mNativeObject, width, height, format, t.mNativeObject);
+    }
+
     public void update(SurfaceControl sc, int width, int height, @PixelFormat.Format int format) {
-        nativeUpdate(mNativeObject, sc.mNativeObject, width, height, format);
+        nativeUpdate(mNativeObject, sc.mNativeObject, width, height, format, 0);
     }
 
     /**
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index 5b79d76..884d27f 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -178,22 +178,8 @@
      * <p>Android YUV P010 format.</p>
      *
      * P010 is a 4:2:0 YCbCr semiplanar format comprised of a WxH Y plane
-     * followed immediately by a Wx(H/2) CbCr plane. Each sample is
-     * represented by a 16-bit little-endian value, with the lower 6 bits set
-     * to zero.
-     *
-     * <p>This format assumes
-     * <ul>
-     * <li>an even height</li>
-     * <li>a vertical stride equal to the height</li>
-     * </ul>
-     * </p>
-     *
-     * <pre>   stride_in_bytes = stride * 2 </pre>
-     * <pre>   y_size = stride_in_bytes * height </pre>
-     * <pre>   cbcr_size = stride_in_bytes * (height / 2) </pre>
-     * <pre>   cb_offset = y_size </pre>
-     * <pre>   cr_offset = cb_offset + 2 </pre>
+     * followed by a Wx(H/2) CbCr plane. Each sample is represented by a 16-bit
+     * little-endian value, with the lower 6 bits set to zero.
      *
      * <p>For example, the {@link android.media.Image} object can provide data
      * in this format from a {@link android.hardware.camera2.CameraDevice}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 51bf6d53..42e470b 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -255,12 +255,6 @@
             | FILTER_BITMAP_FLAG;
 
     /**
-     * These flags are always set on a reset paint or a new paint instantiated using
-     * {@link #Paint()}.
-     */
-    private static final int DEFAULT_PAINT_FLAGS = ANTI_ALIAS_FLAG | DITHER_FLAG;
-
-    /**
      * Font hinter option that disables font hinting.
      *
      * @see #setHinting(int)
@@ -577,12 +571,12 @@
      * On devices running {@link Build.VERSION_CODES#Q} and above,
      * {@code FILTER_BITMAP_FLAG} is set by this constructor, and it can be
      * cleared with {@link #setFlags} or {@link #setFilterBitmap}.
-     * On devices running {@link Build.VERSION_CODES#S} and above, {@code ANTI_ALIAS_FLAG} and
-     * {@code DITHER_FLAG} are set by this constructor, and they can be cleared with
-     * {@link #setFlags} or {@link #setAntiAlias} and {@link #setDither}, respectively.</p>
+     * On devices running {@link Build.VERSION_CODES#S} and above, {@code ANTI_ALIAS_FLAG}
+     * is set by this constructor, and it can be cleared with {@link #setFlags} or
+     * {@link #setAntiAlias}.</p>
      */
     public Paint() {
-        this(DEFAULT_PAINT_FLAGS);
+        this(ANTI_ALIAS_FLAG);
     }
 
     /**
@@ -627,7 +621,7 @@
     /** Restores the paint to its default settings. */
     public void reset() {
         nReset(mNativePaint);
-        setFlags(HIDDEN_DEFAULT_PAINT_FLAGS | DEFAULT_PAINT_FLAGS);
+        setFlags(HIDDEN_DEFAULT_PAINT_FLAGS | ANTI_ALIAS_FLAG);
 
         // TODO: Turning off hinting has undesirable side effects, we need to
         //       revisit hinting once we add support for subpixel positioning
diff --git a/graphics/java/android/graphics/drawable/RippleAnimationSession.java b/graphics/java/android/graphics/drawable/RippleAnimationSession.java
index 74fb618..872331c 100644
--- a/graphics/java/android/graphics/drawable/RippleAnimationSession.java
+++ b/graphics/java/android/graphics/drawable/RippleAnimationSession.java
@@ -53,6 +53,7 @@
     private long mStartTime;
     private boolean mForceSoftware;
     private Animator mLoopAnimation;
+    private Animator mCurrentAnimation;
 
     RippleAnimationSession(@NonNull AnimationProperties<Float, Paint> properties,
             boolean forceSoftware) {
@@ -66,7 +67,7 @@
 
     @NonNull RippleAnimationSession enter(Canvas canvas) {
         mStartTime = AnimationUtils.currentAnimationTimeMillis();
-        if (isHwAccelerated(canvas)) {
+        if (useRTAnimations(canvas)) {
             enterHardware((RecordingCanvas) canvas);
         } else {
             enterSoftware();
@@ -74,8 +75,14 @@
         return this;
     }
 
+    void end() {
+        if (mCurrentAnimation != null) {
+            mCurrentAnimation.end();
+        }
+    }
+
     @NonNull RippleAnimationSession exit(Canvas canvas) {
-        if (isHwAccelerated(canvas)) exitHardware((RecordingCanvas) canvas);
+        if (useRTAnimations(canvas)) exitHardware((RecordingCanvas) canvas);
         else exitSoftware();
         return this;
     }
@@ -95,8 +102,12 @@
         return this;
     }
 
-    private boolean isHwAccelerated(Canvas canvas) {
-        return canvas.isHardwareAccelerated() && !mForceSoftware;
+    private boolean useRTAnimations(Canvas canvas) {
+        if (mForceSoftware) return false;
+        if (!canvas.isHardwareAccelerated()) return false;
+        RecordingCanvas hwCanvas = (RecordingCanvas) canvas;
+        if (hwCanvas.mNode == null || !hwCanvas.mNode.isAttached()) return false;
+        return true;
     }
 
     private void exitSoftware() {
@@ -114,10 +125,12 @@
                 if (mLoopAnimation != null) mLoopAnimation.cancel();
                 Consumer<RippleAnimationSession> onEnd = mOnSessionEnd;
                 if (onEnd != null) onEnd.accept(RippleAnimationSession.this);
+                if (mCurrentAnimation == expand) mCurrentAnimation = null;
             }
         });
         expand.setInterpolator(LINEAR_INTERPOLATOR);
         expand.start();
+        mCurrentAnimation = expand;
     }
 
     private long computeDelay() {
@@ -147,6 +160,7 @@
                 if (mLoopAnimation != null) mLoopAnimation.cancel();
                 Consumer<RippleAnimationSession> onEnd = mOnSessionEnd;
                 if (onEnd != null) onEnd.accept(RippleAnimationSession.this);
+                if (mCurrentAnimation == exit) mCurrentAnimation = null;
             }
         });
         exit.setTarget(canvas);
@@ -155,6 +169,7 @@
         long delay = computeDelay();
         exit.setStartDelay(delay);
         exit.start();
+        mCurrentAnimation = exit;
     }
 
     private void enterHardware(RecordingCanvas canvas) {
@@ -167,6 +182,7 @@
                 mStartTime + MAX_NOISE_PHASE);
         loop.setTarget(canvas);
         startAnimation(expand, loop);
+        mCurrentAnimation = expand;
     }
 
     private void startAnimation(Animator expand, Animator loop) {
@@ -200,6 +216,7 @@
             mProperties.getShader().setNoisePhase((float) loop.getAnimatedValue());
         });
         startAnimation(expand, loop);
+        mCurrentAnimation = expand;
     }
 
     void setRadius(float radius) {
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index b994ad2..7354c90 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -49,7 +49,9 @@
 import android.graphics.Rect;
 import android.graphics.Shader;
 import android.os.Build;
+import android.os.Looper;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.animation.AnimationUtils;
 import android.view.animation.LinearInterpolator;
 
@@ -113,6 +115,7 @@
  * @attr ref android.R.styleable#RippleDrawable_color
  */
 public class RippleDrawable extends LayerDrawable {
+    private static final String TAG = "RippleDrawable";
     /**
      * Radius value that specifies the ripple radius should be computed based
      * on the size of the ripple's container.
@@ -278,6 +281,15 @@
         }
 
         cancelExitingRipples();
+        endPatternedAnimations();
+    }
+
+    private void endPatternedAnimations() {
+        for (int i = 0; i < mRunningAnimations.size(); i++) {
+            RippleAnimationSession session = mRunningAnimations.get(i);
+            session.end();
+        }
+        mRunningAnimations.clear();
     }
 
     private void cancelExitingRipples() {
@@ -291,7 +303,6 @@
             Arrays.fill(ripples, 0, count, null);
         }
         mExitingRipplesCount = 0;
-        mExitingAnimation = true;
         // Always draw an additional "clean" frame after canceling animations.
         invalidateSelf(false);
     }
@@ -714,7 +725,7 @@
         }
 
         cancelExitingRipples();
-        exitPatternedAnimation();
+        endPatternedAnimations();
     }
 
     @Override
@@ -840,6 +851,10 @@
 
     private void startBackgroundAnimation() {
         mRunBackgroundAnimation = false;
+        if (Looper.myLooper() == null) {
+            Log.w(TAG, "Thread doesn't have a looper. Skipping animation.");
+            return;
+        }
         mBackgroundAnimation = ValueAnimator.ofFloat(mBackgroundOpacity, mTargetBackgroundOpacity);
         mBackgroundAnimation.setInterpolator(LINEAR_INTERPOLATOR);
         mBackgroundAnimation.setDuration(BACKGROUND_OPACITY_DURATION);
diff --git a/keystore/java/android/security/GenerateRkpKey.java b/keystore/java/android/security/GenerateRkpKey.java
index 053bec74..2e54e63 100644
--- a/keystore/java/android/security/GenerateRkpKey.java
+++ b/keystore/java/android/security/GenerateRkpKey.java
@@ -25,6 +25,8 @@
 import android.util.Log;
 
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -88,7 +90,8 @@
         }
         intent.setComponent(comp);
         mCountDownLatch = new CountDownLatch(1);
-        if (!mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
+        Executor executor = Executors.newCachedThreadPool();
+        if (!mContext.bindService(intent, Context.BIND_AUTO_CREATE, executor, mConnection)) {
             throw new RemoteException("Failed to bind to GenerateRkpKeyService");
         }
         try {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index f3cfcf1..67358c4 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -579,7 +579,7 @@
         //
         // Note: mNamespace == KeyProperties.NAMESPACE_APPLICATION implies that the target domain
         // is Domain.APP and Domain.SELINUX is the target domain otherwise.
-        if (alias != descriptor.alias
+        if (!alias.equals(descriptor.alias)
                 || descriptor.domain != targetDomain
                 || (descriptor.domain == Domain.SELINUX && descriptor.nspace != targetNamespace)) {
             throw new KeyStoreException("Can only replace keys with same alias: " + alias
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java
index b7a6039..ce4e103 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java
@@ -18,6 +18,10 @@
 
 import android.content.Context;
 
+import androidx.annotation.NonNull;
+import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
+import androidx.window.extensions.organizer.EmbeddingExtensionImpl;
+
 /**
  * Provider class that will instantiate the library implementation. It must be included in the
  * vendor library, and the vendor implementation must match the signature of this class.
@@ -31,6 +35,12 @@
         return new SampleExtensionImpl(context);
     }
 
+    /** Provides a reference implementation of {@link ActivityEmbeddingComponent}. */
+    public static ActivityEmbeddingComponent getActivityEmbeddingExtensionImpl(
+            @NonNull Context context) {
+        return new EmbeddingExtensionImpl();
+    }
+
     /**
      * The support library will use this method to check API version compatibility.
      * @return API version string in MAJOR.MINOR.PATCH-description format.
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
index cafc233..a0d5b00 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
@@ -23,19 +23,15 @@
 
 import android.app.Activity;
 import android.content.Context;
-import android.content.Intent;
 import android.graphics.Rect;
-import android.os.Bundle;
 import android.util.Log;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.window.common.DeviceStateManagerPostureProducer;
 import androidx.window.common.DisplayFeature;
 import androidx.window.common.ResourceConfigDisplayFeatureProducer;
 import androidx.window.common.SettingsDevicePostureProducer;
 import androidx.window.common.SettingsDisplayFeatureProducer;
-import androidx.window.extensions.organizer.SplitController;
 import androidx.window.util.DataProducer;
 import androidx.window.util.PriorityDataProducer;
 
@@ -60,8 +56,6 @@
     private final SettingsDisplayFeatureProducer mSettingsDisplayFeatureProducer;
     private final DataProducer<List<DisplayFeature>> mDisplayFeatureProducer;
 
-    private final SplitController mSplitController;
-
     SampleExtensionImpl(Context context) {
         mSettingsDevicePostureProducer = new SettingsDevicePostureProducer(context);
         mDevicePostureProducer = new PriorityDataProducer<>(List.of(
@@ -77,8 +71,6 @@
 
         mDevicePostureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
         mDisplayFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
-
-        mSplitController = new SplitController();
     }
 
     private int getFeatureState(DisplayFeature feature) {
@@ -142,28 +134,4 @@
 
         onDisplayFeaturesChanged();
     }
-
-    @Override
-    public void setSplitRules(@NonNull List<ExtensionSplitRule> splitRules) {
-        mSplitController.setSplitRules(splitRules);
-    }
-
-    @Override
-    @NonNull
-    public List<ExtensionSplitRule> getSplitRules() {
-        return new ArrayList<>(mSplitController.getSplitRules());
-    }
-
-    @Override
-    public void setSplitOrganizerCallback(@Nullable SplitOrganizerCallback callback) {
-        mSplitController.setSplitOrganizerCallback(callback);
-    }
-
-    @Override
-    public void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent intent,
-            @Nullable Bundle options, @NonNull ExtensionSplitPairRule splitPairRule,
-            int startRequestId) {
-        mSplitController.startActivityToSide(launchingActivity, intent, options, splitPairRule,
-                startRequestId);
-    }
 }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/EmbeddingExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/EmbeddingExtensionImpl.java
new file mode 100644
index 0000000..9a8961f
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/EmbeddingExtensionImpl.java
@@ -0,0 +1,48 @@
+/*
+ * 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 androidx.window.extensions.organizer;
+
+import androidx.annotation.NonNull;
+import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
+import androidx.window.extensions.embedding.EmbeddingRule;
+import androidx.window.extensions.embedding.SplitInfo;
+
+import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+
+/**
+ * Reference implementation of the activity embedding interface defined in WM Jetpack.
+ */
+public class EmbeddingExtensionImpl implements ActivityEmbeddingComponent {
+
+    private final SplitController mSplitController;
+
+    public EmbeddingExtensionImpl() {
+        mSplitController = new SplitController();
+    }
+
+    @Override
+    public void setEmbeddingRules(@NonNull Set<EmbeddingRule> rules) {
+        mSplitController.setEmbeddingRules(rules);
+    }
+
+    @Override
+    public void setEmbeddingCallback(@NonNull Consumer<List<SplitInfo>> consumer) {
+        mSplitController.setEmbeddingCallback(consumer);
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java
index 27bd53d..4206d03 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/JetpackTaskFragmentOrganizer.java
@@ -36,6 +36,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.window.extensions.embedding.SplitRule;
 
 import java.util.Map;
 import java.util.concurrent.Executor;
@@ -96,11 +97,11 @@
      * @param activityIntent    Intent to start the secondary Activity with.
      * @param activityOptions   ActivityOptions to start the secondary Activity with.
      */
-    void startActivityToSide(IBinder launchingFragmentToken, Rect launchingFragmentBounds,
-            Activity launchingActivity, IBinder secondaryFragmentToken,
-            Rect secondaryFragmentBounds,  Intent activityIntent,
-            @Nullable Bundle activityOptions) {
-        final WindowContainerTransaction wct = new WindowContainerTransaction();
+    void startActivityToSide(@NonNull WindowContainerTransaction wct,
+            @NonNull IBinder launchingFragmentToken, @NonNull Rect launchingFragmentBounds,
+            @NonNull Activity launchingActivity, @NonNull IBinder secondaryFragmentToken,
+            @NonNull Rect secondaryFragmentBounds, @NonNull Intent activityIntent,
+            @Nullable Bundle activityOptions, @NonNull SplitRule rule) {
         final IBinder ownerToken = launchingActivity.getActivityToken();
 
         // Create or resize the launching TaskFragment.
@@ -116,7 +117,8 @@
                 secondaryFragmentBounds, WINDOWING_MODE_MULTI_WINDOW, activityIntent,
                 activityOptions);
 
-        applyTransaction(wct);
+        // Set adjacent to each other so that the containers below will be invisible.
+        setAdjacentTaskFragments(wct, launchingFragmentToken, secondaryFragmentToken, rule);
     }
 
     /**
@@ -126,6 +128,7 @@
      */
     void expandTaskFragment(WindowContainerTransaction wct, IBinder fragmentToken) {
         resizeTaskFragment(wct, fragmentToken, new Rect());
+        setAdjacentTaskFragments(wct, fragmentToken, null /* secondary */, null /* splitRule */);
     }
 
     /**
@@ -155,13 +158,22 @@
      * @param ownerToken The token of the activity that creates this task fragment. It does not
      *                   have to be a child of this task fragment, but must belong to the same task.
      */
+    void createTaskFragment(WindowContainerTransaction wct, IBinder fragmentToken,
+            IBinder ownerToken, @NonNull Rect bounds, @WindowingMode int windowingMode) {
+        final TaskFragmentCreationParams fragmentOptions =
+                createFragmentOptions(fragmentToken, ownerToken, bounds, windowingMode);
+        wct.createTaskFragment(fragmentOptions);
+    }
+
+    /**
+     * @param ownerToken The token of the activity that creates this task fragment. It does not
+     *                   have to be a child of this task fragment, but must belong to the same task.
+     */
     private void createTaskFragmentAndReparentActivity(
             WindowContainerTransaction wct, IBinder fragmentToken, IBinder ownerToken,
             @NonNull Rect bounds, @WindowingMode int windowingMode, Activity activity) {
-        final TaskFragmentCreationParams fragmentOptions =
-                createFragmentOptions(fragmentToken, ownerToken, bounds, windowingMode);
-        wct.createTaskFragment(fragmentOptions)
-                .reparentActivityToTaskFragment(fragmentToken, activity.getActivityToken());
+        createTaskFragment(wct, fragmentToken, ownerToken, bounds, windowingMode);
+        wct.reparentActivityToTaskFragment(fragmentToken, activity.getActivityToken());
     }
 
     /**
@@ -172,11 +184,23 @@
             WindowContainerTransaction wct, IBinder fragmentToken, IBinder ownerToken,
             @NonNull Rect bounds, @WindowingMode int windowingMode, Intent activityIntent,
             @Nullable Bundle activityOptions) {
-        final TaskFragmentCreationParams fragmentOptions =
-                createFragmentOptions(fragmentToken, ownerToken, bounds, windowingMode);
-        wct.createTaskFragment(fragmentOptions)
-                .startActivityInTaskFragment(fragmentToken, ownerToken, activityIntent,
-                        activityOptions);
+        createTaskFragment(wct, fragmentToken, ownerToken, bounds, windowingMode);
+        wct.startActivityInTaskFragment(fragmentToken, ownerToken, activityIntent, activityOptions);
+    }
+
+    void setAdjacentTaskFragments(@NonNull WindowContainerTransaction wct,
+            @NonNull IBinder primary, @Nullable IBinder secondary, @Nullable SplitRule splitRule) {
+        WindowContainerTransaction.TaskFragmentAdjacentOptions adjacentOptions = null;
+        final boolean finishSecondaryWithPrimary =
+                splitRule != null && SplitContainer.shouldFinishSecondaryWithPrimary(splitRule);
+        final boolean finishPrimaryWithSecondary =
+                splitRule != null && SplitContainer.shouldFinishPrimaryWithSecondary(splitRule);
+        if (finishSecondaryWithPrimary || finishPrimaryWithSecondary) {
+            adjacentOptions = new WindowContainerTransaction.TaskFragmentAdjacentOptions();
+            adjacentOptions.setDelayPrimaryLastActivityRemoval(finishSecondaryWithPrimary);
+            adjacentOptions.setDelaySecondaryLastActivityRemoval(finishPrimaryWithSecondary);
+        }
+        wct.setAdjacentTaskFragments(primary, secondary, adjacentOptions);
     }
 
     TaskFragmentCreationParams createFragmentOptions(IBinder fragmentToken, IBinder ownerToken,
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java
index ade8573..4fd2126 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitContainer.java
@@ -19,7 +19,9 @@
 import android.annotation.NonNull;
 import android.app.Activity;
 
-import androidx.window.extensions.ExtensionSplitPairRule;
+import androidx.window.extensions.embedding.SplitPairRule;
+import androidx.window.extensions.embedding.SplitPlaceholderRule;
+import androidx.window.extensions.embedding.SplitRule;
 
 /**
  * Client-side descriptor of a split that holds two containers.
@@ -27,20 +29,20 @@
 class SplitContainer {
     private final TaskFragmentContainer mPrimaryContainer;
     private final TaskFragmentContainer mSecondaryContainer;
-    private final ExtensionSplitPairRule mSplitPairRule;
+    private final SplitRule mSplitRule;
 
     SplitContainer(@NonNull TaskFragmentContainer primaryContainer,
             @NonNull Activity primaryActivity,
             @NonNull TaskFragmentContainer secondaryContainer,
-            @NonNull ExtensionSplitPairRule splitPairRule) {
+            @NonNull SplitRule splitRule) {
         mPrimaryContainer = primaryContainer;
         mSecondaryContainer = secondaryContainer;
-        mSplitPairRule = splitPairRule;
+        mSplitRule = splitRule;
 
-        if (mSplitPairRule.finishPrimaryWithSecondary || mSplitPairRule.useAsPlaceholder) {
+        if (shouldFinishPrimaryWithSecondary(splitRule)) {
             mSecondaryContainer.addActivityToFinishOnExit(primaryActivity);
         }
-        if (mSplitPairRule.finishSecondaryWithPrimary || mSplitPairRule.useAsPlaceholder) {
+        if (shouldFinishSecondaryWithPrimary(splitRule)) {
             mPrimaryContainer.addContainerToFinishOnExit(mSecondaryContainer);
         }
     }
@@ -56,7 +58,25 @@
     }
 
     @NonNull
-    ExtensionSplitPairRule getSplitPairRule() {
-        return mSplitPairRule;
+    SplitRule getSplitRule() {
+        return mSplitRule;
+    }
+
+    boolean isPlaceholderContainer() {
+        return (mSplitRule instanceof SplitPlaceholderRule);
+    }
+
+    static boolean shouldFinishPrimaryWithSecondary(@NonNull SplitRule splitRule) {
+        final boolean isPlaceholderContainer = splitRule instanceof SplitPlaceholderRule;
+        final boolean shouldFinishPrimaryWithSecondary = (splitRule instanceof SplitPairRule)
+                && ((SplitPairRule) splitRule).shouldFinishPrimaryWithSecondary();
+        return shouldFinishPrimaryWithSecondary || isPlaceholderContainer;
+    }
+
+    static boolean shouldFinishSecondaryWithPrimary(@NonNull SplitRule splitRule) {
+        final boolean isPlaceholderContainer = splitRule instanceof SplitPlaceholderRule;
+        final boolean shouldFinishSecondaryWithPrimary = (splitRule instanceof SplitPairRule)
+                && ((SplitPairRule) splitRule).shouldFinishSecondaryWithPrimary();
+        return shouldFinishSecondaryWithPrimary || isPlaceholderContainer;
     }
 }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitController.java
index 407c43d..05c6792 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitController.java
@@ -20,29 +20,35 @@
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.ActivityClient;
+import android.app.ActivityOptions;
 import android.app.ActivityThread;
 import android.app.Application.ActivityLifecycleCallbacks;
-import android.content.ComponentName;
+import android.app.Instrumentation;
+import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
+import android.util.Pair;
 import android.window.TaskFragmentAppearedInfo;
 import android.window.TaskFragmentInfo;
 import android.window.WindowContainerTransaction;
 
-import androidx.window.extensions.ExtensionInterface.SplitOrganizerCallback;
-import androidx.window.extensions.ExtensionSplitActivityRule;
-import androidx.window.extensions.ExtensionSplitInfo;
-import androidx.window.extensions.ExtensionSplitPairRule;
-import androidx.window.extensions.ExtensionSplitRule;
-import androidx.window.extensions.ExtensionTaskFragment;
+import androidx.window.extensions.embedding.ActivityRule;
+import androidx.window.extensions.embedding.EmbeddingRule;
+import androidx.window.extensions.embedding.SplitInfo;
+import androidx.window.extensions.embedding.SplitPairRule;
+import androidx.window.extensions.embedding.SplitPlaceholderRule;
+import androidx.window.extensions.embedding.SplitRule;
+import androidx.window.extensions.embedding.TaskFragment;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 
 /**
  * Main controller class that manages split states and presentation.
@@ -52,27 +58,32 @@
     private final SplitPresenter mPresenter;
 
     // Currently applied split configuration.
-    private final List<ExtensionSplitRule> mSplitRules = new ArrayList<>();
+    private final List<EmbeddingRule> mSplitRules = new ArrayList<>();
     private final List<TaskFragmentContainer> mContainers = new ArrayList<>();
     private final List<SplitContainer> mSplitContainers = new ArrayList<>();
 
     // Callback to Jetpack to notify about changes to split states.
-    private SplitOrganizerCallback mSplitOrganizerCallback;
+    private @NonNull Consumer<List<SplitInfo>> mEmbeddingCallback;
 
     public SplitController() {
         mPresenter = new SplitPresenter(new MainThreadExecutor(), this);
+        ActivityThread activityThread = ActivityThread.currentActivityThread();
         // Register a callback to be notified about activities being created.
-        ActivityThread.currentActivityThread().getApplication().registerActivityLifecycleCallbacks(
+        activityThread.getApplication().registerActivityLifecycleCallbacks(
                 new LifecycleCallbacks());
+        // Intercept activity starts to route activities to new containers if necessary.
+        Instrumentation instrumentation = activityThread.getInstrumentation();
+        instrumentation.addMonitor(new ActivityStartMonitor());
     }
 
-    public void setSplitRules(@NonNull List<ExtensionSplitRule> splitRules) {
+    /** Updates the embedding rules applied to future activity launches. */
+    public void setEmbeddingRules(@NonNull Set<EmbeddingRule> rules) {
         mSplitRules.clear();
-        mSplitRules.addAll(splitRules);
+        mSplitRules.addAll(rules);
     }
 
     @NonNull
-    public List<ExtensionSplitRule> getSplitRules() {
+    public List<EmbeddingRule> getSplitRules() {
         return mSplitRules;
     }
 
@@ -80,22 +91,20 @@
      * Starts an activity to side of the launchingActivity with the provided split config.
      */
     public void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent intent,
-            @Nullable Bundle options, @NonNull ExtensionSplitPairRule splitPairRule,
-            int startRequestId) {
+            @Nullable Bundle options, @NonNull SplitRule sideRule,
+            @NonNull Consumer<Exception> failureCallback) {
         try {
-            mPresenter.startActivityToSide(launchingActivity, intent, options, splitPairRule);
+            mPresenter.startActivityToSide(launchingActivity, intent, options, sideRule);
         } catch (Exception e) {
-            if (mSplitOrganizerCallback != null && startRequestId != -1) {
-                mSplitOrganizerCallback.onActivityFailedToStartInContainer(startRequestId, e);
-            }
+            failureCallback.accept(e);
         }
     }
 
     /**
      * Registers the split organizer callback to notify about changes to active splits.
      */
-    public void setSplitOrganizerCallback(@NonNull SplitOrganizerCallback callback) {
-        mSplitOrganizerCallback = callback;
+    public void setEmbeddingCallback(@NonNull Consumer<List<SplitInfo>> callback) {
+        mEmbeddingCallback = callback;
         updateCallbackIfNecessary();
     }
 
@@ -118,8 +127,10 @@
         }
 
         container.setInfo(taskFragmentInfo);
-        if (taskFragmentInfo.isEmpty()) {
-            cleanupContainer(container, true /* shouldFinishDependent */);
+        // Check if there are no running activities - consider the container empty if there are no
+        // non-finishing activities left.
+        if (!taskFragmentInfo.hasRunningActivity()) {
+            mPresenter.cleanupContainer(container, true /* shouldFinishDependent */);
             updateCallbackIfNecessary();
         }
     }
@@ -131,7 +142,7 @@
             return;
         }
 
-        cleanupContainer(container, true /* shouldFinishDependent */);
+        mPresenter.cleanupContainer(container, true /* shouldFinishDependent */);
         updateCallbackIfNecessary();
     }
 
@@ -151,14 +162,12 @@
      */
     // TODO(b/190433398): Break down into smaller functions.
     void onActivityCreated(@NonNull Activity launchedActivity) {
-        final ComponentName componentName = launchedActivity.getComponentName();
-
-        final List<ExtensionSplitRule> splitRules = getSplitRules();
+        final List<EmbeddingRule> splitRules = getSplitRules();
         final TaskFragmentContainer currentContainer = getContainerWithActivity(
-                launchedActivity.getActivityToken());
+                launchedActivity.getActivityToken(), launchedActivity);
 
         // Check if the activity is configured to always be expanded.
-        if (shouldExpand(componentName, splitRules)) {
+        if (shouldExpand(launchedActivity, splitRules)) {
             if (shouldContainerBeExpanded(currentContainer)) {
                 // Make sure that the existing container is expanded
                 mPresenter.expandTaskFragment(currentContainer.getTaskFragmentToken());
@@ -215,8 +224,8 @@
             }
         }
 
-        final ExtensionSplitPairRule splitPairRule = getSplitRule(
-                activityBelow.getComponentName(), componentName, splitRules);
+        final SplitPairRule splitPairRule = getSplitRule(activityBelow, launchedActivity,
+                splitRules);
         if (splitPairRule == null) {
             return;
         }
@@ -247,9 +256,29 @@
      */
     @Nullable
     TaskFragmentContainer getContainerWithActivity(@NonNull IBinder activityToken) {
+        return getContainerWithActivity(activityToken, null /* activityToAdd */);
+    }
+
+    /**
+     * This method can only be called from {@link #onActivityCreated(Activity)}, use
+     * {@link #getContainerWithActivity(IBinder) } otherwise.
+     *
+     * Returns a container that this activity is registered with. The activity could be created
+     * before the container appeared, adding the activity to the container if so.
+     */
+    @Nullable
+    private TaskFragmentContainer getContainerWithActivity(@NonNull IBinder activityToken,
+            Activity activityToAdd) {
+        final IBinder taskFragmentToken = ActivityThread.currentActivityThread().getActivityClient(
+                activityToken).mInitialTaskFragmentToken;
         for (TaskFragmentContainer container : mContainers) {
             if (container.hasActivity(activityToken)) {
                 return container;
+            } else if (container.getTaskFragmentToken().equals(taskFragmentToken)) {
+                if (activityToAdd != null) {
+                    container.addPendingAppearedActivity(activityToAdd);
+                }
+                return container;
             }
         }
 
@@ -267,24 +296,25 @@
     }
 
     /**
-     * Creates and registers a new split with the provided containers and configuration.
+     * Creates and registers a new split with the provided containers and configuration. Finishes
+     * existing secondary containers if found for the given primary container.
      */
-    void registerSplit(@NonNull TaskFragmentContainer primaryContainer,
-            @NonNull Activity primaryActivity,
+    void registerSplit(@NonNull WindowContainerTransaction wct,
+            @NonNull TaskFragmentContainer primaryContainer, @NonNull Activity primaryActivity,
             @NonNull TaskFragmentContainer secondaryContainer,
-            @NonNull ExtensionSplitPairRule splitPairRule) {
+            @NonNull SplitRule splitRule) {
+        if (splitRule instanceof SplitPairRule && ((SplitPairRule) splitRule).shouldClearTop()) {
+            removeExistingSecondaryContainers(wct, primaryContainer);
+        }
         SplitContainer splitContainer = new SplitContainer(primaryContainer, primaryActivity,
-                secondaryContainer, splitPairRule);
+                secondaryContainer, splitRule);
         mSplitContainers.add(splitContainer);
     }
 
-    void cleanupContainer(@NonNull TaskFragmentContainer container, boolean shouldFinishDependent) {
-        if (container.isFinished()) {
-            return;
-        }
-
-        container.finish(shouldFinishDependent);
-
+    /**
+     * Removes the container from bookkeeping records.
+     */
+    void removeContainer(@NonNull TaskFragmentContainer container) {
         // Remove all split containers that included this one
         mContainers.remove(container);
         List<SplitContainer> containersToRemove = new ArrayList<>();
@@ -295,8 +325,25 @@
             }
         }
         mSplitContainers.removeAll(containersToRemove);
+    }
 
-        mPresenter.deleteContainer(container);
+    /**
+     * Removes a secondary container for the given primary container if an existing split is
+     * already registered.
+     */
+    void removeExistingSecondaryContainers(@NonNull WindowContainerTransaction wct,
+            @NonNull TaskFragmentContainer primaryContainer) {
+        // If the primary container was already in a split - remove the secondary container that
+        // is now covered by the new one that replaced it.
+        final SplitContainer existingSplitContainer = getActiveSplitForContainer(
+                primaryContainer);
+        if (existingSplitContainer == null
+                || primaryContainer == existingSplitContainer.getSecondaryContainer()) {
+            return;
+        }
+
+        existingSplitContainer.getSecondaryContainer().finish(
+                false /* shouldFinishDependent */, mPresenter, wct, this);
     }
 
     /**
@@ -410,22 +457,20 @@
         }
 
         // Check if there is enough space for launch
-        final ExtensionSplitPairRule placeholderRule = getPlaceholderRule(
-                activity.getComponentName());
+        final SplitPlaceholderRule placeholderRule = getPlaceholderRule(activity);
         if (placeholderRule == null || !mPresenter.shouldShowSideBySide(
                 mPresenter.getParentContainerBounds(activity), placeholderRule)) {
             return false;
         }
 
-        Intent placeholderIntent = new Intent();
-        placeholderIntent.setComponent(placeholderRule.secondaryActivityName);
         // TODO(b/190433398): Handle failed request
-        startActivityToSide(activity, placeholderIntent, null, placeholderRule, -1);
+        startActivityToSide(activity, placeholderRule.getPlaceholderIntent(), null,
+                placeholderRule, null);
         return true;
     }
 
     private boolean dismissPlaceholderIfNecessary(@NonNull SplitContainer splitContainer) {
-        if (!splitContainer.getSplitPairRule().useAsPlaceholder) {
+        if (!splitContainer.isPlaceholderContainer()) {
             return false;
         }
 
@@ -433,7 +478,7 @@
             return false;
         }
 
-        cleanupContainer(splitContainer.getSecondaryContainer(),
+        mPresenter.cleanupContainer(splitContainer.getSecondaryContainer(),
                 false /* shouldFinishDependent */);
         return true;
     }
@@ -442,15 +487,14 @@
      * Returns the rule to launch a placeholder for the activity with the provided component name
      * if it is configured in the split config.
      */
-    private ExtensionSplitPairRule getPlaceholderRule(@NonNull ComponentName componentName) {
-        for (ExtensionSplitRule rule : mSplitRules) {
-            if (!(rule instanceof ExtensionSplitPairRule)) {
+    private SplitPlaceholderRule getPlaceholderRule(@NonNull Activity activity) {
+        for (EmbeddingRule rule : mSplitRules) {
+            if (!(rule instanceof SplitPlaceholderRule)) {
                 continue;
             }
-            ExtensionSplitPairRule pairRule = (ExtensionSplitPairRule) rule;
-            if (componentName.equals(pairRule.primaryActivityName)
-                    && pairRule.useAsPlaceholder) {
-                return pairRule;
+            SplitPlaceholderRule placeholderRule = (SplitPlaceholderRule) rule;
+            if (placeholderRule.getActivityPredicate().test(activity)) {
+                return placeholderRule;
             }
         }
         return null;
@@ -460,27 +504,27 @@
      * Notifies listeners about changes to split states if necessary.
      */
     private void updateCallbackIfNecessary() {
-        if (mSplitOrganizerCallback == null) {
+        if (mEmbeddingCallback == null) {
             return;
         }
         // TODO(b/190433398): Check if something actually changed
-        mSplitOrganizerCallback.onSplitInfoChanged(getActiveSplitStates());
+        mEmbeddingCallback.accept(getActiveSplitStates());
     }
 
     /**
      * Returns a list of descriptors for currently active split states.
      */
-    private List<ExtensionSplitInfo> getActiveSplitStates() {
-        List<ExtensionSplitInfo> splitStates = new ArrayList<>();
+    private List<SplitInfo> getActiveSplitStates() {
+        List<SplitInfo> splitStates = new ArrayList<>();
         for (SplitContainer container : mSplitContainers) {
-            ExtensionTaskFragment primaryContainer =
-                    new ExtensionTaskFragment(
+            TaskFragment primaryContainer =
+                    new TaskFragment(
                             container.getPrimaryContainer().collectActivities());
-            ExtensionTaskFragment secondaryContainer =
-                    new ExtensionTaskFragment(
+            TaskFragment secondaryContainer =
+                    new TaskFragment(
                             container.getSecondaryContainer().collectActivities());
-            ExtensionSplitInfo splitState = new ExtensionSplitInfo(primaryContainer,
-                    secondaryContainer, container.getSplitPairRule().splitRatio);
+            SplitInfo splitState = new SplitInfo(primaryContainer,
+                    secondaryContainer, container.getSplitRule().getSplitRatio());
             splitStates.add(splitState);
         }
         return splitStates;
@@ -504,23 +548,41 @@
     }
 
     /**
-     * Returns a split rule for the provided pair of component names if available.
+     * Returns a split rule for the provided pair of primary activity and secondary activity intent
+     * if available.
      */
     @Nullable
-    private static ExtensionSplitPairRule getSplitRule(@NonNull ComponentName primaryActivityName,
-            @NonNull ComponentName secondaryActivityName,
-            @NonNull List<ExtensionSplitRule> splitRules) {
-        if (splitRules == null || primaryActivityName == null || secondaryActivityName == null) {
-            return null;
-        }
-
-        for (ExtensionSplitRule rule : splitRules) {
-            if (!(rule instanceof ExtensionSplitPairRule)) {
+    private static SplitPairRule getSplitRule(@NonNull Activity primaryActivity,
+            @NonNull Intent secondaryActivityIntent, @NonNull List<EmbeddingRule> splitRules) {
+        for (EmbeddingRule rule : splitRules) {
+            if (!(rule instanceof SplitPairRule)) {
                 continue;
             }
-            ExtensionSplitPairRule pairRule = (ExtensionSplitPairRule) rule;
-            if (match(secondaryActivityName, pairRule.secondaryActivityName)
-                    && match(primaryActivityName, pairRule.primaryActivityName)) {
+            SplitPairRule pairRule = (SplitPairRule) rule;
+            if (pairRule.getActivityIntentPredicate().test(
+                    new Pair(primaryActivity, secondaryActivityIntent))) {
+                return pairRule;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns a split rule for the provided pair of primary and secondary activities if available.
+     */
+    @Nullable
+    private static SplitPairRule getSplitRule(@NonNull Activity primaryActivity,
+            @NonNull Activity secondaryActivity, @NonNull List<EmbeddingRule> splitRules) {
+        for (EmbeddingRule rule : splitRules) {
+            if (!(rule instanceof SplitPairRule)) {
+                continue;
+            }
+            SplitPairRule pairRule = (SplitPairRule) rule;
+            final Intent intent = secondaryActivity.getIntent();
+            if (pairRule.getActivityPairPredicate().test(
+                    new Pair(primaryActivity, secondaryActivity))
+                    && (intent == null || pairRule.getActivityIntentPredicate().test(
+                            new Pair(primaryActivity, intent)))) {
                 return pairRule;
             }
         }
@@ -541,55 +603,26 @@
      * Returns {@code true} if an Activity with the provided component name should always be
      * expanded to occupy full task bounds. Such activity must not be put in a split.
      */
-    private static boolean shouldExpand(@NonNull ComponentName componentName,
-            List<ExtensionSplitRule> splitRules) {
+    private static boolean shouldExpand(@NonNull Activity activity,
+            List<EmbeddingRule> splitRules) {
         if (splitRules == null) {
             return false;
         }
-        for (ExtensionSplitRule rule : splitRules) {
-            if (!(rule instanceof ExtensionSplitActivityRule)) {
+        for (EmbeddingRule rule : splitRules) {
+            if (!(rule instanceof ActivityRule)) {
                 continue;
             }
-            ExtensionSplitActivityRule activityRule = (ExtensionSplitActivityRule) rule;
-            if (match(componentName, activityRule.activityName)
-                    && activityRule.alwaysExpand) {
+            ActivityRule activityRule = (ActivityRule) rule;
+            if (!activityRule.shouldAlwaysExpand()) {
+                continue;
+            }
+            if (activityRule.getActivityPredicate().test(activity)) {
                 return true;
             }
         }
         return false;
     }
 
-    /** Match check allowing wildcards for activity class name but not package name. */
-    private static boolean match(@NonNull ComponentName activityComponent,
-            @NonNull ComponentName ruleComponent) {
-        if (activityComponent.toString().contains("*")) {
-            throw new IllegalArgumentException("Wildcard can only be part of the rule.");
-        }
-        final boolean packagesMatch =
-                activityComponent.getPackageName().equals(ruleComponent.getPackageName());
-        final boolean classesMatch =
-                activityComponent.getClassName().equals(ruleComponent.getClassName());
-        return packagesMatch && (classesMatch
-                || wildcardMatch(activityComponent.getClassName(), ruleComponent.getClassName()));
-    }
-
-    /**
-     * Checks if the provided name matches the pattern.
-     */
-    private static boolean wildcardMatch(@NonNull String name, @NonNull String pattern) {
-        if (!pattern.contains("*")) {
-            return false;
-        }
-        if (pattern.equals("*")) {
-            return true;
-        }
-        if (pattern.indexOf("*") != pattern.lastIndexOf("*") || !pattern.endsWith("*")) {
-            throw new IllegalArgumentException(
-                    "Name pattern with a wildcard must only contain a single * in the end");
-        }
-        return name.startsWith(pattern.substring(0, pattern.length() - 1));
-    }
-
     private final class LifecycleCallbacks implements ActivityLifecycleCallbacks {
 
         @Override
@@ -644,4 +677,98 @@
             handler.post(r);
         }
     }
+
+    /**
+     * A monitor that intercepts all activity start requests originating in the client process and
+     * can amend them to target a specific task fragment to form a split.
+     */
+    private class ActivityStartMonitor extends Instrumentation.ActivityMonitor {
+
+        @Override
+        public Instrumentation.ActivityResult onStartActivity(@NonNull Context who,
+                @NonNull Intent intent, @NonNull Bundle options) {
+            // TODO(b/190433398): Check if the activity is configured to always be expanded.
+
+            // Check if activity should be put in a split with the activity that launched it.
+            if (!(who instanceof Activity)) {
+                return super.onStartActivity(who, intent, options);
+            }
+            final Activity launchingActivity = (Activity) who;
+
+            if (!setLaunchingToSideContainer(launchingActivity, intent, options)) {
+                setLaunchingInSameContainer(launchingActivity, intent, options);
+            }
+
+            return super.onStartActivity(who, intent, options);
+        }
+
+        /**
+         * Returns {@code true} if the activity that is going to be started via the
+         * {@code intent} should be paired with the {@code launchingActivity} and is set to be
+         * launched in an empty side container.
+         */
+        private boolean setLaunchingToSideContainer(Activity launchingActivity, Intent intent,
+                Bundle options) {
+            final SplitPairRule splitPairRule = getSplitRule(launchingActivity, intent,
+                    getSplitRules());
+            if (splitPairRule == null) {
+                return false;
+            }
+
+            // Create a new split with an empty side container
+            final TaskFragmentContainer secondaryContainer = mPresenter
+                    .createNewSplitWithEmptySideContainer(launchingActivity, splitPairRule);
+
+            // Amend the request to let the WM know that the activity should be placed in the
+            // dedicated container.
+            options.putBinder(ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN,
+                    secondaryContainer.getTaskFragmentToken());
+            return true;
+        }
+
+        /**
+         * Checks if the activity that is going to be started via the {@code intent} should be
+         * paired with the existing top activity which is currently paired with the
+         * {@code launchingActivity}. If so, set the activity to be launched in the same
+         * container of the {@code launchingActivity}.
+         */
+        private void setLaunchingInSameContainer(Activity launchingActivity, Intent intent,
+                Bundle options) {
+            final TaskFragmentContainer launchingContainer = getContainerWithActivity(
+                    launchingActivity.getActivityToken());
+            if (launchingContainer == null) {
+                return;
+            }
+
+            final SplitContainer splitContainer = getActiveSplitForContainer(launchingContainer);
+            if (splitContainer == null) {
+                return;
+            }
+
+            if (splitContainer.getSecondaryContainer() != launchingContainer) {
+                return;
+            }
+
+            // The launching activity is on the secondary container. Retrieve the primary
+            // activity from the other container.
+            Activity primaryActivity =
+                    splitContainer.getPrimaryContainer().getTopNonFinishingActivity();
+            if (primaryActivity == null) {
+                return;
+            }
+
+            final SplitPairRule splitPairRule = getSplitRule(primaryActivity, intent,
+                    getSplitRules());
+            if (splitPairRule == null) {
+                return;
+            }
+
+            // Amend the request to let the WM know that the activity should be placed in the
+            // dedicated container. This is necessary for the case that the activity is started
+            // into a new Task, or new Task will be escaped from the current host Task and be
+            // displayed in fullscreen.
+            options.putBinder(ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN,
+                    launchingContainer.getTaskFragmentToken());
+        }
+    }
 }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java
index 90af72b..ac85ac8 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/SplitPresenter.java
@@ -24,13 +24,16 @@
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.view.WindowInsets;
+import android.view.WindowMetrics;
 import android.window.TaskFragmentCreationParams;
 import android.window.WindowContainerTransaction;
 
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.window.extensions.ExtensionSplitPairRule;
+import androidx.window.extensions.embedding.SplitPairRule;
+import androidx.window.extensions.embedding.SplitRule;
 
 import java.util.concurrent.Executor;
 
@@ -68,11 +71,13 @@
     }
 
     /**
-     * Deletes the provided container and updates the presentation if necessary.
+     * Deletes the specified container and all other associated and dependent containers in the same
+     * transaction.
      */
-    void deleteContainer(TaskFragmentContainer container) {
+    void cleanupContainer(@NonNull TaskFragmentContainer container, boolean shouldFinishDependent) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
-        deleteTaskFragment(wct, container.getTaskFragmentToken());
+
+        container.finish(shouldFinishDependent, this, wct, mController);
 
         final TaskFragmentContainer newTopContainer = mController.getTopActiveContainer();
         if (newTopContainer != null) {
@@ -83,6 +88,38 @@
     }
 
     /**
+     * Creates a new split with the primary activity and an empty secondary container.
+     * @return The newly created secondary container.
+     */
+    TaskFragmentContainer createNewSplitWithEmptySideContainer(@NonNull Activity primaryActivity,
+            @NonNull SplitPairRule rule) {
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+
+        final Rect parentBounds = getParentContainerBounds(primaryActivity);
+        final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule);
+        final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct,
+                primaryActivity, primaryRectBounds, null);
+
+        // Create new empty task fragment
+        TaskFragmentContainer secondaryContainer = mController.newContainer(null);
+        final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule);
+        createTaskFragment(wct, secondaryContainer.getTaskFragmentToken(),
+                primaryActivity.getActivityToken(), secondaryRectBounds,
+                WINDOWING_MODE_MULTI_WINDOW);
+        secondaryContainer.setLastRequestedBounds(secondaryRectBounds);
+
+        // Set adjacent to each other so that the containers below will be invisible.
+        setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
+                secondaryContainer.getTaskFragmentToken(), rule);
+
+        mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule);
+
+        applyTransaction(wct);
+
+        return secondaryContainer;
+    }
+
+    /**
      * Creates a new split container with the two provided activities.
      * @param primaryActivity An activity that should be in the primary container. If it is not
      *                        currently in an existing container, a new one will be created and the
@@ -94,59 +131,57 @@
      * @param rule The split rule to be applied to the container.
      */
     void createNewSplitContainer(@NonNull Activity primaryActivity,
-            @NonNull Activity secondaryActivity, @NonNull ExtensionSplitPairRule rule) {
+            @NonNull Activity secondaryActivity, @NonNull SplitPairRule rule) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
 
         final Rect parentBounds = getParentContainerBounds(primaryActivity);
         final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule);
-        TaskFragmentContainer primaryContainer = mController.getContainerWithActivity(
-                primaryActivity.getActivityToken());
-        if (primaryContainer == null) {
-            primaryContainer = mController.newContainer(primaryActivity);
-
-            final TaskFragmentCreationParams fragmentOptions =
-                    createFragmentOptions(
-                            primaryContainer.getTaskFragmentToken(),
-                            primaryActivity.getActivityToken(),
-                            primaryRectBounds,
-                            WINDOWING_MODE_MULTI_WINDOW);
-            wct.createTaskFragment(fragmentOptions);
-
-            wct.reparentActivityToTaskFragment(primaryContainer.getTaskFragmentToken(),
-                    primaryActivity.getActivityToken());
-
-            primaryContainer.setLastRequestedBounds(primaryRectBounds);
-        } else {
-            resizeTaskFragmentIfRegistered(wct, primaryContainer, primaryRectBounds);
-        }
+        final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct,
+                primaryActivity, primaryRectBounds, null);
 
         final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule);
-        TaskFragmentContainer secondaryContainer = mController.getContainerWithActivity(
-                secondaryActivity.getActivityToken());
-        if (secondaryContainer == null || secondaryContainer == primaryContainer) {
-            secondaryContainer = mController.newContainer(secondaryActivity);
+        final TaskFragmentContainer secondaryContainer = prepareContainerForActivity(wct,
+                secondaryActivity, secondaryRectBounds, primaryContainer);
+
+        // Set adjacent to each other so that the containers below will be invisible.
+        setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
+                secondaryContainer.getTaskFragmentToken(), rule);
+
+        mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule);
+
+        applyTransaction(wct);
+    }
+
+    /**
+     * Creates a new container or resizes an existing container for activity to the provided bounds.
+     * @param activity The activity to be re-parented to the container if necessary.
+     * @param containerToAvoid Re-parent from this container if an activity is already in it.
+     */
+    private TaskFragmentContainer prepareContainerForActivity(
+            @NonNull WindowContainerTransaction wct, @NonNull Activity activity,
+            @NonNull Rect bounds, @Nullable TaskFragmentContainer containerToAvoid) {
+        TaskFragmentContainer container = mController.getContainerWithActivity(
+                activity.getActivityToken());
+        if (container == null || container == containerToAvoid) {
+            container = mController.newContainer(activity);
 
             final TaskFragmentCreationParams fragmentOptions =
                     createFragmentOptions(
-                            secondaryContainer.getTaskFragmentToken(),
-                            secondaryActivity.getActivityToken(),
-                            secondaryRectBounds,
+                            container.getTaskFragmentToken(),
+                            activity.getActivityToken(),
+                            bounds,
                             WINDOWING_MODE_MULTI_WINDOW);
             wct.createTaskFragment(fragmentOptions);
 
-            wct.reparentActivityToTaskFragment(secondaryContainer.getTaskFragmentToken(),
-                    secondaryActivity.getActivityToken());
+            wct.reparentActivityToTaskFragment(container.getTaskFragmentToken(),
+                    activity.getActivityToken());
 
-            secondaryContainer.setLastRequestedBounds(secondaryRectBounds);
+            container.setLastRequestedBounds(bounds);
         } else {
-            resizeTaskFragmentIfRegistered(wct, secondaryContainer, secondaryRectBounds);
+            resizeTaskFragmentIfRegistered(wct, container, bounds);
         }
 
-        // TODO(b/190433398): The primary container and the secondary container should also be set
-        // as adjacent (WCT#setAdjacentRoots) to make activities behind invisible.
-        applyTransaction(wct);
-
-        mController.registerSplit(primaryContainer, primaryActivity, secondaryContainer, rule);
+        return container;
     }
 
     /**
@@ -160,7 +195,7 @@
      * @param rule The split rule to be applied to the container.
      */
     void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent activityIntent,
-            @Nullable Bundle activityOptions, @NonNull ExtensionSplitPairRule rule) {
+            @Nullable Bundle activityOptions, @NonNull SplitRule rule) {
         final Rect parentBounds = getParentContainerBounds(launchingActivity);
         final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule);
         final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule);
@@ -172,23 +207,16 @@
         }
 
         TaskFragmentContainer secondaryContainer = mController.newContainer(null);
-        startActivityToSide(
-                primaryContainer.getTaskFragmentToken(),
-                primaryRectBounds,
-                launchingActivity,
-                secondaryContainer.getTaskFragmentToken(),
-                secondaryRectBounds,
-                activityIntent,
-                activityOptions);
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+        mController.registerSplit(wct, primaryContainer, launchingActivity, secondaryContainer,
+                rule);
+        startActivityToSide(wct, primaryContainer.getTaskFragmentToken(), primaryRectBounds,
+                launchingActivity, secondaryContainer.getTaskFragmentToken(), secondaryRectBounds,
+                activityIntent, activityOptions, rule);
+        applyTransaction(wct);
 
         primaryContainer.setLastRequestedBounds(primaryRectBounds);
         secondaryContainer.setLastRequestedBounds(secondaryRectBounds);
-
-        // TODO(b/190433398): The primary container and the secondary container should also be set
-        // as adjacent (WCT#setAdjacentRoots) to make activities behind invisible.
-
-        mController.registerSplit(primaryContainer, launchingActivity, secondaryContainer,
-                rule);
     }
 
     /**
@@ -202,7 +230,7 @@
             @NonNull WindowContainerTransaction wct) {
         // Getting the parent bounds using the updated container - it will have the recent value.
         final Rect parentBounds = getParentContainerBounds(updatedContainer);
-        final ExtensionSplitPairRule rule = splitContainer.getSplitPairRule();
+        final SplitRule rule = splitContainer.getSplitRule();
         final Rect primaryRectBounds = getBoundsForPosition(POSITION_LEFT, parentBounds, rule);
         final Rect secondaryRectBounds = getBoundsForPosition(POSITION_RIGHT, parentBounds, rule);
 
@@ -248,24 +276,24 @@
 
     boolean shouldShowSideBySide(@NonNull SplitContainer splitContainer) {
         final Rect parentBounds = getParentContainerBounds(splitContainer.getPrimaryContainer());
-        return shouldShowSideBySide(parentBounds, splitContainer.getSplitPairRule());
+        return shouldShowSideBySide(parentBounds, splitContainer.getSplitRule());
     }
 
-    boolean shouldShowSideBySide(@Nullable Rect parentBounds,
-            @NonNull ExtensionSplitPairRule rule) {
-        return parentBounds != null && parentBounds.width() >= rule.minWidth
-                // TODO(b/190433398): Consider proper smallest width computation.
-                && Math.min(parentBounds.width(), parentBounds.height()) >= rule.minSmallestWidth;
+    boolean shouldShowSideBySide(@Nullable Rect parentBounds, @NonNull SplitRule rule) {
+        // TODO(b/190433398): Supply correct insets.
+        final WindowMetrics parentMetrics = new WindowMetrics(parentBounds,
+                new WindowInsets(new Rect()));
+        return rule.getParentWindowMetricsPredicate().test(parentMetrics);
     }
 
     @NonNull
     private Rect getBoundsForPosition(@Position int position, @NonNull Rect parentBounds,
-            @NonNull ExtensionSplitPairRule rule) {
+            @NonNull SplitRule rule) {
         if (!shouldShowSideBySide(parentBounds, rule)) {
             return new Rect();
         }
 
-        float splitRatio = rule.splitRatio;
+        float splitRatio = rule.getSplitRatio();
         switch (position) {
             case POSITION_LEFT:
                 return new Rect(
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentContainer.java
index 368adef..a4f5c75 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/organizer/TaskFragmentContainer.java
@@ -24,6 +24,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.window.TaskFragmentInfo;
+import android.window.WindowContainerTransaction;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -45,10 +46,10 @@
     private TaskFragmentInfo mInfo;
 
     /**
-     * Activity that is being reparented to this container, but haven't been added to {@link #mInfo}
-     * yet.
+     * Activities that are being reparented or being started to this container, but haven't been
+     * added to {@link #mInfo} yet.
      */
-    private Activity mReparentingActivity;
+    private final ArrayList<Activity> mPendingAppearedActivities = new ArrayList<>();
 
     /** Containers that are dependent on this one and should be completely destroyed on exit. */
     private final List<TaskFragmentContainer> mContainersToFinishOnExit =
@@ -71,7 +72,9 @@
      */
     TaskFragmentContainer(@Nullable Activity activity) {
         mToken = new Binder("TaskFragmentContainer");
-        mReparentingActivity = activity;
+        if (activity != null) {
+            addPendingAppearedActivity(activity);
+        }
     }
 
     /**
@@ -89,8 +92,8 @@
         // fragment info update with it placed in this container. We still want to apply rules
         // in this intermediate state.
         List<Activity> allActivities = new ArrayList<>();
-        if (mReparentingActivity != null) {
-            allActivities.add(mReparentingActivity);
+        if (!mPendingAppearedActivities.isEmpty()) {
+            allActivities.addAll(mPendingAppearedActivities);
         }
         // Add activities reported from the server.
         if (mInfo == null) {
@@ -106,12 +109,20 @@
         return allActivities;
     }
 
+    void addPendingAppearedActivity(@NonNull Activity pendingAppearedActivity) {
+        mPendingAppearedActivities.add(pendingAppearedActivity);
+    }
+
     boolean hasActivity(@NonNull IBinder token) {
         if (mInfo != null && mInfo.getActivities().contains(token)) {
             return true;
         }
-        return mReparentingActivity != null
-                && mReparentingActivity.getActivityToken().equals(token);
+        for (Activity activity : mPendingAppearedActivities) {
+            if (activity.getActivityToken().equals(token)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     @Nullable
@@ -121,14 +132,15 @@
 
     void setInfo(@Nullable TaskFragmentInfo info) {
         mInfo = info;
-        if (mInfo == null || mReparentingActivity == null) {
+        if (mInfo == null || mPendingAppearedActivities.isEmpty()) {
             return;
         }
         // Cleanup activities that were being re-parented
-        for (IBinder activityToken : mInfo.getActivities()) {
-            if (mReparentingActivity.getActivityToken().equals(activityToken)) {
-                mReparentingActivity = null;
-                break;
+        List<IBinder> infoActivities = mInfo.getActivities();
+        for (int i = mPendingAppearedActivities.size() - 1; i >= 0; --i) {
+            final Activity activity = mPendingAppearedActivities.get(i);
+            if (infoActivities.contains(activity.getActivityToken())) {
+                mPendingAppearedActivities.remove(i);
             }
         }
     }
@@ -147,7 +159,7 @@
     }
 
     boolean isEmpty() {
-        return mReparentingActivity == null && (mInfo == null || mInfo.isEmpty());
+        return mPendingAppearedActivities.isEmpty() && (mInfo == null || mInfo.isEmpty());
     }
 
     /**
@@ -168,7 +180,8 @@
      * Removes all activities that belong to this process and finishes other containers/activities
      * configured to finish together.
      */
-    void finish(boolean shouldFinishDependent) {
+    void finish(boolean shouldFinishDependent, @NonNull SplitPresenter presenter,
+            @NonNull WindowContainerTransaction wct, @NonNull SplitController controller) {
         if (mIsFinished) {
             return;
         }
@@ -179,13 +192,19 @@
             activity.finish();
         }
 
+        // Cleanup the visuals
+        presenter.deleteTaskFragment(wct, getTaskFragmentToken());
+        // Cleanup the records
+        controller.removeContainer(this);
+
         if (!shouldFinishDependent) {
             return;
         }
 
         // Finish dependent containers
         for (TaskFragmentContainer container : mContainersToFinishOnExit) {
-            container.finish(true /* shouldFinishDependent */);
+            container.finish(true /* shouldFinishDependent */, presenter,
+                    wct, controller);
         }
         mContainersToFinishOnExit.clear();
 
@@ -196,10 +215,10 @@
         mActivitiesToFinishOnExit.clear();
 
         // Finish activities that were being re-parented to this container.
-        if (mReparentingActivity != null) {
-            mReparentingActivity.finish();
-            mReparentingActivity = null;
+        for (Activity activity : mPendingAppearedActivities) {
+            activity.finish();
         }
+        mPendingAppearedActivities.clear();
     }
 
     boolean isFinished() {
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index fdbc5f6..097febf 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/packages/SystemUI/res/values-h560dp-xhdpi/config.xml b/libs/WindowManager/Shell/res/color/split_divider_background.xml
similarity index 65%
copy from packages/SystemUI/res/values-h560dp-xhdpi/config.xml
copy to libs/WindowManager/Shell/res/color/split_divider_background.xml
index cf2017f..84f4fdf 100644
--- a/packages/SystemUI/res/values-h560dp-xhdpi/config.xml
+++ b/libs/WindowManager/Shell/res/color/split_divider_background.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-
 <!--
-  ~ Copyright (C) 2014 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.
@@ -13,11 +12,8 @@
   ~ distributed under the License is distributed on an "AS IS" BASIS,
   ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
+  ~ limitations under the License.
   -->
-<resources>
-    <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
-     card. -->
-    <integer name="keyguard_max_notification_count">3</integer>
-</resources>
-
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@android:color/system_neutral2_500" android:lStar="35" />
+</selector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/bubble_manage_btn_bg.xml b/libs/WindowManager/Shell/res/drawable/bubble_manage_btn_bg.xml
index 8710fb8..96d2d7c 100644
--- a/libs/WindowManager/Shell/res/drawable/bubble_manage_btn_bg.xml
+++ b/libs/WindowManager/Shell/res/drawable/bubble_manage_btn_bg.xml
@@ -18,7 +18,7 @@
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="rectangle">
     <solid
-        android:color="@android:color/system_neutral1_900"
+        android:color="@android:color/system_neutral1_800"
         />
     <corners android:radius="20dp" />
 
diff --git a/libs/WindowManager/Shell/res/layout/bubble_manage_button.xml b/libs/WindowManager/Shell/res/layout/bubble_manage_button.xml
index c09ae53..0cf6d73 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_manage_button.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_manage_button.xml
@@ -17,13 +17,13 @@
 <com.android.wm.shell.common.AlphaOptimizedButton
     xmlns:android="http://schemas.android.com/apk/res/android"
     style="@android:style/Widget.DeviceDefault.Button.Borderless"
-    android:id="@+id/settings_button"
+    android:id="@+id/manage_button"
     android:layout_gravity="start"
     android:layout_width="wrap_content"
-    android:layout_height="40dp"
-    android:layout_marginTop="8dp"
-    android:layout_marginLeft="16dp"
-    android:layout_marginBottom="8dp"
+    android:layout_height="@dimen/bubble_manage_button_height"
+    android:layout_marginStart="@dimen/bubble_manage_button_margin"
+    android:layout_marginTop="@dimen/bubble_manage_button_margin"
+    android:layout_marginBottom="@dimen/bubble_manage_button_margin"
     android:focusable="true"
     android:text="@string/manage_bubbles_text"
     android:textSize="@*android:dimen/text_size_body_2_material"
diff --git a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
index f4b3aca..298ad30 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
@@ -25,15 +25,15 @@
         android:id="@+id/bubble_manage_menu_dismiss_container"
         android:background="@drawable/bubble_manage_menu_row"
         android:layout_width="match_parent"
-        android:layout_height="48dp"
+        android:layout_height="@dimen/bubble_menu_item_height"
         android:gravity="center_vertical"
-        android:paddingStart="16dp"
-        android:paddingEnd="16dp"
+        android:paddingStart="@dimen/bubble_menu_padding"
+        android:paddingEnd="@dimen/bubble_menu_padding"
         android:orientation="horizontal">
 
         <ImageView
-            android:layout_width="24dp"
-            android:layout_height="24dp"
+            android:layout_width="@dimen/bubble_menu_icon_size"
+            android:layout_height="@dimen/bubble_menu_icon_size"
             android:src="@drawable/ic_remove_no_shadow"
             android:tint="@color/bubbles_icon_tint"/>
 
@@ -50,15 +50,15 @@
         android:id="@+id/bubble_manage_menu_dont_bubble_container"
         android:background="@drawable/bubble_manage_menu_row"
         android:layout_width="match_parent"
-        android:layout_height="48dp"
+        android:layout_height="@dimen/bubble_menu_item_height"
         android:gravity="center_vertical"
-        android:paddingStart="16dp"
-        android:paddingEnd="16dp"
+        android:paddingStart="@dimen/bubble_menu_padding"
+        android:paddingEnd="@dimen/bubble_menu_padding"
         android:orientation="horizontal">
 
         <ImageView
-            android:layout_width="24dp"
-            android:layout_height="24dp"
+            android:layout_width="@dimen/bubble_menu_icon_size"
+            android:layout_height="@dimen/bubble_menu_icon_size"
             android:src="@drawable/bubble_ic_stop_bubble"
             android:tint="@color/bubbles_icon_tint"/>
 
@@ -75,16 +75,16 @@
         android:id="@+id/bubble_manage_menu_settings_container"
         android:background="@drawable/bubble_manage_menu_row"
         android:layout_width="match_parent"
-        android:layout_height="48dp"
+        android:layout_height="@dimen/bubble_menu_item_height"
         android:gravity="center_vertical"
-        android:paddingStart="16dp"
-        android:paddingEnd="16dp"
+        android:paddingStart="@dimen/bubble_menu_padding"
+        android:paddingEnd="@dimen/bubble_menu_padding"
         android:orientation="horizontal">
 
         <ImageView
             android:id="@+id/bubble_manage_menu_settings_icon"
-            android:layout_width="24dp"
-            android:layout_height="24dp"
+            android:layout_width="@dimen/bubble_menu_icon_size"
+            android:layout_height="@dimen/bubble_menu_icon_size"
             android:src="@drawable/ic_remove_no_shadow"/>
 
         <TextView
diff --git a/libs/WindowManager/Shell/res/layout/bubble_stack_user_education.xml b/libs/WindowManager/Shell/res/layout/bubble_stack_user_education.xml
index fd4c3ba..87deb8b 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_stack_user_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_stack_user_education.xml
@@ -21,7 +21,6 @@
     android:layout_width="wrap_content"
     android:paddingTop="48dp"
     android:paddingBottom="48dp"
-    android:paddingStart="@dimen/bubble_stack_user_education_side_inset"
     android:paddingEnd="16dp"
     android:layout_marginEnd="24dp"
     android:orientation="vertical"
diff --git a/libs/WindowManager/Shell/res/layout/bubbles_manage_button_education.xml b/libs/WindowManager/Shell/res/layout/bubbles_manage_button_education.xml
index c5c42fc..fafe40e 100644
--- a/libs/WindowManager/Shell/res/layout/bubbles_manage_button_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubbles_manage_button_education.xml
@@ -23,7 +23,6 @@
     android:clickable="true"
     android:paddingTop="28dp"
     android:paddingBottom="16dp"
-    android:paddingStart="@dimen/bubble_expanded_view_padding"
     android:paddingEnd="48dp"
     android:layout_marginEnd="24dp"
     android:orientation="vertical"
@@ -66,27 +65,21 @@
         android:id="@+id/button_layout"
         android:orientation="horizontal" >
 
-        <com.android.wm.shell.common.AlphaOptimizedButton
-            style="@android:style/Widget.Material.Button.Borderless"
-            android:id="@+id/manage"
-            android:layout_gravity="start"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:focusable="true"
-            android:clickable="false"
-            android:text="@string/manage_bubbles_text"
-            android:textColor="@android:color/system_neutral1_900"
+        <include
+            layout="@layout/bubble_manage_button"
             />
 
         <com.android.wm.shell.common.AlphaOptimizedButton
-            style="@android:style/Widget.Material.Button.Borderless"
+            style="@android:style/Widget.DeviceDefault.Button.Borderless"
             android:id="@+id/got_it"
             android:layout_gravity="start"
             android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_height="@dimen/bubble_manage_button_height"
             android:focusable="true"
             android:text="@string/bubbles_user_education_got_it"
+            android:textSize="@*android:dimen/text_size_body_2_material"
             android:textColor="@android:color/system_neutral1_900"
+            android:background="@drawable/bubble_manage_btn_bg"
             />
     </LinearLayout>
 </LinearLayout>
diff --git a/libs/WindowManager/Shell/res/layout/docked_stack_divider.xml b/libs/WindowManager/Shell/res/layout/docked_stack_divider.xml
index ed5d2e1..d732b01 100644
--- a/libs/WindowManager/Shell/res/layout/docked_stack_divider.xml
+++ b/libs/WindowManager/Shell/res/layout/docked_stack_divider.xml
@@ -22,7 +22,7 @@
     <View
         style="@style/DockedDividerBackground"
         android:id="@+id/docked_divider_background"
-        android:background="@color/docked_divider_background"/>
+        android:background="@color/split_divider_background"/>
 
     <com.android.wm.shell.legacysplitscreen.MinimizedDockShadow
         style="@style/DockedDividerMinimizedShadow"
diff --git a/libs/WindowManager/Shell/res/layout/split_divider.xml b/libs/WindowManager/Shell/res/layout/split_divider.xml
index 7f583f3..a1dba51 100644
--- a/libs/WindowManager/Shell/res/layout/split_divider.xml
+++ b/libs/WindowManager/Shell/res/layout/split_divider.xml
@@ -21,8 +21,7 @@
 
     <View
         style="@style/DockedDividerBackground"
-        android:id="@+id/docked_divider_background"
-        android:background="@color/docked_divider_background"/>
+        android:id="@+id/docked_divider_background"/>
 
     <com.android.wm.shell.common.split.DividerHandleView
         style="@style/DockedDividerHandle"
diff --git a/libs/WindowManager/Shell/res/values-land/dimens.xml b/libs/WindowManager/Shell/res/values-land/dimens.xml
index aafba58..a95323f 100644
--- a/libs/WindowManager/Shell/res/values-land/dimens.xml
+++ b/libs/WindowManager/Shell/res/values-land/dimens.xml
@@ -16,8 +16,12 @@
 */
 -->
 <resources>
+    <!-- Divider handle size for legacy split screen -->
     <dimen name="docked_divider_handle_width">2dp</dimen>
     <dimen name="docked_divider_handle_height">16dp</dimen>
+    <!-- Divider handle size for split screen -->
+    <dimen name="split_divider_handle_width">3dp</dimen>
+    <dimen name="split_divider_handle_height">72dp</dimen>
 
     <!-- Padding between status bar and bubbles when displayed in expanded state, smaller
      value in landscape since we have limited vertical space-->
diff --git a/libs/WindowManager/Shell/res/values-land/styles.xml b/libs/WindowManager/Shell/res/values-land/styles.xml
index 863bb69..9eddac4 100644
--- a/libs/WindowManager/Shell/res/values-land/styles.xml
+++ b/libs/WindowManager/Shell/res/values-land/styles.xml
@@ -19,6 +19,7 @@
         <item name="android:layout_width">10dp</item>
         <item name="android:layout_height">match_parent</item>
         <item name="android:layout_gravity">center_horizontal</item>
+        <item name="android:background">@color/split_divider_background</item>
     </style>
 
     <style name="DockedDividerHandle">
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 624b8b3..c2b6ffb 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -31,7 +31,7 @@
     <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"స్టాచ్"</string>
     <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"ఆన్‌స్టాచ్"</string>
     <string name="dock_forced_resizable" msgid="1749750436092293116">"స్క్రీన్ విభజనతో యాప్‌ పని చేయకపోవచ్చు."</string>
-    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"అనువర్తనంలో స్క్రీన్ విభజనకు మద్దతు లేదు."</string>
+    <string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"యాప్‌లో స్క్రీన్ విభజనకు మద్దతు లేదు."</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>
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index 350beaf..93c0352 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -17,7 +17,6 @@
  */
 -->
 <resources>
-    <color name="docked_divider_background">#ff000000</color>
     <color name="docked_divider_handle">#ffffff</color>
     <drawable name="forced_resizable_background">#59000000</drawable>
     <color name="minimize_dock_shadow_start">#60000000</color>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index f28ee82..26c619d 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -76,8 +76,12 @@
     <!-- How high we lift the divider when touching -->
     <dimen name="docked_stack_divider_lift_elevation">4dp</dimen>
 
+    <!-- Divider handle size for legacy split screen -->
     <dimen name="docked_divider_handle_width">16dp</dimen>
     <dimen name="docked_divider_handle_height">2dp</dimen>
+    <!-- Divider handle size for split screen -->
+    <dimen name="split_divider_handle_width">72dp</dimen>
+    <dimen name="split_divider_handle_height">3dp</dimen>
 
     <!-- One-Handed Mode -->
     <!-- Threshold for dragging distance to enable one-handed mode -->
@@ -100,6 +104,8 @@
     <dimen name="bubble_flyout_space_from_bubble">8dp</dimen>
     <!-- How much space to leave between the flyout text and the avatar displayed in the flyout. -->
     <dimen name="bubble_flyout_avatar_message_space">6dp</dimen>
+    <!-- If the screen percentage is smaller than this, we'll use this value instead.  -->
+    <dimen name="bubbles_flyout_min_width_large_screen">200dp</dimen>
     <!-- Padding between status bar and bubbles when displayed in expanded state -->
     <dimen name="bubble_padding_top">16dp</dimen>
     <!-- Space between bubbles when expanded. -->
@@ -122,7 +128,7 @@
          should also be updated. -->
     <dimen name="bubble_expanded_default_height">180dp</dimen>
     <!-- On large screens the width of the expanded view is restricted to this size. -->
-    <dimen name="bubble_expanded_view_tablet_width">412dp</dimen>
+    <dimen name="bubble_expanded_view_phone_landscape_overflow_width">412dp</dimen>
     <!-- Inset to apply to the icon in the overflow button. -->
     <dimen name="bubble_overflow_icon_inset">30dp</dimen>
     <!-- Default (and minimum) height of bubble overflow -->
@@ -149,9 +155,17 @@
     <!-- Extra padding around the dismiss target for bubbles -->
     <dimen name="bubble_dismiss_slop">16dp</dimen>
     <!-- Height of button allowing users to adjust settings for bubbles. -->
-    <dimen name="bubble_manage_button_height">56dp</dimen>
+    <dimen name="bubble_manage_button_height">36dp</dimen>
+    <!-- Height of manage button including margins. -->
+    <dimen name="bubble_manage_button_total_height">68dp</dimen>
+    <!-- The margin around the outside of the manage button. -->
+    <dimen name="bubble_manage_button_margin">16dp</dimen>
     <!-- Height of an item in the bubble manage menu. -->
     <dimen name="bubble_menu_item_height">60dp</dimen>
+    <!-- Padding applied to the bubble manage menu. -->
+    <dimen name="bubble_menu_padding">16dp</dimen>
+    <!-- Size of the icons in the manage menu. -->
+    <dimen name="bubble_menu_icon_size">24dp</dimen>
     <!-- Max width of the message bubble-->
     <dimen name="bubble_message_max_width">144dp</dimen>
     <!-- Min width of the message bubble -->
@@ -174,14 +188,8 @@
     <dimen name="bubble_dismiss_target_padding_x">40dp</dimen>
     <dimen name="bubble_dismiss_target_padding_y">20dp</dimen>
     <dimen name="bubble_manage_menu_elevation">4dp</dimen>
-
-    <!-- Bubbles user education views -->
-    <dimen name="bubbles_manage_education_width">160dp</dimen>
-    <!-- The inset from the top bound of the manage button to place the user education. -->
-    <dimen name="bubbles_manage_education_top_inset">65dp</dimen>
-    <!-- Size of padding for the user education cling, this should at minimum be larger than
-        individual_bubble_size + some padding. -->
-    <dimen name="bubble_stack_user_education_side_inset">72dp</dimen>
+    <!-- Size of user education views on large screens (phone is just match parent). -->
+    <dimen name="bubbles_user_education_width_large_screen">400dp</dimen>
 
     <!-- The width/height of the size compat restart button. -->
     <dimen name="size_compat_button_size">48dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
index fffcd33..3e4ca3a 100644
--- a/libs/WindowManager/Shell/res/values/styles.xml
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -34,6 +34,7 @@
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">10dp</item>
         <item name="android:layout_gravity">center_vertical</item>
+        <item name="android:background">@color/split_divider_background</item>
     </style>
 
     <style name="DockedDividerMinimizedShadow">
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
index d1fbf31..df4f238 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
@@ -20,10 +20,13 @@
 
 import com.android.wm.shell.apppairs.AppPairsController;
 import com.android.wm.shell.bubbles.BubbleController;
+import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayInsetsController;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.annotations.ExternalThread;
 import com.android.wm.shell.draganddrop.DragAndDropController;
+import com.android.wm.shell.freeform.FreeformTaskListener;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
 import com.android.wm.shell.pip.phone.PipTouchHandler;
 import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -38,7 +41,9 @@
 public class ShellInitImpl {
     private static final String TAG = ShellInitImpl.class.getSimpleName();
 
+    private final DisplayController mDisplayController;
     private final DisplayImeController mDisplayImeController;
+    private final DisplayInsetsController mDisplayInsetsController;
     private final DragAndDropController mDragAndDropController;
     private final ShellTaskOrganizer mShellTaskOrganizer;
     private final Optional<BubbleController> mBubblesOptional;
@@ -47,13 +52,17 @@
     private final Optional<AppPairsController> mAppPairsOptional;
     private final Optional<PipTouchHandler> mPipTouchHandlerOptional;
     private final FullscreenTaskListener mFullscreenTaskListener;
+    private final Optional<FreeformTaskListener> mFreeformTaskListenerOptional;
     private final ShellExecutor mMainExecutor;
     private final Transitions mTransitions;
     private final StartingWindowController mStartingWindow;
 
     private final InitImpl mImpl = new InitImpl();
 
-    public ShellInitImpl(DisplayImeController displayImeController,
+    public ShellInitImpl(
+            DisplayController displayController,
+            DisplayImeController displayImeController,
+            DisplayInsetsController displayInsetsController,
             DragAndDropController dragAndDropController,
             ShellTaskOrganizer shellTaskOrganizer,
             Optional<BubbleController> bubblesOptional,
@@ -62,10 +71,13 @@
             Optional<AppPairsController> appPairsOptional,
             Optional<PipTouchHandler> pipTouchHandlerOptional,
             FullscreenTaskListener fullscreenTaskListener,
+            Optional<Optional<FreeformTaskListener>> freeformTaskListenerOptional,
             Transitions transitions,
             StartingWindowController startingWindow,
             ShellExecutor mainExecutor) {
+        mDisplayController = displayController;
         mDisplayImeController = displayImeController;
+        mDisplayInsetsController = displayInsetsController;
         mDragAndDropController = dragAndDropController;
         mShellTaskOrganizer = shellTaskOrganizer;
         mBubblesOptional = bubblesOptional;
@@ -74,6 +86,7 @@
         mAppPairsOptional = appPairsOptional;
         mFullscreenTaskListener = fullscreenTaskListener;
         mPipTouchHandlerOptional = pipTouchHandlerOptional;
+        mFreeformTaskListenerOptional = freeformTaskListenerOptional.flatMap(f -> f);
         mTransitions = transitions;
         mMainExecutor = mainExecutor;
         mStartingWindow = startingWindow;
@@ -84,7 +97,9 @@
     }
 
     private void init() {
-        // Start listening for display changes
+        // Start listening for display and insets changes
+        mDisplayController.initialize();
+        mDisplayInsetsController.initialize();
         mDisplayImeController.startMonitorDisplays();
 
         // Setup the shell organizer
@@ -108,6 +123,11 @@
         // controller instead of the feature interface, can just initialize the touch handler if
         // needed
         mPipTouchHandlerOptional.ifPresent((handler) -> handler.init());
+
+        // Initialize optional freeform
+        mFreeformTaskListenerOptional.ifPresent(f ->
+                mShellTaskOrganizer.addListenerForType(
+                        f, ShellTaskOrganizer.TASK_LISTENER_TYPE_FREEFORM));
     }
 
     @ExternalThread
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index ba0ab6d..b5dffba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -31,6 +31,7 @@
 import android.app.TaskInfo;
 import android.content.Context;
 import android.content.LocusId;
+import android.content.pm.ActivityInfo;
 import android.graphics.Rect;
 import android.os.Binder;
 import android.os.IBinder;
@@ -46,6 +47,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.wm.shell.common.ScreenshotUtils;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.sizecompatui.SizeCompatUIController;
@@ -71,12 +73,14 @@
     public static final int TASK_LISTENER_TYPE_FULLSCREEN = -2;
     public static final int TASK_LISTENER_TYPE_MULTI_WINDOW = -3;
     public static final int TASK_LISTENER_TYPE_PIP = -4;
+    public static final int TASK_LISTENER_TYPE_FREEFORM = -5;
 
     @IntDef(prefix = {"TASK_LISTENER_TYPE_"}, value = {
             TASK_LISTENER_TYPE_UNDEFINED,
             TASK_LISTENER_TYPE_FULLSCREEN,
             TASK_LISTENER_TYPE_MULTI_WINDOW,
             TASK_LISTENER_TYPE_PIP,
+            TASK_LISTENER_TYPE_FREEFORM,
     })
     public @interface TaskListenerType {}
 
@@ -486,14 +490,40 @@
     }
 
     @Override
+    public void onSizeCompatRestartButtonAppeared(int taskId) {
+        final TaskAppearedInfo info;
+        synchronized (mLock) {
+            info = mTasks.get(taskId);
+        }
+        if (info == null) {
+            return;
+        }
+        logSizeCompatRestartButtonEventReported(info,
+                FrameworkStatsLog.SIZE_COMPAT_RESTART_BUTTON_EVENT_REPORTED__EVENT__APPEARED);
+    }
+
+    @Override
     public void onSizeCompatRestartButtonClicked(int taskId) {
         final TaskAppearedInfo info;
         synchronized (mLock) {
             info = mTasks.get(taskId);
         }
-        if (info != null) {
-            restartTaskTopActivityProcessIfVisible(info.getTaskInfo().token);
+        if (info == null) {
+            return;
         }
+        logSizeCompatRestartButtonEventReported(info,
+                FrameworkStatsLog.SIZE_COMPAT_RESTART_BUTTON_EVENT_REPORTED__EVENT__CLICKED);
+        restartTaskTopActivityProcessIfVisible(info.getTaskInfo().token);
+    }
+
+    private void logSizeCompatRestartButtonEventReported(@NonNull TaskAppearedInfo info,
+            int event) {
+        ActivityInfo topActivityInfo = info.getTaskInfo().topActivityInfo;
+        if (topActivityInfo == null) {
+            return;
+        }
+        FrameworkStatsLog.write(FrameworkStatsLog.SIZE_COMPAT_RESTART_BUTTON_EVENT_REPORTED,
+                topActivityInfo.applicationInfo.uid, event);
     }
 
     /**
@@ -572,6 +602,7 @@
             case WINDOWING_MODE_PINNED:
                 return TASK_LISTENER_TYPE_PIP;
             case WINDOWING_MODE_FREEFORM:
+                return TASK_LISTENER_TYPE_FREEFORM;
             case WINDOWING_MODE_UNDEFINED:
             default:
                 return TASK_LISTENER_TYPE_UNDEFINED;
@@ -586,6 +617,8 @@
                 return "TASK_LISTENER_TYPE_MULTI_WINDOW";
             case TASK_LISTENER_TYPE_PIP:
                 return "TASK_LISTENER_TYPE_PIP";
+            case TASK_LISTENER_TYPE_FREEFORM:
+                return "TASK_LISTENER_TYPE_FREEFORM";
             case TASK_LISTENER_TYPE_UNDEFINED:
                 return "TASK_LISTENER_TYPE_UNDEFINED";
             default:
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java
index 8aca01d..2aead93 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java
@@ -62,4 +62,10 @@
      */
     public static final Interpolator PANEL_CLOSE_ACCELERATED =
             new PathInterpolator(0.3f, 0, 0.5f, 1);
+
+    public static final PathInterpolator SLOWDOWN_INTERPOLATOR =
+            new PathInterpolator(0.5f, 1f, 0.5f, 1f);
+
+    public static final PathInterpolator DIM_INTERPOLATOR =
+            new PathInterpolator(.23f, .87f, .52f, -0.11f);
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
index 94a8758..3800b8d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
@@ -19,6 +19,7 @@
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER;
 
 import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
@@ -181,7 +182,7 @@
 
         // TODO: Is there more we need to do here?
         mSyncQueue.runInSync(t -> {
-            t.setLayer(dividerLeash, Integer.MAX_VALUE)
+            t.setLayer(dividerLeash, SPLIT_DIVIDER_LAYER)
                     .setPosition(mTaskLeash1, mTaskInfo1.positionInParent.x,
                             mTaskInfo1.positionInParent.y)
                     .setPosition(mTaskLeash2, mTaskInfo2.positionInParent.x,
@@ -215,7 +216,7 @@
 
             if (mSplitLayout != null) {
                 if (mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)) {
-                    onBoundsChanged(mSplitLayout);
+                    onLayoutChanged(mSplitLayout);
                 }
                 // updateConfiguration re-inits the dividerbar, so show it now
                 mSyncQueue.runInSync(t -> t.show(mSplitLayout.getDividerLeash()));
@@ -299,17 +300,24 @@
     }
 
     @Override
-    public void onBoundsChanging(SplitLayout layout) {
+    public void onLayoutChanging(SplitLayout layout) {
         mSyncQueue.runInSync(t ->
                 layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2));
     }
 
     @Override
-    public void onBoundsChanged(SplitLayout layout) {
+    public void onLayoutChanged(SplitLayout layout) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         layout.applyTaskChanges(wct, mTaskInfo1, mTaskInfo2);
         mSyncQueue.queue(wct);
         mSyncQueue.runInSync(t ->
                 layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2));
     }
+
+    @Override
+    public void onLayoutShifted(int offsetX, int offsetY, SplitLayout layout) {
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+        layout.applyLayoutShifted(wct, offsetX, offsetY, mTaskInfo1, mTaskInfo2);
+        mController.getTaskOrganizer().applyTransaction(wct);
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 28e2f7dd..95b80df 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -56,7 +56,6 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
@@ -98,7 +97,6 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.Executor;
-import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 import java.util.function.IntConsumer;
 
@@ -581,7 +579,7 @@
 
     /**
      * BubbleStackView is lazily created by this method the first time a Bubble is added. This
-     * method initializes the stack view and adds it to the StatusBar just above the scrim.
+     * method initializes the stack view and adds it to window manager.
      */
     private void ensureStackViewCreated() {
         if (mStackView == null) {
@@ -629,7 +627,6 @@
         try {
             mAddedToWindowManager = true;
             mBubbleData.getOverflow().initialize(this);
-            mStackView.addView(mBubbleScrim);
             mWindowManager.addView(mStackView, mWmLayoutParams);
             // Position info is dependent on us being attached to a window
             mBubblePositioner.update();
@@ -639,10 +636,16 @@
         }
     }
 
-    /** For the overflow to be focusable & receive key events the flags must be update. **/
-    void updateWindowFlagsForOverflow(boolean showingOverflow) {
+    /**
+     * In some situations bubble's should be able to receive key events for back:
+     * - when the bubble overflow is showing
+     * - when the user education for the stack is showing.
+     *
+     * @param interceptBack whether back should be intercepted or not.
+     */
+    void updateWindowFlagsForBackpress(boolean interceptBack) {
         if (mStackView != null && mAddedToWindowManager) {
-            mWmLayoutParams.flags = showingOverflow
+            mWmLayoutParams.flags = interceptBack
                     ? 0
                     : WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                             | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
@@ -661,7 +664,6 @@
             mAddedToWindowManager = false;
             if (mStackView != null) {
                 mWindowManager.removeView(mStackView);
-                mStackView.removeView(mBubbleScrim);
                 mBubbleData.getOverflow().cleanUpExpandedState();
             } else {
                 Log.w(TAG, "StackView added to WindowManager, but was null when removing!");
@@ -763,13 +765,6 @@
         }
     }
 
-    private void setBubbleScrim(View view, BiConsumer<Executor, Looper> callback) {
-        mBubbleScrim = view;
-        callback.accept(mMainExecutor, mMainExecutor.executeBlockingForResult(() -> {
-            return Looper.myLooper();
-        }, Looper.class));
-    }
-
     private void setSysuiProxy(Bubbles.SysuiProxy proxy) {
         mSysuiProxy = proxy;
     }
@@ -1574,13 +1569,6 @@
         }
 
         @Override
-        public void setBubbleScrim(View view, BiConsumer<Executor, Looper> callback) {
-            mMainExecutor.execute(() -> {
-                BubbleController.this.setBubbleScrim(view, callback);
-            });
-        }
-
-        @Override
         public void setExpandListener(BubbleExpandListener listener) {
             mMainExecutor.execute(() -> {
                 BubbleController.this.setExpandListener(listener);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index d73ce69..b48bda3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -699,10 +699,9 @@
         if (DEBUG_BUBBLE_DATA) {
             Log.d(TAG, "setSelectedBubbleInternal: " + bubble);
         }
-        if (!mShowingOverflow && Objects.equals(bubble, mSelectedBubble)) {
+        if (Objects.equals(bubble, mSelectedBubble)) {
             return;
         }
-        // Otherwise, if we are showing the overflow menu, return to the previously selected bubble.
         boolean isOverflow = bubble != null && BubbleOverflow.KEY.equals(bubble.getKey());
         if (bubble != null
                 && !mBubbles.contains(bubble)
@@ -771,6 +770,10 @@
                 Log.e(TAG, "Attempt to expand stack without selected bubble!");
                 return;
             }
+            if (mSelectedBubble.getKey().equals(mOverflow.getKey()) && !mBubbles.isEmpty()) {
+                // Show previously selected bubble instead of overflow menu when expanding.
+                setSelectedBubbleInternal(mBubbles.get(0));
+            }
             if (mSelectedBubble instanceof Bubble) {
                 ((Bubble) mSelectedBubble).markAsAccessedAt(mTimeSource.currentTimeMillis());
             }
@@ -779,16 +782,6 @@
             // Apply ordering and grouping rules from expanded -> collapsed, then save
             // the result.
             mStateChange.orderChanged |= repackAll();
-            // Save the state which should be returned to when expanded (with no other changes)
-
-            if (mShowingOverflow) {
-                // Show previously selected bubble instead of overflow menu on next expansion.
-                if (!mSelectedBubble.getKey().equals(mOverflow.getKey())) {
-                    setSelectedBubbleInternal(mSelectedBubble);
-                } else {
-                    setSelectedBubbleInternal(mBubbles.get(0));
-                }
-            }
             if (mBubbles.indexOf(mSelectedBubble) > 0) {
                 // Move the selected bubble to the top while collapsed.
                 int index = mBubbles.indexOf(mSelectedBubble);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index a02fa9b..7d7bfb2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -228,7 +228,7 @@
         @Override
         public void onBackPressedOnTaskRoot(int taskId) {
             if (mTaskId == taskId && mStackView.isExpanded()) {
-                mController.collapseStack();
+                mStackView.onBackPressed();
             }
         }
     };
@@ -696,8 +696,10 @@
                 ? mPointerHeight - mPointerOverlap
                 : 0;
         final float paddingRight = (showVertically && !onLeft)
-                ? mPointerHeight - mPointerOverlap : 0;
-        final float paddingTop = showVertically ? 0
+                ? mPointerHeight - mPointerOverlap
+                : 0;
+        final float paddingTop = showVertically
+                ? 0
                 : mPointerHeight - mPointerOverlap;
         setPadding((int) paddingLeft, (int) paddingTop, (int) paddingRight, 0);
 
@@ -735,6 +737,10 @@
         mManageButton.getBoundsOnScreen(rect);
     }
 
+    public int getManageButtonMargin() {
+        return ((LinearLayout.LayoutParams) mManageButton.getLayoutParams()).getMarginStart();
+    }
+
     /**
      * Cleans up anything related to the task and {@code TaskView}. If this view should be reused
      * after this method is called, then
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
index 35a4f33..9374da4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
@@ -56,9 +56,6 @@
  * transform into the 'new' dot, which is used during flyout dismiss animations/gestures.
  */
 public class BubbleFlyoutView extends FrameLayout {
-    /** Max width of the flyout, in terms of percent of the screen width. */
-    private static final float FLYOUT_MAX_WIDTH_PERCENT = .6f;
-
     /** Translation Y of fade animation. */
     private static final float FLYOUT_FADE_Y = 40f;
 
@@ -68,6 +65,8 @@
     // Whether the flyout view should show a pointer to the bubble.
     private static final boolean SHOW_POINTER = false;
 
+    private BubblePositioner mPositioner;
+
     private final int mFlyoutPadding;
     private final int mFlyoutSpaceFromBubble;
     private final int mPointerSize;
@@ -156,10 +155,11 @@
     /** Callback to run when the flyout is hidden. */
     @Nullable private Runnable mOnHide;
 
-    public BubbleFlyoutView(Context context) {
+    public BubbleFlyoutView(Context context, BubblePositioner positioner) {
         super(context);
-        LayoutInflater.from(context).inflate(R.layout.bubble_flyout, this, true);
+        mPositioner = positioner;
 
+        LayoutInflater.from(context).inflate(R.layout.bubble_flyout, this, true);
         mFlyoutTextContainer = findViewById(R.id.bubble_flyout_text_container);
         mSenderText = findViewById(R.id.bubble_flyout_name);
         mSenderAvatar = findViewById(R.id.bubble_flyout_avatar);
@@ -230,11 +230,11 @@
     /*
      * Fade animation for consecutive flyouts.
      */
-    void animateUpdate(Bubble.FlyoutMessage flyoutMessage, float parentWidth, PointF stackPos,
+    void animateUpdate(Bubble.FlyoutMessage flyoutMessage, PointF stackPos,
             boolean hideDot, Runnable onHide) {
         mOnHide = onHide;
         final Runnable afterFadeOut = () -> {
-            updateFlyoutMessage(flyoutMessage, parentWidth);
+            updateFlyoutMessage(flyoutMessage);
             // Wait for TextViews to layout with updated height.
             post(() -> {
                 fade(true /* in */, stackPos, hideDot, () -> {} /* after */);
@@ -266,7 +266,7 @@
                 .withEndAction(afterFade);
     }
 
-    private void updateFlyoutMessage(Bubble.FlyoutMessage flyoutMessage, float parentWidth) {
+    private void updateFlyoutMessage(Bubble.FlyoutMessage flyoutMessage) {
         final Drawable senderAvatar = flyoutMessage.senderAvatar;
         if (senderAvatar != null && flyoutMessage.isGroupChat) {
             mSenderAvatar.setVisibility(VISIBLE);
@@ -278,8 +278,7 @@
             mSenderText.setTranslationX(0);
         }
 
-        final int maxTextViewWidth =
-                (int) (parentWidth * FLYOUT_MAX_WIDTH_PERCENT) - mFlyoutPadding * 2;
+        final int maxTextViewWidth = (int) mPositioner.getMaxFlyoutSize() - mFlyoutPadding * 2;
 
         // Name visibility
         if (!TextUtils.isEmpty(flyoutMessage.senderName)) {
@@ -328,22 +327,20 @@
     void setupFlyoutStartingAsDot(
             Bubble.FlyoutMessage flyoutMessage,
             PointF stackPos,
-            float parentWidth,
             boolean arrowPointingLeft,
             int dotColor,
             @Nullable Runnable onLayoutComplete,
             @Nullable Runnable onHide,
             float[] dotCenter,
-            boolean hideDot,
-            BubblePositioner positioner)  {
+            boolean hideDot)  {
 
-        mBubbleSize = positioner.getBubbleSize();
+        mBubbleSize = mPositioner.getBubbleSize();
 
         mOriginalDotSize = SIZE_PERCENTAGE * mBubbleSize;
         mNewDotRadius = (DOT_SCALE * mOriginalDotSize) / 2f;
         mNewDotSize = mNewDotRadius * 2f;
 
-        updateFlyoutMessage(flyoutMessage, parentWidth);
+        updateFlyoutMessage(flyoutMessage);
 
         mArrowPointingLeft = arrowPointingLeft;
         mDotColor = dotColor;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
index ede4228..5e9d97f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
@@ -142,7 +142,7 @@
         super.onAttachedToWindow();
         if (mController != null) {
             // For the overflow to get key events (e.g. back press) we need to adjust the flags
-            mController.updateWindowFlagsForOverflow(true);
+            mController.updateWindowFlagsForBackpress(true);
         }
         setOnKeyListener(mKeyListener);
     }
@@ -151,7 +151,7 @@
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         if (mController != null) {
-            mController.updateWindowFlagsForOverflow(false);
+            mController.updateWindowFlagsForBackpress(false);
         }
         setOnKeyListener(null);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 0a856a8..306224b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -61,10 +61,17 @@
     public static final int NUM_VISIBLE_WHEN_RESTING = 2;
     /** Indicates a bubble's height should be the maximum available space. **/
     public static final int MAX_HEIGHT = -1;
+    /** The max percent of screen width to use for the flyout on large screens. */
+    public static final float FLYOUT_MAX_WIDTH_PERCENT_LARGE_SCREEN = 0.3f;
+    /** The max percent of screen width to use for the flyout on phone. */
+    public static final float FLYOUT_MAX_WIDTH_PERCENT = 0.6f;
+    /** The percent of screen width that should be used for the expanded view on a large screen. **/
+    public static final float EXPANDED_VIEW_LARGE_SCREEN_WIDTH_PERCENT = 0.72f;
 
     private Context mContext;
     private WindowManager mWindowManager;
     private Rect mPositionRect;
+    private Rect mScreenRect;
     private @Surface.Rotation int mRotation = Surface.ROTATION_0;
     private Insets mInsets;
     private int mDefaultMaxBubbles;
@@ -72,15 +79,20 @@
 
     private int mBubbleSize;
     private int mSpacingBetweenBubbles;
+
+    private int mExpandedViewMinHeight;
     private int mExpandedViewLargeScreenWidth;
+    private int mExpandedViewLargeScreenInset;
+
+    private int mOverflowWidth;
     private int mExpandedViewPadding;
     private int mPointerMargin;
     private int mPointerWidth;
     private int mPointerHeight;
     private int mPointerOverlap;
     private int mManageButtonHeight;
-    private int mExpandedViewMinHeight;
     private int mOverflowHeight;
+    private int mMinimumFlyoutWidthLargeScreen;
 
     private PointF mPinLocation;
     private PointF mRestingStackPosition;
@@ -149,6 +161,7 @@
         mRotation = rotation;
         mInsets = insets;
 
+        mScreenRect = new Rect(bounds);
         mPositionRect = new Rect(bounds);
         mPositionRect.left += mInsets.left;
         mPositionRect.top += mInsets.top;
@@ -159,17 +172,25 @@
         mBubbleSize = res.getDimensionPixelSize(R.dimen.bubble_size);
         mSpacingBetweenBubbles = res.getDimensionPixelSize(R.dimen.bubble_spacing);
         mDefaultMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered);
-
-        mExpandedViewLargeScreenWidth = res.getDimensionPixelSize(
-                R.dimen.bubble_expanded_view_tablet_width);
         mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
+        mExpandedViewLargeScreenWidth = (int) (bounds.width()
+                * EXPANDED_VIEW_LARGE_SCREEN_WIDTH_PERCENT);
+        mExpandedViewLargeScreenInset = mIsLargeScreen
+                ? (bounds.width() - mExpandedViewLargeScreenWidth) / 2
+                : mExpandedViewPadding;
+        mOverflowWidth = mIsLargeScreen
+                ? mExpandedViewLargeScreenWidth
+                : res.getDimensionPixelSize(
+                        R.dimen.bubble_expanded_view_phone_landscape_overflow_width);
         mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
         mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
         mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin);
         mPointerOverlap = res.getDimensionPixelSize(R.dimen.bubble_pointer_overlap);
-        mManageButtonHeight = res.getDimensionPixelSize(R.dimen.bubble_manage_button_height);
+        mManageButtonHeight = res.getDimensionPixelSize(R.dimen.bubble_manage_button_total_height);
         mExpandedViewMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
         mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height);
+        mMinimumFlyoutWidthLargeScreen = res.getDimensionPixelSize(
+                R.dimen.bubbles_flyout_min_width_large_screen);
 
         mMaxBubbles = calculateMaxBubbles();
 
@@ -234,6 +255,13 @@
     }
 
     /**
+     * @return a rect of the screen size.
+     */
+    public Rect getScreenRect() {
+        return mScreenRect;
+    }
+
+    /**
      * @return the relevant insets (status bar, nav bar, cutouts). If taskbar is showing, its
      * inset is not included here.
      */
@@ -275,63 +303,93 @@
     }
 
     /**
-     * Calculates the left & right padding for the bubble expanded view.
+     * Calculates the padding for the bubble expanded view.
      *
-     * On larger screens the width of the expanded view is restricted via this padding.
-     * On landscape the bubble overflow expanded view is also restricted via this padding.
+     * Some specifics:
+     * On large screens the width of the expanded view is restricted via this padding.
+     * On phone landscape the bubble overflow expanded view is also restricted via this padding.
+     * On large screens & landscape no top padding is set, the top position is set via translation.
+     * On phone portrait top padding is set as the space between the tip of the pointer and the
+     * bubble.
+     * When the overflow is shown it doesn't have the manage button to pad out the bottom so
+     * padding is added.
      */
-    public int[] getExpandedViewPadding(boolean onLeft, boolean isOverflow) {
-        int leftPadding = mInsets.left + mExpandedViewPadding;
-        int rightPadding = mInsets.right + mExpandedViewPadding;
-        final boolean isLargeOrOverflow = mIsLargeScreen || isOverflow;
-        if (showBubblesVertically()) {
-            if (!onLeft) {
-                rightPadding += mBubbleSize - mPointerHeight;
-                leftPadding += isLargeOrOverflow
-                        ? (mPositionRect.width() - rightPadding - mExpandedViewLargeScreenWidth)
-                        : 0;
-            } else {
-                leftPadding += mBubbleSize - mPointerHeight;
-                rightPadding += isLargeOrOverflow
-                        ? (mPositionRect.width() - leftPadding - mExpandedViewLargeScreenWidth)
-                        : 0;
+    public int[] getExpandedViewContainerPadding(boolean onLeft, boolean isOverflow) {
+        final int pointerTotalHeight = mPointerHeight - mPointerOverlap;
+        if (mIsLargeScreen) {
+            // [left, top, right, bottom]
+            mPaddings[0] = onLeft
+                    ? mExpandedViewLargeScreenInset - pointerTotalHeight
+                    : mExpandedViewLargeScreenInset;
+            mPaddings[1] = 0;
+            mPaddings[2] = onLeft
+                    ? mExpandedViewLargeScreenInset
+                    : mExpandedViewLargeScreenInset - pointerTotalHeight;
+            // Overflow doesn't show manage button / get padding from it so add padding here for it
+            mPaddings[3] = isOverflow ? mExpandedViewPadding : 0;
+            return mPaddings;
+        } else {
+            int leftPadding = mInsets.left + mExpandedViewPadding;
+            int rightPadding = mInsets.right + mExpandedViewPadding;
+            final float expandedViewWidth = isOverflow
+                    ? mOverflowWidth
+                    : mExpandedViewLargeScreenWidth;
+            if (showBubblesVertically()) {
+                if (!onLeft) {
+                    rightPadding += mBubbleSize - pointerTotalHeight;
+                    leftPadding += isOverflow
+                            ? (mPositionRect.width() - rightPadding - expandedViewWidth)
+                            : 0;
+                } else {
+                    leftPadding += mBubbleSize - pointerTotalHeight;
+                    rightPadding += isOverflow
+                            ? (mPositionRect.width() - leftPadding - expandedViewWidth)
+                            : 0;
+                }
             }
+            // [left, top, right, bottom]
+            mPaddings[0] = leftPadding;
+            mPaddings[1] = showBubblesVertically() ? 0 : mPointerMargin;
+            mPaddings[2] = rightPadding;
+            mPaddings[3] = 0;
+            return mPaddings;
         }
-        // [left, top, right, bottom]
-        mPaddings[0] = leftPadding;
-        mPaddings[1] = showBubblesVertically() ? 0 : mPointerMargin;
-        mPaddings[2] = rightPadding;
-        mPaddings[3] = 0;
-        return mPaddings;
     }
 
     /** Gets the y position of the expanded view if it was top-aligned. */
     private float getExpandedViewYTopAligned() {
         final int top = getAvailableRect().top;
         if (showBubblesVertically()) {
-            return top - mPointerWidth;
+            return top - mPointerWidth + mExpandedViewPadding;
         } else {
             return top + mBubbleSize + mPointerMargin;
         }
     }
 
-    /** The maximum height the expanded view can be. */
+    public float getExpandedBubblesY() {
+        return getAvailableRect().top + mExpandedViewPadding;
+    }
+
+    /**
+     * Calculate the maximum height the expanded view can be depending on where it's placed on
+     * the screen and the size of the elements around it (e.g. padding, pointer, manage button).
+     */
     public int getMaxExpandedViewHeight(boolean isOverflow) {
+        // Subtract top insets because availableRect.height would account for that
+        int expandedContainerY = (int) getExpandedViewYTopAligned() - getInsets().top;
         int paddingTop = showBubblesVertically()
                 ? 0
                 : mPointerHeight;
-        int settingsHeight = isOverflow ? 0 : mManageButtonHeight;
         // Subtract pointer size because it's laid out in LinearLayout with the expanded view.
         int pointerSize = showBubblesVertically()
                 ? mPointerWidth
                 : (mPointerHeight + mPointerMargin);
-        // Subtract top insets because availableRect.height would account for that
-        int expandedContainerY = (int) getExpandedViewYTopAligned() - getInsets().top;
+        int bottomPadding = isOverflow ? mExpandedViewPadding : mManageButtonHeight;
         return getAvailableRect().height()
                 - expandedContainerY
                 - paddingTop
-                - settingsHeight
-                - pointerSize;
+                - pointerSize
+                - bottomPadding;
     }
 
     /**
@@ -340,11 +398,14 @@
      */
     public float getExpandedViewHeight(BubbleViewProvider bubble) {
         boolean isOverflow = bubble == null || BubbleOverflow.KEY.equals(bubble.getKey());
+        if (isOverflow && showBubblesVertically() && !mIsLargeScreen) {
+            // overflow in landscape on phone is max
+            return MAX_HEIGHT;
+        }
         float desiredHeight = isOverflow
                 ? mOverflowHeight
                 : ((Bubble) bubble).getDesiredHeight(mContext);
-        int manageButtonHeight = isOverflow ? 0 : mManageButtonHeight;
-        desiredHeight = Math.max(manageButtonHeight + desiredHeight, mExpandedViewMinHeight);
+        desiredHeight = Math.max(desiredHeight, mExpandedViewMinHeight);
         if (desiredHeight > getMaxExpandedViewHeight(isOverflow)) {
             return MAX_HEIGHT;
         }
@@ -369,7 +430,7 @@
             return topAlignment;
         }
         // If we're here, we're showing vertically & developer has made height less than maximum.
-        int manageButtonHeight = isOverflow ? 0 : mManageButtonHeight;
+        int manageButtonHeight = isOverflow ? mExpandedViewPadding : mManageButtonHeight;
         float pointerPosition = getPointerPosition(bubblePosition);
         float bottomIfCentered = pointerPosition + (expandedViewHeight / 2) + manageButtonHeight;
         float topIfCentered = pointerPosition - (expandedViewHeight / 2);
@@ -404,25 +465,62 @@
     }
 
     /**
-     * When bubbles are expanded in portrait, they display at the top of the screen in a horizontal
-     * row. When in landscape or on a large screen, they show at the left or right side in a
-     * vertical row. This method accounts for screen orientation and will return an x or y value
-     * for the position of the bubble in the row.
+     * Returns the position of the bubble on-screen when the stack is expanded.
      *
-     * @param index bubble index in the row.
-     * @param numberOfBubbles the number of bubbles (including the overflow) in the row.
-     * @return the y position of the bubble if showing vertically and the x position if showing
-     * horizontally.
+     * @param index the index of the bubble in the stack.
+     * @param numberOfBubbles the total number of bubbles in the stack.
+     * @param onLeftEdge whether the stack would rest on the left edge of the screen when collapsed.
+     * @return the x, y position of the bubble on-screen when the stack is expanded.
      */
-    public float getBubbleXOrYForOrientation(int index, int numberOfBubbles) {
-        final float positionInBar = index * (mBubbleSize + mSpacingBetweenBubbles);
+    public PointF getExpandedBubbleXY(int index, int numberOfBubbles, boolean onLeftEdge) {
+        final float positionInRow = index * (mBubbleSize + mSpacingBetweenBubbles);
         final float expandedStackSize = (numberOfBubbles * mBubbleSize)
                 + ((numberOfBubbles - 1) * mSpacingBetweenBubbles);
         final float centerPosition = showBubblesVertically()
                 ? mPositionRect.centerY()
                 : mPositionRect.centerX();
+        // alignment - centered on the edge
         final float rowStart = centerPosition - (expandedStackSize / 2f);
-        return rowStart + positionInBar;
+        float x;
+        float y;
+        if (showBubblesVertically()) {
+            y = rowStart + positionInRow;
+            int left = mIsLargeScreen
+                    ? mExpandedViewLargeScreenInset - mExpandedViewPadding - mBubbleSize
+                    : mPositionRect.left;
+            int right = mIsLargeScreen
+                    ? mPositionRect.right - mExpandedViewLargeScreenInset + mExpandedViewPadding
+                    : mPositionRect.right - mBubbleSize;
+            x = onLeftEdge
+                    ? left
+                    : right;
+        } else {
+            y = mPositionRect.top + mExpandedViewPadding;
+            x = rowStart + positionInRow;
+        }
+        return new PointF(x, y);
+    }
+
+    /**
+     * @return the width of the bubble flyout (message originating from the bubble).
+     */
+    public float getMaxFlyoutSize() {
+        if (isLargeScreen()) {
+            return Math.max(mScreenRect.width() * FLYOUT_MAX_WIDTH_PERCENT_LARGE_SCREEN,
+                    mMinimumFlyoutWidthLargeScreen);
+        }
+        return mScreenRect.width() * FLYOUT_MAX_WIDTH_PERCENT;
+    }
+
+    /**
+     * @return whether the stack is considered on the left side of the screen.
+     */
+    public boolean isStackOnLeft(PointF currentStackPosition) {
+        if (currentStackPosition == null) {
+            currentStackPosition = getRestingPosition();
+        }
+        final int stackCenter = (int) currentStackPosition.x + mBubbleSize / 2;
+        return stackCenter < mScreenRect.width() / 2;
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 36b8923..5a51eed 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -19,6 +19,8 @@
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
+import static com.android.wm.shell.animation.Interpolators.ALPHA_IN;
+import static com.android.wm.shell.animation.Interpolators.ALPHA_OUT;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_STACK_VIEW;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
@@ -33,11 +35,11 @@
 import android.content.Intent;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.graphics.Color;
 import android.graphics.Outline;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.graphics.drawable.ColorDrawable;
 import android.os.Bundle;
 import android.provider.Settings;
 import android.util.Log;
@@ -106,14 +108,8 @@
      */
     private static final float FLYOUT_OVERSCROLL_ATTENUATION_FACTOR = 8f;
 
-    /** Duration of the flyout alpha animations. */
-    private static final int FLYOUT_ALPHA_ANIMATION_DURATION = 100;
-
     private static final int FADE_IN_DURATION = 320;
 
-    /** Percent to darken the bubbles when they're in the dismiss target. */
-    private static final float DARKEN_PERCENT = 0.3f;
-
     /** How long to wait, in milliseconds, before hiding the flyout. */
     @VisibleForTesting
     static final int FLYOUT_HIDE_AFTER = 5000;
@@ -122,6 +118,10 @@
 
     private static final int EXPANDED_VIEW_ALPHA_ANIMATION_DURATION = 150;
 
+    private static final int MANAGE_MENU_SCRIM_ANIM_DURATION = 150;
+
+    private static final float SCRIM_ALPHA = 0.6f;
+
     /**
      * How long to wait to animate the stack temporarily invisible after a drag/flyout hide
      * animation ends, if we are in fact temporarily invisible.
@@ -195,7 +195,8 @@
     private StackAnimationController mStackAnimationController;
     private ExpandedAnimationController mExpandedAnimationController;
 
-    private View mTaskbarScrim;
+    private View mScrim;
+    private View mManageMenuScrim;
     private FrameLayout mExpandedViewContainer;
 
     /** Matrix used to scale the expanded view container with a given pivot point. */
@@ -555,7 +556,7 @@
 
             if (mBubbleData.isExpanded()) {
                 if (mManageEduView != null) {
-                    mManageEduView.hide(false /* show */);
+                    mManageEduView.hide();
                 }
 
                 // If we're expanded, tell the animation controller to prepare to drag this bubble,
@@ -777,8 +778,8 @@
                 floatingContentCoordinator, this::getBubbleCount, onBubbleAnimatedOut,
                 this::animateShadows /* onStackAnimationFinished */, mPositioner);
 
-        mExpandedAnimationController = new ExpandedAnimationController(
-                mPositioner, mExpandedViewPadding, onBubbleAnimatedOut);
+        mExpandedAnimationController = new ExpandedAnimationController(mPositioner,
+                onBubbleAnimatedOut);
         mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER;
 
         // Force LTR by default since most of the Bubbles UI is positioned manually by the user, or
@@ -793,8 +794,6 @@
         mBubbleContainer.setClipChildren(false);
         addView(mBubbleContainer, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
 
-        updateUserEdu();
-
         mExpandedViewContainer = new FrameLayout(context);
         mExpandedViewContainer.setElevation(elevation);
         mExpandedViewContainer.setClipChildren(false);
@@ -858,11 +857,20 @@
             mBubbleData.setExpanded(true);
         });
 
-        mTaskbarScrim = new View(getContext());
-        mTaskbarScrim.setBackgroundColor(Color.BLACK);
-        addView(mTaskbarScrim);
-        mTaskbarScrim.setAlpha(0f);
-        mTaskbarScrim.setVisibility(GONE);
+        mScrim = new View(getContext());
+        mScrim.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+        mScrim.setBackgroundDrawable(new ColorDrawable(
+                getResources().getColor(android.R.color.system_neutral1_1000)));
+        addView(mScrim);
+        mScrim.setAlpha(0f);
+
+        mManageMenuScrim = new View(getContext());
+        mManageMenuScrim.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+        mManageMenuScrim.setBackgroundDrawable(new ColorDrawable(
+                getResources().getColor(android.R.color.system_neutral1_1000)));
+        addView(mManageMenuScrim, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
+        mManageMenuScrim.setAlpha(0f);
+        mManageMenuScrim.setVisibility(INVISIBLE);
 
         mOrientationChangedListener =
                 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
@@ -919,8 +927,10 @@
         setOnClickListener(view -> {
             if (mShowingManage) {
                 showManageMenu(false /* show */);
+            } else if (mManageEduView != null && mManageEduView.getVisibility() == VISIBLE) {
+                mManageEduView.hide();
             } else if (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE) {
-                mStackEduView.hide(false);
+                mStackEduView.hide(false /* isExpanding */);
             } else if (mBubbleData.isExpanded()) {
                 mBubbleData.setExpanded(false);
             }
@@ -1119,10 +1129,10 @@
             return;
         }
         if (mManageEduView == null) {
-            mManageEduView = new ManageEducationView(mContext);
+            mManageEduView = new ManageEducationView(mContext, mPositioner);
             addView(mManageEduView);
         }
-        mManageEduView.show(mExpandedBubble.getExpandedView(), mTempRect);
+        mManageEduView.show(mExpandedBubble.getExpandedView());
     }
 
     /**
@@ -1150,21 +1160,27 @@
             return false;
         }
         if (mStackEduView == null) {
-            mStackEduView = new StackEducationView(mContext);
+            mStackEduView = new StackEducationView(mContext, mPositioner, mBubbleController);
             addView(mStackEduView);
         }
         mBubbleContainer.bringToFront();
         return mStackEduView.show(mPositioner.getDefaultStartPosition());
     }
 
+    // Recreates & shows the education views. Call when a theme/config change happens.
     private void updateUserEdu() {
-        maybeShowStackEdu();
-        if (mManageEduView != null) {
-            mManageEduView.invalidate();
+        if (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE) {
+            removeView(mStackEduView);
+            mStackEduView = new StackEducationView(mContext, mPositioner, mBubbleController);
+            addView(mStackEduView);
+            mBubbleContainer.bringToFront(); // Stack appears on top of the stack education
+            mStackEduView.show(mPositioner.getDefaultStartPosition());
         }
-        maybeShowManageEdu();
-        if (mStackEduView != null) {
-            mStackEduView.invalidate();
+        if (mManageEduView != null && mManageEduView.getVisibility() == VISIBLE) {
+            removeView(mManageEduView);
+            mManageEduView = new ManageEducationView(mContext, mPositioner);
+            addView(mManageEduView);
+            mManageEduView.show(mExpandedBubble.getExpandedView());
         }
     }
 
@@ -1173,7 +1189,7 @@
         if (mFlyout != null) {
             removeView(mFlyout);
         }
-        mFlyout = new BubbleFlyoutView(getContext());
+        mFlyout = new BubbleFlyoutView(getContext(), mPositioner);
         mFlyout.setVisibility(GONE);
         mFlyout.setOnClickListener(mFlyoutClickListener);
         mFlyout.setOnTouchListener(mFlyoutTouchListener);
@@ -1220,6 +1236,10 @@
         updateOverflow();
         updateUserEdu();
         updateExpandedViewTheme();
+        mScrim.setBackgroundDrawable(new ColorDrawable(
+                getResources().getColor(android.R.color.system_neutral1_1000)));
+        mManageMenuScrim.setBackgroundDrawable(new ColorDrawable(
+                getResources().getColor(android.R.color.system_neutral1_1000)));
     }
 
     /**
@@ -1257,6 +1277,7 @@
         setUpManageMenu();
         setUpFlyout();
         setUpDismissView();
+        updateUserEdu();
         mBubbleSize = mPositioner.getBubbleSize();
         for (Bubble b : mBubbleData.getBubbles()) {
             if (b.getIconView() == null) {
@@ -1713,6 +1734,21 @@
         notifyExpansionChanged(mExpandedBubble, mIsExpanded);
     }
 
+    /**
+     * Called when back press occurs while bubbles are expanded.
+     */
+    public void onBackPressed() {
+        if (mIsExpanded) {
+            if (mShowingManage) {
+                showManageMenu(false);
+            } else if (mManageEduView != null && mManageEduView.getVisibility() == VISIBLE) {
+                mManageEduView.hide();
+            } else {
+                setExpanded(false);
+            }
+        }
+    }
+
     void setBubbleVisibility(Bubble b, boolean visible) {
         if (b.getIconView() != null) {
             b.getIconView().setVisibility(visible ? VISIBLE : GONE);
@@ -1799,6 +1835,20 @@
         mExpandedViewAlphaAnimator.start();
     }
 
+    private void showScrim(boolean show) {
+        if (show) {
+            mScrim.animate()
+                    .setInterpolator(ALPHA_IN)
+                    .alpha(SCRIM_ALPHA)
+                    .start();
+        } else {
+            mScrim.animate()
+                    .alpha(0f)
+                    .setInterpolator(ALPHA_OUT)
+                    .start();
+        }
+    }
+
     private void animateExpansion() {
         cancelDelayedExpandCollapseSwitchAnimations();
         final boolean showVertically = mPositioner.showBubblesVertically();
@@ -1808,6 +1858,7 @@
         }
         beforeExpandedViewAnimation();
 
+        showScrim(true);
         updateZOrder();
         updateBadges(false /* setBadgeForCollapsedStack */);
         mBubbleContainer.setActiveController(mExpandedAnimationController);
@@ -1818,38 +1869,28 @@
                 maybeShowManageEdu();
             }
         } /* after */);
-
-        if (mPositioner.showingInTaskbar()
-                // Don't need the scrim when the bar is at the bottom
-                && mPositioner.getTaskbarPosition() != BubblePositioner.TASKBAR_POSITION_BOTTOM) {
-            mTaskbarScrim.getLayoutParams().width = mPositioner.getTaskbarSize();
-            mTaskbarScrim.setTranslationX(mStackOnLeftOrWillBe
-                    ? 0f
-                    : mPositioner.getAvailableRect().right - mPositioner.getTaskbarSize());
-            mTaskbarScrim.setVisibility(VISIBLE);
-            mTaskbarScrim.animate().alpha(1f).start();
-        }
-        final float translationY = mPositioner.getExpandedViewY(mExpandedBubble,
-                getBubbleIndex(mExpandedBubble));
-        mExpandedViewContainer.setTranslationX(0f);
-        mExpandedViewContainer.setTranslationY(translationY);
-        mExpandedViewContainer.setAlpha(1f);
-
         int index;
         if (mExpandedBubble != null && BubbleOverflow.KEY.equals(mExpandedBubble.getKey())) {
             index = mBubbleData.getBubbles().size();
         } else {
             index = getBubbleIndex(mExpandedBubble);
         }
-        // Position of the bubble we're expanding, once it's settled in its row.
-        final float bubbleWillBeAt =
-                mExpandedAnimationController.getBubbleXOrYForOrientation(index);
+        PointF p = mPositioner.getExpandedBubbleXY(index, mBubbleContainer.getChildCount(),
+                mStackOnLeftOrWillBe);
+        final float translationY = mPositioner.getExpandedViewY(mExpandedBubble,
+                mPositioner.showBubblesVertically() ? p.y : p.x);
+        mExpandedViewContainer.setTranslationX(0f);
+        mExpandedViewContainer.setTranslationY(translationY);
+        mExpandedViewContainer.setAlpha(1f);
 
         // How far horizontally the bubble will be animating. We'll wait a bit longer for bubbles
         // that are animating farther, so that the expanded view doesn't move as much.
         final float relevantStackPosition = showVertically
                 ? mStackAnimationController.getStackPosition().y
                 : mStackAnimationController.getStackPosition().x;
+        final float bubbleWillBeAt = showVertically
+                ? p.y
+                : p.x;
         final float distanceAnimated = Math.abs(bubbleWillBeAt - relevantStackPosition);
 
         // Wait for the path animation target to reach its end, and add a small amount of extra time
@@ -1866,22 +1907,22 @@
         // Set the pivot point for the scale, so the expanded view animates out from the bubble.
         if (showVertically) {
             float pivotX;
-            float pivotY = bubbleWillBeAt + mBubbleSize / 2f;
             if (mStackOnLeftOrWillBe) {
-                pivotX = mPositioner.getAvailableRect().left + mBubbleSize + mExpandedViewPadding;
+                pivotX = p.x + mBubbleSize + mExpandedViewPadding;
             } else {
-                pivotX = mPositioner.getAvailableRect().right - mBubbleSize - mExpandedViewPadding;
+                pivotX = p.x - mExpandedViewPadding;
             }
             mExpandedViewContainerMatrix.setScale(
                     1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
                     1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
-                    pivotX, pivotY);
+                    pivotX,
+                    p.y + mBubbleSize / 2f);
         } else {
             mExpandedViewContainerMatrix.setScale(
                     1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
                     1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
-                    bubbleWillBeAt + mBubbleSize / 2f,
-                    translationY);
+                    p.x + mBubbleSize / 2f,
+                    p.y + mBubbleSize + mExpandedViewPadding);
         }
         mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
 
@@ -1918,6 +1959,7 @@
                                 mExpandedViewContainerMatrix);
                     })
                     .withEndActions(() -> {
+                        mExpandedViewContainer.setAnimationMatrix(null);
                         afterExpandedViewAnimation();
                         if (mExpandedBubble != null
                                 && mExpandedBubble.getExpandedView() != null) {
@@ -1933,12 +1975,17 @@
     private void animateCollapse() {
         cancelDelayedExpandCollapseSwitchAnimations();
 
+        if (mManageEduView != null && mManageEduView.getVisibility() == VISIBLE) {
+            mManageEduView.hide();
+        }
         // Hide the menu if it's visible.
         showManageMenu(false);
 
         mIsExpanded = false;
         mIsExpansionAnimating = true;
 
+        showScrim(false);
+
         mBubbleContainer.cancelAllAnimations();
 
         // If we were in the middle of swapping, the animating-out surface would have been scaling
@@ -1956,10 +2003,6 @@
                 /* collapseTo */,
                 () -> mBubbleContainer.setActiveController(mStackAnimationController));
 
-        if (mTaskbarScrim.getVisibility() == VISIBLE) {
-            mTaskbarScrim.animate().alpha(0f).start();
-        }
-
         int index;
         if (mExpandedBubble != null && BubbleOverflow.KEY.equals(mExpandedBubble.getKey())) {
             index = mBubbleData.getBubbles().size();
@@ -1967,12 +2010,11 @@
             index = mBubbleData.getBubbles().indexOf(mExpandedBubble);
         }
         // Value the bubble is animating from (back into the stack).
-        final float expandingFromBubbleAt =
-                mExpandedAnimationController.getBubbleXOrYForOrientation(index);
-        final boolean showVertically = mPositioner.showBubblesVertically();
+        final PointF p = mPositioner.getExpandedBubbleXY(index,
+                mBubbleContainer.getChildCount(), mStackOnLeftOrWillBe);
         if (mPositioner.showBubblesVertically()) {
             float pivotX;
-            float pivotY = expandingFromBubbleAt + mBubbleSize / 2f;
+            float pivotY = p.y + mBubbleSize / 2f;
             if (mStackOnLeftOrWillBe) {
                 pivotX = mPositioner.getAvailableRect().left + mBubbleSize + mExpandedViewPadding;
             } else {
@@ -1984,8 +2026,8 @@
         } else {
             mExpandedViewContainerMatrix.setScale(
                     1f, 1f,
-                    expandingFromBubbleAt + mBubbleSize / 2f,
-                    mPositioner.getExpandedViewY(mExpandedBubble, index));
+                    p.x + mBubbleSize / 2f,
+                    p.y + mBubbleSize + mExpandedViewPadding);
         }
 
         mExpandedViewAlphaAnimator.reverse();
@@ -2012,7 +2054,7 @@
                     final BubbleViewProvider previouslySelected = mExpandedBubble;
                     beforeExpandedViewAnimation();
                     if (mManageEduView != null) {
-                        mManageEduView.hide(false /* fromExpansion */);
+                        mManageEduView.hide();
                     }
 
                     if (DEBUG_BUBBLE_STACK_VIEW) {
@@ -2027,10 +2069,6 @@
                     if (previouslySelected != null) {
                         previouslySelected.setTaskViewVisibility(false);
                     }
-
-                    if (mPositioner.showingInTaskbar()) {
-                        mTaskbarScrim.setVisibility(GONE);
-                    }
                 })
                 .start();
     }
@@ -2067,32 +2105,31 @@
 
         boolean isOverflow = mExpandedBubble != null
                 && mExpandedBubble.getKey().equals(BubbleOverflow.KEY);
-        float expandingFromBubbleDestination =
-                mExpandedAnimationController.getBubbleXOrYForOrientation(isOverflow
-                        ? getBubbleCount()
-                        : mBubbleData.getBubbles().indexOf(mExpandedBubble));
-
+        PointF p = mPositioner.getExpandedBubbleXY(isOverflow
+                        ? mBubbleContainer.getChildCount() - 1
+                        : mBubbleData.getBubbles().indexOf(mExpandedBubble),
+                    mBubbleContainer.getChildCount(), mStackOnLeftOrWillBe);
         mExpandedViewContainer.setAlpha(1f);
         mExpandedViewContainer.setVisibility(View.VISIBLE);
 
         if (mPositioner.showBubblesVertically()) {
             float pivotX;
-            float pivotY = expandingFromBubbleDestination + mBubbleSize / 2f;
+            float pivotY = p.y + mBubbleSize / 2f;
             if (mStackOnLeftOrWillBe) {
-                pivotX = mPositioner.getAvailableRect().left + mBubbleSize + mExpandedViewPadding;
+                pivotX = p.x + mBubbleSize + mExpandedViewPadding;
             } else {
-                pivotX = mPositioner.getAvailableRect().right - mBubbleSize - mExpandedViewPadding;
-
+                pivotX = p.x - mExpandedViewPadding;
             }
             mExpandedViewContainerMatrix.setScale(
-                    0f, 0f,
+                    1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
+                    1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
                     pivotX, pivotY);
         } else {
             mExpandedViewContainerMatrix.setScale(
                     1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
                     1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
-                    expandingFromBubbleDestination + mBubbleSize / 2f,
-                    mPositioner.getExpandedViewY(mExpandedBubble, expandingFromBubbleDestination));
+                    p.x + mBubbleSize / 2f,
+                    p.y + mBubbleSize + mExpandedViewPadding);
         }
 
         mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
@@ -2117,6 +2154,7 @@
                     .withEndActions(() -> {
                         mExpandedViewTemporarilyHidden = false;
                         mIsBubbleSwitchAnimating = false;
+                        mExpandedViewContainer.setAnimationMatrix(null);
                     })
                     .start();
         }, 25);
@@ -2407,20 +2445,19 @@
 
 
             if (mFlyout.getVisibility() == View.VISIBLE) {
-                mFlyout.animateUpdate(bubble.getFlyoutMessage(), getWidth(),
+                mFlyout.animateUpdate(bubble.getFlyoutMessage(),
                         mStackAnimationController.getStackPosition(), !bubble.showDot(),
                         mAfterFlyoutHidden /* onHide */);
             } else {
                 mFlyout.setVisibility(INVISIBLE);
                 mFlyout.setupFlyoutStartingAsDot(bubble.getFlyoutMessage(),
-                        mStackAnimationController.getStackPosition(), getWidth(),
+                        mStackAnimationController.getStackPosition(),
                         mStackAnimationController.isStackOnLeftSide(),
                         bubble.getIconView().getDotColor() /* dotColor */,
                         expandFlyoutAfterDelay /* onLayoutComplete */,
                         mAfterFlyoutHidden /* onHide */,
                         bubble.getIconView().getDotCenter(),
-                        !bubble.showDot(),
-                        mPositioner);
+                        !bubble.showDot());
             }
             mFlyout.bringToFront();
         });
@@ -2505,6 +2542,24 @@
             return;
         }
 
+        if (show) {
+            mManageMenuScrim.setVisibility(VISIBLE);
+            mManageMenuScrim.setTranslationZ(mManageMenu.getElevation() - 1f);
+        }
+        Runnable endAction = () -> {
+            if (!show) {
+                mManageMenuScrim.setVisibility(INVISIBLE);
+                mManageMenuScrim.setTranslationZ(0f);
+            }
+        };
+
+        mManageMenuScrim.animate()
+                .setDuration(MANAGE_MENU_SCRIM_ANIM_DURATION)
+                .setInterpolator(show ? ALPHA_IN : ALPHA_OUT)
+                .alpha(show ? SCRIM_ALPHA : 0f)
+                .withEndAction(endAction)
+                .start();
+
         // If available, update the manage menu's settings option with the expanded bubble's app
         // name and icon.
         if (show && mBubbleData.hasBubbleInStackWithKey(mExpandedBubble.getKey())) {
@@ -2514,7 +2569,6 @@
                     R.string.bubbles_app_settings, bubble.getAppName()));
         }
 
-        mExpandedBubble.getExpandedView().getManageButtonBoundsOnScreen(mTempRect);
         if (mExpandedBubble.getExpandedView().getTaskView() != null) {
             mExpandedBubble.getExpandedView().getTaskView().setObscuredTouchRect(mShowingManage
                     ? new Rect(0, 0, getWidth(), getHeight())
@@ -2526,7 +2580,11 @@
 
         // When the menu is open, it should be at these coordinates. The menu pops out to the right
         // in LTR and to the left in RTL.
-        final float targetX = isLtr ? mTempRect.left : mTempRect.right - mManageMenu.getWidth();
+        mExpandedBubble.getExpandedView().getManageButtonBoundsOnScreen(mTempRect);
+        final float margin = mExpandedBubble.getExpandedView().getManageButtonMargin();
+        final float targetX = isLtr
+                ? mTempRect.left - margin
+                : mTempRect.right + margin - mManageMenu.getWidth();
         final float targetY = mTempRect.bottom - mManageMenu.getHeight();
 
         final float xOffsetForAnimation = (isLtr ? 1 : -1) * mManageMenu.getWidth() / 4f;
@@ -2706,16 +2764,17 @@
         }
         boolean isOverflowExpanded = mExpandedBubble != null
                 && BubbleOverflow.KEY.equals(mExpandedBubble.getKey());
-        int[] paddings = mPositioner.getExpandedViewPadding(
+        int[] paddings = mPositioner.getExpandedViewContainerPadding(
                 mStackAnimationController.isStackOnLeftSide(), isOverflowExpanded);
         mExpandedViewContainer.setPadding(paddings[0], paddings[1], paddings[2], paddings[3]);
         if (mIsExpansionAnimating) {
             mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
         }
         if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+            PointF p = mPositioner.getExpandedBubbleXY(getBubbleIndex(mExpandedBubble),
+                    mBubbleContainer.getChildCount(), mStackOnLeftOrWillBe);
             mExpandedViewContainer.setTranslationY(mPositioner.getExpandedViewY(mExpandedBubble,
-                    mExpandedAnimationController.getBubbleXOrYForOrientation(
-                            getBubbleIndex(mExpandedBubble))));
+                    mPositioner.showBubblesVertically() ? p.y : p.x));
             mExpandedViewContainer.setTranslationX(0f);
             mExpandedBubble.getExpandedView().updateView(
                     mExpandedViewContainer.getLocationOnScreen());
@@ -2798,8 +2857,13 @@
         if (index == -1) {
             return;
         }
-        float bubblePosition = mExpandedAnimationController.getBubbleXOrYForOrientation(index);
-        mExpandedBubble.getExpandedView().setPointerPosition(bubblePosition, mStackOnLeftOrWillBe);
+        PointF bubblePosition = mPositioner.getExpandedBubbleXY(index,
+                mBubbleContainer.getChildCount(),
+                mStackOnLeftOrWillBe);
+        mExpandedBubble.getExpandedView().setPointerPosition(mPositioner.showBubblesVertically()
+                ? bubblePosition.y
+                : bubblePosition.x,
+                mStackOnLeftOrWillBe);
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index c73b5ee..9b7eb2f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -24,12 +24,10 @@
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.os.Bundle;
-import android.os.Looper;
 import android.service.notification.NotificationListenerService.RankingMap;
 import android.util.ArraySet;
 import android.util.Pair;
 import android.util.SparseArray;
-import android.view.View;
 
 import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
@@ -43,7 +41,6 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.concurrent.Executor;
-import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 import java.util.function.IntConsumer;
 
@@ -160,14 +157,6 @@
     /** Set the proxy to commnuicate with SysUi side components. */
     void setSysuiProxy(SysuiProxy proxy);
 
-    /**
-     * Set the scrim view for bubbles.
-     *
-     * @param callback The callback made with the executor and the executor's looper that the view
-     *                 will be running on.
-     **/
-    void setBubbleScrim(View view, BiConsumer<Executor, Looper> callback);
-
     /** Set a listener to be notified of bubble expand events. */
     void setExpandListener(BubbleExpandListener listener);
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
index 4cc6702..eb4737a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
@@ -18,12 +18,13 @@
 import android.content.Context
 import android.graphics.Color
 import android.graphics.Rect
+import android.graphics.drawable.ColorDrawable
 import android.view.LayoutInflater
 import android.view.View
+import android.view.ViewGroup
 import android.widget.Button
 import android.widget.LinearLayout
-import android.widget.TextView
-import com.android.internal.util.ContrastColorUtil
+import com.android.internal.R.color.system_neutral1_900
 import com.android.wm.shell.R
 import com.android.wm.shell.animation.Interpolators
 
@@ -31,21 +32,22 @@
  * User education view to highlight the manage button that allows a user to configure the settings
  * for the bubble. Shown only the first time a user expands a bubble.
  */
-class ManageEducationView constructor(context: Context) : LinearLayout(context) {
+class ManageEducationView constructor(context: Context, positioner: BubblePositioner)
+    : LinearLayout(context) {
 
-    private val TAG = if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "BubbleManageEducationView"
+    private val TAG = if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "ManageEducationView"
         else BubbleDebugConfig.TAG_BUBBLES
 
     private val ANIMATE_DURATION: Long = 200
-    private val ANIMATE_DURATION_SHORT: Long = 40
 
-    private val manageView by lazy { findViewById<View>(R.id.manage_education_view) }
-    private val manageButton by lazy { findViewById<Button>(R.id.manage) }
+    private val positioner: BubblePositioner = positioner
+    private val manageView by lazy { findViewById<ViewGroup>(R.id.manage_education_view) }
+    private val manageButton by lazy { findViewById<Button>(R.id.manage_button) }
     private val gotItButton by lazy { findViewById<Button>(R.id.got_it) }
-    private val titleTextView by lazy { findViewById<TextView>(R.id.user_education_title) }
-    private val descTextView by lazy { findViewById<TextView>(R.id.user_education_description) }
 
     private var isHiding = false
+    private var realManageButtonRect = Rect()
+    private var bubbleExpandedView: BubbleExpandedView? = null
 
     init {
         LayoutInflater.from(context).inflate(R.layout.bubbles_manage_button_education, this)
@@ -66,18 +68,17 @@
     override fun onFinishInflate() {
         super.onFinishInflate()
         layoutDirection = resources.configuration.layoutDirection
-        setTextColor()
     }
 
-    private fun setTextColor() {
-        val typedArray = mContext.obtainStyledAttributes(intArrayOf(android.R.attr.colorAccent,
-            android.R.attr.textColorPrimaryInverse))
-        val bgColor = typedArray.getColor(0 /* index */, Color.BLACK)
-        var textColor = typedArray.getColor(1 /* index */, Color.WHITE)
+    private fun setButtonColor() {
+        val typedArray = mContext.obtainStyledAttributes(intArrayOf(
+                com.android.internal.R.attr.colorAccentPrimary))
+        val buttonColor = typedArray.getColor(0 /* index */, Color.TRANSPARENT)
         typedArray.recycle()
-        textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, true)
-        titleTextView.setTextColor(textColor)
-        descTextView.setTextColor(textColor)
+
+        manageButton.setTextColor(mContext.getColor(system_neutral1_900))
+        manageButton.setBackgroundDrawable(ColorDrawable(buttonColor))
+        gotItButton.setBackgroundDrawable(ColorDrawable(buttonColor))
     }
 
     private fun setDrawableDirection() {
@@ -91,30 +92,39 @@
      * If necessary, toggles the user education view for the manage button. This is shown when the
      * bubble stack is expanded for the first time.
      *
-     * @param show whether the user education view should show or not.
+     * @param expandedView the expandedView the user education is shown on top of.
      */
-    fun show(expandedView: BubbleExpandedView, rect: Rect) {
+    fun show(expandedView: BubbleExpandedView) {
+        setButtonColor()
         if (visibility == VISIBLE) return
 
+        bubbleExpandedView = expandedView
+        expandedView.taskView?.setObscuredTouchRect(Rect(positioner.screenRect))
+
+        layoutParams.width = if (positioner.isLargeScreen)
+            context.resources.getDimensionPixelSize(
+                    R.dimen.bubbles_user_education_width_large_screen)
+        else ViewGroup.LayoutParams.MATCH_PARENT
+
         alpha = 0f
         visibility = View.VISIBLE
+        expandedView.getManageButtonBoundsOnScreen(realManageButtonRect)
+        manageView.setPadding(realManageButtonRect.left - expandedView.manageButtonMargin,
+                manageView.paddingTop, manageView.paddingRight, manageView.paddingBottom)
         post {
-            expandedView.getManageButtonBoundsOnScreen(rect)
-
             manageButton
                 .setOnClickListener {
-                    expandedView.findViewById<View>(R.id.settings_button).performClick()
-                    hide(true /* isStackExpanding */)
+                    hide()
+                    expandedView.findViewById<View>(R.id.manage_button).performClick()
                 }
-            gotItButton.setOnClickListener { hide(true /* isStackExpanding */) }
-            setOnClickListener { hide(true /* isStackExpanding */) }
+            gotItButton.setOnClickListener { hide() }
+            setOnClickListener { hide() }
 
-            with(manageView) {
-                translationX = 0f
-                val inset = resources.getDimensionPixelSize(
-                    R.dimen.bubbles_manage_education_top_inset)
-                translationY = (rect.top - manageView.height + inset).toFloat()
-            }
+            val offsetViewBounds = Rect()
+            manageButton.getDrawingRect(offsetViewBounds)
+            manageView.offsetDescendantRectToMyCoords(manageButton, offsetViewBounds)
+            translationX = 0f
+            translationY = (realManageButtonRect.top - offsetViewBounds.top).toFloat()
             bringToFront()
             animate()
                 .setDuration(ANIMATE_DURATION)
@@ -124,13 +134,14 @@
         setShouldShow(false)
     }
 
-    fun hide(isStackExpanding: Boolean) {
+    fun hide() {
+        bubbleExpandedView?.taskView?.setObscuredTouchRect(null)
         if (visibility != VISIBLE || isHiding) return
 
         animate()
             .withStartAction { isHiding = true }
             .alpha(0f)
-            .setDuration(if (isStackExpanding) ANIMATE_DURATION_SHORT else ANIMATE_DURATION)
+            .setDuration(ANIMATE_DURATION)
             .withEndAction {
                 isHiding = false
                 visibility = GONE
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
index 0a2cfc4..f6a90b7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
@@ -18,8 +18,11 @@
 import android.content.Context
 import android.graphics.Color
 import android.graphics.PointF
+import android.view.KeyEvent
 import android.view.LayoutInflater
 import android.view.View
+import android.view.View.OnKeyListener
+import android.view.ViewGroup
 import android.widget.LinearLayout
 import android.widget.TextView
 import com.android.internal.util.ContrastColorUtil
@@ -30,7 +33,12 @@
  * User education view to highlight the collapsed stack of bubbles.
  * Shown only the first time a user taps the stack.
  */
-class StackEducationView constructor(context: Context) : LinearLayout(context) {
+class StackEducationView constructor(
+    context: Context,
+    positioner: BubblePositioner,
+    controller: BubbleController
+)
+    : LinearLayout(context) {
 
     private val TAG = if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "BubbleStackEducationView"
         else BubbleDebugConfig.TAG_BUBBLES
@@ -38,6 +46,9 @@
     private val ANIMATE_DURATION: Long = 200
     private val ANIMATE_DURATION_SHORT: Long = 40
 
+    private val positioner: BubblePositioner = positioner
+    private val controller: BubbleController = controller
+
     private val view by lazy { findViewById<View>(R.id.stack_education_layout) }
     private val titleTextView by lazy { findViewById<TextView>(R.id.stack_education_title) }
     private val descTextView by lazy { findViewById<TextView>(R.id.stack_education_description) }
@@ -67,6 +78,28 @@
         setTextColor()
     }
 
+    override fun onAttachedToWindow() {
+        super.onAttachedToWindow()
+        setFocusableInTouchMode(true)
+        setOnKeyListener(object : OnKeyListener {
+            override fun onKey(v: View?, keyCode: Int, event: KeyEvent): Boolean {
+                // if the event is a key down event on the enter button
+                if (event.action == KeyEvent.ACTION_UP &&
+                        keyCode == KeyEvent.KEYCODE_BACK && !isHiding) {
+                    hide(false)
+                    return true
+                }
+                return false
+            }
+        })
+    }
+
+    override fun onDetachedFromWindow() {
+        super.onDetachedFromWindow()
+        setOnKeyListener(null)
+        controller.updateWindowFlagsForBackpress(false /* interceptBack */)
+    }
+
     private fun setTextColor() {
         val ta = mContext.obtainStyledAttributes(intArrayOf(android.R.attr.colorAccent,
             android.R.attr.textColorPrimaryInverse))
@@ -94,13 +127,25 @@
     fun show(stackPosition: PointF): Boolean {
         if (visibility == VISIBLE) return false
 
+        controller.updateWindowFlagsForBackpress(true /* interceptBack */)
+        layoutParams.width = if (positioner.isLargeScreen)
+            context.resources.getDimensionPixelSize(
+                    R.dimen.bubbles_user_education_width_large_screen)
+        else ViewGroup.LayoutParams.MATCH_PARENT
+
         setAlpha(0f)
         setVisibility(View.VISIBLE)
         post {
+            requestFocus()
             with(view) {
-                val bubbleSize = context.resources.getDimensionPixelSize(
-                    R.dimen.bubble_size)
-                translationY = stackPosition.y + bubbleSize / 2 - getHeight() / 2
+                if (resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR) {
+                    setPadding(positioner.bubbleSize + paddingRight, paddingTop, paddingRight,
+                            paddingBottom)
+                } else {
+                    setPadding(paddingLeft, paddingTop, positioner.bubbleSize + paddingLeft,
+                            paddingBottom)
+                }
+                translationY = stackPosition.y + positioner.bubbleSize / 2 - getHeight() / 2
             }
             animate()
                 .setDuration(ANIMATE_DURATION)
@@ -114,15 +159,16 @@
     /**
      * If necessary, hides the stack education view.
      *
-     * @param fromExpansion if true this indicates the hide is happening due to the bubble being
+     * @param isExpanding if true this indicates the hide is happening due to the bubble being
      *                      expanded, false if due to a touch outside of the bubble stack.
      */
-    fun hide(fromExpansion: Boolean) {
+    fun hide(isExpanding: Boolean) {
         if (visibility != VISIBLE || isHiding) return
 
+        controller.updateWindowFlagsForBackpress(false /* interceptBack */)
         animate()
             .alpha(0f)
-            .setDuration(if (fromExpansion) ANIMATE_DURATION_SHORT else ANIMATE_DURATION)
+            .setDuration(if (isExpanding) ANIMATE_DURATION_SHORT else ANIMATE_DURATION)
             .withEndAction { visibility = GONE }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
index efe07fb..c32be98 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
@@ -64,9 +64,6 @@
     /** Stiffness for the expand/collapse path-following animation. */
     private static final int EXPAND_COLLAPSE_ANIM_STIFFNESS = 1000;
 
-    /** What percentage of the screen to use when centering the bubbles in landscape. */
-    private static final float CENTER_BUBBLES_LANDSCAPE_PERCENT = 0.66f;
-
     /**
      * Velocity required to dismiss an individual bubble without dragging it into the dismiss
      * target.
@@ -79,8 +76,6 @@
 
     /** Horizontal offset between bubbles, which we need to know to re-stack them. */
     private float mStackOffsetPx;
-    /** Space between status bar and bubbles in the expanded state. */
-    private float mBubblePaddingTop;
     /** Size of each bubble. */
     private float mBubbleSizePx;
     /** Whether the expand / collapse animation is running. */
@@ -121,8 +116,6 @@
     /** The bubble currently being dragged out of the row (to potentially be dismissed). */
     private MagnetizedObject<View> mMagnetizedBubbleDraggingOut;
 
-    private int mExpandedViewPadding;
-
     /**
      * Callback to run whenever any bubble is animated out. The BubbleStackView will check if the
      * end of this animation means we have no bubbles left, and notify the BubbleController.
@@ -131,11 +124,10 @@
 
     private BubblePositioner mPositioner;
 
-    public ExpandedAnimationController(BubblePositioner positioner, int expandedViewPadding,
+    public ExpandedAnimationController(BubblePositioner positioner,
             Runnable onBubbleAnimatedOutAction) {
         mPositioner = positioner;
         updateResources();
-        mExpandedViewPadding = expandedViewPadding;
         mOnBubbleAnimatedOutAction = onBubbleAnimatedOutAction;
         mCollapsePoint = mPositioner.getDefaultStartPosition();
     }
@@ -202,7 +194,6 @@
             return;
         }
         Resources res = mLayout.getContext().getResources();
-        mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
         mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
         mBubbleSizePx = mPositioner.getBubbleSize();
     }
@@ -248,31 +239,22 @@
             final Path path = new Path();
             path.moveTo(bubble.getTranslationX(), bubble.getTranslationY());
 
-            final float expandedY = mPositioner.showBubblesVertically()
-                    ? getBubbleXOrYForOrientation(index)
-                    : getExpandedY();
+            boolean onLeft = mPositioner.isStackOnLeft(mCollapsePoint);
+            final PointF p = mPositioner.getExpandedBubbleXY(index,
+                    mLayout.getChildCount(),
+                    onLeft);
             if (expanding) {
-                // If we're expanding, first draw a line from the bubble's current position to the
-                // top of the screen.
-                path.lineTo(bubble.getTranslationX(), expandedY);
+                // If we're expanding, first draw a line from the bubble's current position to where
+                // it'll end up
+                path.lineTo(bubble.getTranslationX(), p.y);
                 // Then, draw a line across the screen to the bubble's resting position.
-                if (mPositioner.showBubblesVertically()) {
-                    Rect availableRect = mPositioner.getAvailableRect();
-                    boolean onLeft = mCollapsePoint != null
-                            && mCollapsePoint.x < (availableRect.width() / 2f);
-                    float translationX = onLeft
-                            ? availableRect.left
-                            : availableRect.right - mBubbleSizePx;
-                    path.lineTo(translationX, getBubbleXOrYForOrientation(index));
-                } else {
-                    path.lineTo(getBubbleXOrYForOrientation(index), expandedY);
-                }
+                path.lineTo(p.x, p.y);
             } else {
                 final float stackedX = mCollapsePoint.x;
 
                 // If we're collapsing, draw a line from the bubble's current position to the side
                 // of the screen where the bubble will be stacked.
-                path.lineTo(stackedX, expandedY);
+                path.lineTo(stackedX, p.y);
 
                 // Then, draw a line down to the stack position.
                 path.lineTo(stackedX, mCollapsePoint.y
@@ -382,8 +364,9 @@
             bubbleView.setTranslationY(y);
         }
 
+        final float expandedY = mPositioner.getExpandedBubblesY();
         final boolean draggedOutEnough =
-                y > getExpandedY() + mBubbleSizePx || y < getExpandedY() - mBubbleSizePx;
+                y > expandedY + mBubbleSizePx || y < expandedY - mBubbleSizePx;
         if (draggedOutEnough != mBubbleDraggedOutEnough) {
             updateBubblePositions();
             mBubbleDraggedOutEnough = draggedOutEnough;
@@ -427,9 +410,10 @@
             return;
         }
         final int index = mLayout.indexOfChild(bubbleView);
-
+        final PointF p = mPositioner.getExpandedBubbleXY(index, mLayout.getChildCount(),
+                mPositioner.isStackOnLeft(mCollapsePoint));
         animationForChildAtIndex(index)
-                .position(getBubbleXOrYForOrientation(index), getExpandedY())
+                .position(p.x, p.y)
                 .withPositionStartVelocities(velX, velY)
                 .start(() -> bubbleView.setTranslationZ(0f) /* after */);
 
@@ -446,17 +430,13 @@
     }
 
     /**
-     * Animates the bubbles to {@link #getExpandedY()} position. Used in response to IME showing.
+     * Animates the bubbles to the y position. Used in response to IME showing.
      */
     public void updateYPosition(Runnable after) {
         if (mLayout == null) return;
         animationsForChildrenFromIndex(
-                0, (i, anim) -> anim.translationY(getExpandedY())).startAll(after);
-    }
-
-    /** The Y value of the row of expanded bubbles. */
-    public float getExpandedY() {
-        return mPositioner.getAvailableRect().top + mBubblePaddingTop;
+                0, (i, anim) -> anim.translationY(mPositioner.getExpandedBubblesY()))
+                .startAll(after);
     }
 
     /** Description of current animation controller state. */
@@ -514,35 +494,36 @@
             startOrUpdatePathAnimation(true /* expanding */);
         } else if (mAnimatingCollapse) {
             startOrUpdatePathAnimation(false /* expanding */);
-        } else if (mPositioner.showBubblesVertically()) {
-            child.setTranslationY(getBubbleXOrYForOrientation(index));
-            if (!mPreparingToCollapse) {
-                // Only animate if we're not collapsing as that animation will handle placing the
-                // new bubble in the stacked position.
-                Rect availableRect = mPositioner.getAvailableRect();
-                boolean onLeft = mCollapsePoint != null
-                        && mCollapsePoint.x < (availableRect.width() / 2f);
-                float fromX = onLeft
-                        ? -mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR
-                        : availableRect.right + mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR;
-                float toX = onLeft
-                        ? availableRect.left + mExpandedViewPadding
-                        : availableRect.right - mBubbleSizePx - mExpandedViewPadding;
-                animationForChild(child)
-                        .translationX(fromX, toX)
-                        .start();
-                updateBubblePositions();
-            }
         } else {
-            child.setTranslationX(getBubbleXOrYForOrientation(index));
+            boolean onLeft = mPositioner.isStackOnLeft(mCollapsePoint);
+            final PointF p = mPositioner.getExpandedBubbleXY(index,
+                    mLayout.getChildCount(),
+                    onLeft);
+            if (mPositioner.showBubblesVertically()) {
+                child.setTranslationY(p.y);
+            } else {
+                child.setTranslationX(p.x);
+            }
             if (!mPreparingToCollapse) {
                 // Only animate if we're not collapsing as that animation will handle placing the
                 // new bubble in the stacked position.
-                float toY = getExpandedY();
-                float fromY = getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR;
-                animationForChild(child)
-                        .translationY(fromY, toY)
-                        .start();
+                if (mPositioner.showBubblesVertically()) {
+                    Rect availableRect = mPositioner.getAvailableRect();
+                    float fromX = onLeft
+                            ? -mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR
+                            : availableRect.right + mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR;
+                    animationForChild(child)
+                            .translationX(fromX, p.y)
+                            .start();
+                } else {
+                    // Only animate if we're not collapsing as that animation will handle placing
+                    // the new bubble in the stacked position.
+                    float fromY = mPositioner.getExpandedBubblesY() - mBubbleSizePx
+                            * ANIMATE_TRANSLATION_FACTOR;
+                    animationForChild(child)
+                            .translationY(fromY, p.y)
+                            .start();
+                }
                 updateBubblePositions();
             }
         }
@@ -591,7 +572,7 @@
         if (mAnimatingExpand || mAnimatingCollapse) {
             return;
         }
-
+        boolean onLeft = mPositioner.isStackOnLeft(mCollapsePoint);
         for (int i = 0; i < mLayout.getChildCount(); i++) {
             final View bubble = mLayout.getChildAt(i);
 
@@ -601,39 +582,11 @@
                 return;
             }
 
-            if (mPositioner.showBubblesVertically()) {
-                Rect availableRect = mPositioner.getAvailableRect();
-                boolean onLeft = mCollapsePoint != null
-                        && mCollapsePoint.x < (availableRect.width() / 2f);
-                animationForChild(bubble)
-                        .translationX(onLeft
-                                ? availableRect.left
-                                : availableRect.right - mBubbleSizePx)
-                        .translationY(getBubbleXOrYForOrientation(i))
-                        .start();
-            } else {
-                animationForChild(bubble)
-                        .translationX(getBubbleXOrYForOrientation(i))
-                        .translationY(getExpandedY())
-                        .start();
-            }
+            final PointF p = mPositioner.getExpandedBubbleXY(i, mLayout.getChildCount(), onLeft);
+            animationForChild(bubble)
+                    .translationX(p.x)
+                    .translationY(p.y)
+                    .start();
         }
     }
-
-    /**
-     * When bubbles are expanded in portrait, they display at the top of the screen in a horizontal
-     * row. When in landscape or on a large screen, they show at the left or right side in a
-     * vertical row. This method accounts for screen orientation and will return an x or y value
-     * for the position of the bubble in the row.
-     *
-     * @param index bubble index in the row.
-     * @return the y position of the bubble if showing vertically and the x position if showing
-     * horizontally.
-     */
-    public float getBubbleXOrYForOrientation(int index) {
-        if (mLayout == null) {
-            return 0;
-        }
-        return mPositioner.getBubbleXOrYForOrientation(index, mLayout.getChildCount());
-    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
index 636e145..9a08190 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
@@ -305,10 +305,7 @@
         if (mLayout == null || !isStackPositionSet()) {
             return true; // Default to left, which is where it starts by default.
         }
-
-        float stackCenter = mStackPosition.x + mBubbleSize / 2;
-        float screenCenter = mLayout.getWidth() / 2;
-        return stackCenter < screenCenter;
+        return mPositioner.isStackOnLeft(mStackPosition);
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
index 3a7b534..ffda1f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java
@@ -17,6 +17,7 @@
 package com.android.wm.shell.common;
 
 import android.os.RemoteException;
+import android.util.Slog;
 import android.view.IDisplayWindowRotationCallback;
 import android.view.IDisplayWindowRotationController;
 import android.view.IWindowManager;
@@ -27,6 +28,7 @@
 import com.android.wm.shell.common.annotations.ShellMainThread;
 
 import java.util.ArrayList;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
  * This module deals with display rotations coming from WM. When WM starts a rotation: after it has
@@ -35,14 +37,14 @@
  * rotation.
  */
 public class DisplayChangeController {
+    private static final String TAG = DisplayChangeController.class.getSimpleName();
 
     private final ShellExecutor mMainExecutor;
     private final IWindowManager mWmService;
     private final IDisplayWindowRotationController mControllerImpl;
 
-    private final ArrayList<OnDisplayChangingListener> mRotationListener =
-            new ArrayList<>();
-    private final ArrayList<OnDisplayChangingListener> mTmpListeners = new ArrayList<>();
+    private final CopyOnWriteArrayList<OnDisplayChangingListener> mRotationListener =
+            new CopyOnWriteArrayList<>();
 
     public DisplayChangeController(IWindowManager wmService, ShellExecutor mainExecutor) {
         mMainExecutor = mainExecutor;
@@ -59,34 +61,26 @@
      * Adds a display rotation controller.
      */
     public void addRotationListener(OnDisplayChangingListener listener) {
-        synchronized (mRotationListener) {
-            mRotationListener.add(listener);
-        }
+        mRotationListener.add(listener);
     }
 
     /**
      * Removes a display rotation controller.
      */
     public void removeRotationListener(OnDisplayChangingListener listener) {
-        synchronized (mRotationListener) {
-            mRotationListener.remove(listener);
-        }
+        mRotationListener.remove(listener);
     }
 
     private void onRotateDisplay(int displayId, final int fromRotation, final int toRotation,
             IDisplayWindowRotationCallback callback) {
         WindowContainerTransaction t = new WindowContainerTransaction();
-        synchronized (mRotationListener) {
-            mTmpListeners.clear();
-            // Make a local copy in case the handlers add/remove themselves.
-            mTmpListeners.addAll(mRotationListener);
-        }
-        for (OnDisplayChangingListener c : mTmpListeners) {
+        for (OnDisplayChangingListener c : mRotationListener) {
             c.onRotateDisplay(displayId, fromRotation, toRotation, t);
         }
         try {
             callback.continueRotateDisplay(toRotation, t);
         } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to continue rotation", e);
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
index ba9ba5e..9a3bdab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
@@ -26,6 +26,7 @@
 import android.view.Display;
 import android.view.IDisplayWindowListener;
 import android.view.IWindowManager;
+import android.view.InsetsState;
 
 import androidx.annotation.BinderThread;
 
@@ -52,14 +53,6 @@
     private final SparseArray<DisplayRecord> mDisplays = new SparseArray<>();
     private final ArrayList<OnDisplaysChangedListener> mDisplayChangedListeners = new ArrayList<>();
 
-    /**
-     * Gets a display by id from DisplayManager.
-     */
-    public Display getDisplay(int displayId) {
-        final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
-        return displayManager.getDisplay(displayId);
-    }
-
     public DisplayController(Context context, IWindowManager wmService,
             ShellExecutor mainExecutor) {
         mMainExecutor = mainExecutor;
@@ -67,14 +60,28 @@
         mWmService = wmService;
         mChangeController = new DisplayChangeController(mWmService, mainExecutor);
         mDisplayContainerListener = new DisplayWindowListenerImpl();
+    }
+
+    /**
+     * Initializes the window listener.
+     */
+    public void initialize() {
         try {
             mWmService.registerDisplayWindowListener(mDisplayContainerListener);
         } catch (RemoteException e) {
-            throw new RuntimeException("Unable to register hierarchy listener");
+            throw new RuntimeException("Unable to register display controller");
         }
     }
 
     /**
+     * Gets a display by id from DisplayManager.
+     */
+    public Display getDisplay(int displayId) {
+        final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+        return displayManager.getDisplay(displayId);
+    }
+
+    /**
      * Gets the DisplayLayout associated with a display.
      */
     public @Nullable DisplayLayout getDisplayLayout(int displayId) {
@@ -91,6 +98,16 @@
     }
 
     /**
+     * Updates the insets for a given display.
+     */
+    public void updateDisplayInsets(int displayId, InsetsState state) {
+        final DisplayRecord r = mDisplays.get(displayId);
+        if (r != null) {
+            r.setInsets(state);
+        }
+    }
+
+    /**
      * Add a display window-container listener. It will get notified whenever a display's
      * configuration changes or when displays are added/removed from the WM hierarchy.
      */
@@ -134,17 +151,18 @@
             if (mDisplays.get(displayId) != null) {
                 return;
             }
-            Display display = getDisplay(displayId);
+            final Display display = getDisplay(displayId);
             if (display == null) {
                 // It's likely that the display is private to some app and thus not
                 // accessible by system-ui.
                 return;
             }
-            DisplayRecord record = new DisplayRecord();
-            record.mDisplayId = displayId;
-            record.mContext = (displayId == Display.DEFAULT_DISPLAY) ? mContext
+
+            final Context context = (displayId == Display.DEFAULT_DISPLAY)
+                    ? mContext
                     : mContext.createDisplayContext(display);
-            record.mDisplayLayout = new DisplayLayout(record.mContext, display);
+            final DisplayRecord record = new DisplayRecord(displayId);
+            record.setDisplayLayout(context, new DisplayLayout(context, display));
             mDisplays.put(displayId, record);
             for (int i = 0; i < mDisplayChangedListeners.size(); ++i) {
                 mDisplayChangedListeners.get(i).onDisplayAdded(displayId);
@@ -154,24 +172,23 @@
 
     private void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
         synchronized (mDisplays) {
-            DisplayRecord dr = mDisplays.get(displayId);
+            final DisplayRecord dr = mDisplays.get(displayId);
             if (dr == null) {
                 Slog.w(TAG, "Skipping Display Configuration change on non-added"
                         + " display.");
                 return;
             }
-            Display display = getDisplay(displayId);
+            final Display display = getDisplay(displayId);
             if (display == null) {
                 Slog.w(TAG, "Skipping Display Configuration change on invalid"
                         + " display. It may have been removed.");
                 return;
             }
-            Context perDisplayContext = mContext;
-            if (displayId != Display.DEFAULT_DISPLAY) {
-                perDisplayContext = mContext.createDisplayContext(display);
-            }
-            dr.mContext = perDisplayContext.createConfigurationContext(newConfig);
-            dr.mDisplayLayout = new DisplayLayout(dr.mContext, display);
+            final Context perDisplayContext = (displayId == Display.DEFAULT_DISPLAY)
+                    ? mContext
+                    : mContext.createDisplayContext(display);
+            final Context context = perDisplayContext.createConfigurationContext(newConfig);
+            dr.setDisplayLayout(context, new DisplayLayout(context, display));
             for (int i = 0; i < mDisplayChangedListeners.size(); ++i) {
                 mDisplayChangedListeners.get(i).onDisplayConfigurationChanged(
                         displayId, newConfig);
@@ -219,9 +236,25 @@
     }
 
     private static class DisplayRecord {
-        int mDisplayId;
-        Context mContext;
-        DisplayLayout mDisplayLayout;
+        private int mDisplayId;
+        private Context mContext;
+        private DisplayLayout mDisplayLayout;
+        private InsetsState mInsetsState = new InsetsState();
+
+        private DisplayRecord(int displayId) {
+            mDisplayId = displayId;
+        }
+
+        private void setDisplayLayout(Context context, DisplayLayout displayLayout) {
+            mContext = context;
+            mDisplayLayout = displayLayout;
+            mDisplayLayout.setInsets(mContext.getResources(), mInsetsState);
+        }
+
+        private void setInsets(InsetsState state) {
+            mInsetsState = state;
+            mDisplayLayout.setInsets(mContext.getResources(), state);
+        }
     }
 
     @BinderThread
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index ba59e07..a7052bc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -69,14 +69,17 @@
     protected final Executor mMainExecutor;
     private final TransactionPool mTransactionPool;
     private final DisplayController mDisplayController;
+    private final DisplayInsetsController mDisplayInsetsController;
     private final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>();
     private final ArrayList<ImePositionProcessor> mPositionProcessors = new ArrayList<>();
 
 
     public DisplayImeController(IWindowManager wmService, DisplayController displayController,
+            DisplayInsetsController displayInsetsController,
             Executor mainExecutor, TransactionPool transactionPool) {
         mWmService = wmService;
         mDisplayController = displayController;
+        mDisplayInsetsController = displayInsetsController;
         mMainExecutor = mainExecutor;
         mTransactionPool = transactionPool;
     }
@@ -110,11 +113,11 @@
 
     @Override
     public void onDisplayRemoved(int displayId) {
-        try {
-            mWmService.setDisplayWindowInsetsController(displayId, null);
-        } catch (RemoteException e) {
-            Slog.w(TAG, "Unable to remove insets controller on display " + displayId);
+        PerDisplay pd = mImePerDisplay.get(displayId);
+        if (pd == null) {
+            return;
         }
+        pd.unregister();
         mImePerDisplay.remove(displayId);
     }
 
@@ -196,12 +199,10 @@
     }
 
     /** An implementation of {@link IDisplayWindowInsetsController} for a given display id. */
-    public class PerDisplay {
+    public class PerDisplay implements DisplayInsetsController.OnInsetsChangedListener {
         final int mDisplayId;
         final InsetsState mInsetsState = new InsetsState();
         final InsetsVisibilities mRequestedVisibilities = new InsetsVisibilities();
-        protected final DisplayWindowInsetsControllerImpl mInsetsControllerImpl =
-                new DisplayWindowInsetsControllerImpl();
         InsetsSourceControl mImeSourceControl = null;
         int mAnimationDirection = DIRECTION_NONE;
         ValueAnimator mAnimation = null;
@@ -216,14 +217,15 @@
         }
 
         public void register() {
-            try {
-                mWmService.setDisplayWindowInsetsController(mDisplayId, mInsetsControllerImpl);
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Unable to set insets controller on display " + mDisplayId);
-            }
+            mDisplayInsetsController.addInsetsChangedListener(mDisplayId, this);
         }
 
-        protected void insetsChanged(InsetsState insetsState) {
+        public void unregister() {
+            mDisplayInsetsController.removeInsetsChangedListener(mDisplayId, this);
+        }
+
+        @Override
+        public void insetsChanged(InsetsState insetsState) {
             if (mInsetsState.equals(insetsState)) {
                 return;
             }
@@ -241,8 +243,9 @@
             }
         }
 
+        @Override
         @VisibleForTesting
-        protected void insetsControlChanged(InsetsState insetsState,
+        public void insetsControlChanged(InsetsState insetsState,
                 InsetsSourceControl[] activeControls) {
             insetsChanged(insetsState);
             InsetsSourceControl imeSourceControl = null;
@@ -303,7 +306,8 @@
             }
         }
 
-        protected void showInsets(int types, boolean fromIme) {
+        @Override
+        public void showInsets(int types, boolean fromIme) {
             if ((types & WindowInsets.Type.ime()) == 0) {
                 return;
             }
@@ -311,8 +315,8 @@
             startAnimation(true /* show */, false /* forceRestart */);
         }
 
-
-        protected void hideInsets(int types, boolean fromIme) {
+        @Override
+        public void hideInsets(int types, boolean fromIme) {
             if ((types & WindowInsets.Type.ime()) == 0) {
                 return;
             }
@@ -320,6 +324,7 @@
             startAnimation(false /* show */, false /* forceRestart */);
         }
 
+        @Override
         public void topFocusedWindowChanged(String packageName) {
             // Do nothing
         }
@@ -493,47 +498,6 @@
                 dispatchVisibilityChanged(mDisplayId, isShowing);
             }
         }
-
-        @VisibleForTesting
-        @BinderThread
-        public class DisplayWindowInsetsControllerImpl
-                extends IDisplayWindowInsetsController.Stub {
-            @Override
-            public void topFocusedWindowChanged(String packageName) throws RemoteException {
-                mMainExecutor.execute(() -> {
-                    PerDisplay.this.topFocusedWindowChanged(packageName);
-                });
-            }
-
-            @Override
-            public void insetsChanged(InsetsState insetsState) throws RemoteException {
-                mMainExecutor.execute(() -> {
-                    PerDisplay.this.insetsChanged(insetsState);
-                });
-            }
-
-            @Override
-            public void insetsControlChanged(InsetsState insetsState,
-                    InsetsSourceControl[] activeControls) throws RemoteException {
-                mMainExecutor.execute(() -> {
-                    PerDisplay.this.insetsControlChanged(insetsState, activeControls);
-                });
-            }
-
-            @Override
-            public void showInsets(int types, boolean fromIme) throws RemoteException {
-                mMainExecutor.execute(() -> {
-                    PerDisplay.this.showInsets(types, fromIme);
-                });
-            }
-
-            @Override
-            public void hideInsets(int types, boolean fromIme) throws RemoteException {
-                mMainExecutor.execute(() -> {
-                    PerDisplay.this.hideInsets(types, fromIme);
-                });
-            }
-        }
     }
 
     void removeImeSurface() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java
new file mode 100644
index 0000000..5f3de7e
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java
@@ -0,0 +1,265 @@
+/*
+ * 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.wm.shell.common;
+
+import android.os.RemoteException;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.IDisplayWindowInsetsController;
+import android.view.IWindowManager;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
+
+import androidx.annotation.BinderThread;
+
+import com.android.wm.shell.common.annotations.ShellMainThread;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Manages insets from the core.
+ */
+public class DisplayInsetsController implements DisplayController.OnDisplaysChangedListener {
+    private static final String TAG = "DisplayInsetsController";
+
+    private final IWindowManager mWmService;
+    private final ShellExecutor mMainExecutor;
+    private final DisplayController mDisplayController;
+    private final SparseArray<PerDisplay> mInsetsPerDisplay = new SparseArray<>();
+    private final SparseArray<CopyOnWriteArrayList<OnInsetsChangedListener>> mListeners =
+            new SparseArray<>();
+
+    public DisplayInsetsController(IWindowManager wmService, DisplayController displayController,
+            ShellExecutor mainExecutor) {
+        mWmService = wmService;
+        mDisplayController = displayController;
+        mMainExecutor = mainExecutor;
+    }
+
+    /**
+     * Starts listening for insets for each display.
+     **/
+    public void initialize() {
+        mDisplayController.addDisplayWindowListener(this);
+    }
+
+    /**
+     * Adds a callback to listen for insets changes for a particular display.  Note that the
+     * listener will not be updated with the existing state of the insets on that display.
+     */
+    public void addInsetsChangedListener(int displayId, OnInsetsChangedListener listener) {
+        CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(displayId);
+        if (listeners == null) {
+            listeners = new CopyOnWriteArrayList<>();
+            mListeners.put(displayId, listeners);
+        }
+        if (!listeners.contains(listener)) {
+            listeners.add(listener);
+        }
+    }
+
+    /**
+     * Removes a callback listening for insets changes from a particular display.
+     */
+    public void removeInsetsChangedListener(int displayId, OnInsetsChangedListener listener) {
+        CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(displayId);
+        if (listeners == null) {
+            return;
+        }
+        listeners.remove(listener);
+    }
+
+    @Override
+    public void onDisplayAdded(int displayId) {
+        PerDisplay pd = new PerDisplay(displayId);
+        pd.register();
+        mInsetsPerDisplay.put(displayId, pd);
+    }
+
+    @Override
+    public void onDisplayRemoved(int displayId) {
+        PerDisplay pd = mInsetsPerDisplay.get(displayId);
+        if (pd == null) {
+            return;
+        }
+        pd.unregister();
+        mInsetsPerDisplay.remove(displayId);
+    }
+
+    /**
+     * An implementation of {@link IDisplayWindowInsetsController} for a given display id.
+     **/
+    public class PerDisplay {
+        private final int mDisplayId;
+        private final DisplayWindowInsetsControllerImpl mInsetsControllerImpl =
+                new DisplayWindowInsetsControllerImpl();
+
+        public PerDisplay(int displayId) {
+            mDisplayId = displayId;
+        }
+
+        public void register() {
+            try {
+                mWmService.setDisplayWindowInsetsController(mDisplayId, mInsetsControllerImpl);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Unable to set insets controller on display " + mDisplayId);
+            }
+        }
+
+        public void unregister() {
+            try {
+                mWmService.setDisplayWindowInsetsController(mDisplayId, null);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Unable to remove insets controller on display " + mDisplayId);
+            }
+        }
+
+        private void insetsChanged(InsetsState insetsState) {
+            CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
+            if (listeners == null) {
+                return;
+            }
+            mDisplayController.updateDisplayInsets(mDisplayId, insetsState);
+            for (OnInsetsChangedListener listener : listeners) {
+                listener.insetsChanged(insetsState);
+            }
+        }
+
+        private void insetsControlChanged(InsetsState insetsState,
+                InsetsSourceControl[] activeControls) {
+            CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
+            if (listeners == null) {
+                return;
+            }
+            for (OnInsetsChangedListener listener : listeners) {
+                listener.insetsControlChanged(insetsState, activeControls);
+            }
+        }
+
+        private void showInsets(int types, boolean fromIme) {
+            CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
+            if (listeners == null) {
+                return;
+            }
+            for (OnInsetsChangedListener listener : listeners) {
+                listener.showInsets(types, fromIme);
+            }
+        }
+
+        private void hideInsets(int types, boolean fromIme) {
+            CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
+            if (listeners == null) {
+                return;
+            }
+            for (OnInsetsChangedListener listener : listeners) {
+                listener.hideInsets(types, fromIme);
+            }
+        }
+
+        private void topFocusedWindowChanged(String packageName) {
+            CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
+            if (listeners == null) {
+                return;
+            }
+            for (OnInsetsChangedListener listener : listeners) {
+                listener.topFocusedWindowChanged(packageName);
+            }
+        }
+
+        @BinderThread
+        private class DisplayWindowInsetsControllerImpl
+                extends IDisplayWindowInsetsController.Stub {
+            @Override
+            public void topFocusedWindowChanged(String packageName) throws RemoteException {
+                mMainExecutor.execute(() -> {
+                    PerDisplay.this.topFocusedWindowChanged(packageName);
+                });
+            }
+
+            @Override
+            public void insetsChanged(InsetsState insetsState) throws RemoteException {
+                mMainExecutor.execute(() -> {
+                    PerDisplay.this.insetsChanged(insetsState);
+                });
+            }
+
+            @Override
+            public void insetsControlChanged(InsetsState insetsState,
+                    InsetsSourceControl[] activeControls) throws RemoteException {
+                mMainExecutor.execute(() -> {
+                    PerDisplay.this.insetsControlChanged(insetsState, activeControls);
+                });
+            }
+
+            @Override
+            public void showInsets(int types, boolean fromIme) throws RemoteException {
+                mMainExecutor.execute(() -> {
+                    PerDisplay.this.showInsets(types, fromIme);
+                });
+            }
+
+            @Override
+            public void hideInsets(int types, boolean fromIme) throws RemoteException {
+                mMainExecutor.execute(() -> {
+                    PerDisplay.this.hideInsets(types, fromIme);
+                });
+            }
+        }
+    }
+
+    /**
+     * Gets notified whenever the insets change.
+     *
+     * @see IDisplayWindowInsetsController
+     */
+    @ShellMainThread
+    public interface OnInsetsChangedListener {
+        /**
+         * Called when top focused window changes to determine whether or not to take over insets
+         * control. Won't be called if config_remoteInsetsControllerControlsSystemBars is false.
+         * @param packageName: Passes the top package name
+         */
+        void topFocusedWindowChanged(String packageName);
+
+        /**
+         * Called when the window insets configuration has changed.
+         */
+        void insetsChanged(InsetsState insetsState);
+
+        /**
+         * Called when this window retrieved control over a specified set of insets sources.
+         */
+        void insetsControlChanged(InsetsState insetsState,
+                InsetsSourceControl[] activeControls);
+
+        /**
+         * Called when a set of insets source window should be shown by policy.
+         *
+         * @param types internal insets types (WindowInsets.Type.InsetsType) to show
+         * @param fromIme true if this request originated from IME (InputMethodService).
+         */
+        void showInsets(int types, boolean fromIme);
+
+        /**
+         * Called when a set of insets source window should be hidden by policy.
+         *
+         * @param types internal insets types (WindowInsets.Type.InsetsType) to hide
+         * @param fromIme true if this request originated from IME (InputMethodService).
+         */
+        void hideInsets(int types, boolean fromIme);
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
index b7235a3..962aca1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
@@ -25,6 +25,7 @@
 import static android.util.RotationUtils.rotateBounds;
 import static android.util.RotationUtils.rotateInsets;
 import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
@@ -44,7 +45,10 @@
 import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 import android.view.Gravity;
+import android.view.InsetsSource;
+import android.view.InsetsState;
 import android.view.Surface;
+import android.view.WindowInsets;
 
 import com.android.internal.R;
 
@@ -82,6 +86,10 @@
     private boolean mHasNavigationBar = false;
     private boolean mHasStatusBar = false;
     private int mNavBarFrameHeight = 0;
+    private boolean mAllowSeamlessRotationDespiteNavBarMoving = false;
+    private boolean mNavigationBarCanMove = false;
+    private boolean mReverseDefaultRotation = false;
+    private InsetsState mInsetsState = new InsetsState();
 
     @Override
     public boolean equals(Object o) {
@@ -98,14 +106,20 @@
                 && Objects.equals(mStableInsets, other.mStableInsets)
                 && mHasNavigationBar == other.mHasNavigationBar
                 && mHasStatusBar == other.mHasStatusBar
-                && mNavBarFrameHeight == other.mNavBarFrameHeight;
+                && mAllowSeamlessRotationDespiteNavBarMoving
+                        == other.mAllowSeamlessRotationDespiteNavBarMoving
+                && mNavigationBarCanMove == other.mNavigationBarCanMove
+                && mReverseDefaultRotation == other.mReverseDefaultRotation
+                && mNavBarFrameHeight == other.mNavBarFrameHeight
+                && Objects.equals(mInsetsState, other.mInsetsState);
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mUiMode, mWidth, mHeight, mCutout, mRotation, mDensityDpi,
                 mNonDecorInsets, mStableInsets, mHasNavigationBar, mHasStatusBar,
-                mNavBarFrameHeight);
+                mNavBarFrameHeight, mAllowSeamlessRotationDespiteNavBarMoving,
+                mNavigationBarCanMove, mReverseDefaultRotation, mInsetsState);
     }
 
     /**
@@ -150,9 +164,13 @@
         mDensityDpi = dl.mDensityDpi;
         mHasNavigationBar = dl.mHasNavigationBar;
         mHasStatusBar = dl.mHasStatusBar;
+        mAllowSeamlessRotationDespiteNavBarMoving = dl.mAllowSeamlessRotationDespiteNavBarMoving;
+        mNavigationBarCanMove = dl.mNavigationBarCanMove;
+        mReverseDefaultRotation = dl.mReverseDefaultRotation;
         mNavBarFrameHeight = dl.mNavBarFrameHeight;
         mNonDecorInsets.set(dl.mNonDecorInsets);
         mStableInsets.set(dl.mStableInsets);
+        mInsetsState.set(dl.mInsetsState, true /* copySources */);
     }
 
     private void init(DisplayInfo info, Resources res, boolean hasNavigationBar,
@@ -165,12 +183,24 @@
         mDensityDpi = info.logicalDensityDpi;
         mHasNavigationBar = hasNavigationBar;
         mHasStatusBar = hasStatusBar;
+        mAllowSeamlessRotationDespiteNavBarMoving = res.getBoolean(
+            R.bool.config_allowSeamlessRotationDespiteNavBarMoving);
+        mNavigationBarCanMove = res.getBoolean(R.bool.config_navBarCanMove);
+        mReverseDefaultRotation = res.getBoolean(R.bool.config_reverseDefaultRotation);
+        recalcInsets(res);
+    }
+
+    /**
+     * Updates the current insets.
+     */
+    public void setInsets(Resources res, InsetsState state) {
+        mInsetsState = state;
         recalcInsets(res);
     }
 
     private void recalcInsets(Resources res) {
-        computeNonDecorInsets(res, mRotation, mWidth, mHeight, mCutout, mUiMode, mNonDecorInsets,
-                mHasNavigationBar);
+        computeNonDecorInsets(res, mRotation, mWidth, mHeight, mCutout, mInsetsState, mUiMode,
+                mNonDecorInsets, mHasNavigationBar);
         mStableInsets.set(mNonDecorInsets);
         if (mHasStatusBar) {
             convertNonDecorInsetsToStableInsets(res, mStableInsets, mWidth, mHeight, mHasStatusBar);
@@ -244,11 +274,33 @@
         return mWidth > mHeight;
     }
 
-    /** Get the navbar frame height (used by ime). */
+    /** Get the navbar frame (or window) height (used by ime). */
     public int navBarFrameHeight() {
         return mNavBarFrameHeight;
     }
 
+    /** @return whether we can seamlessly rotate even if nav-bar can change sides. */
+    public boolean allowSeamlessRotationDespiteNavBarMoving() {
+        return mAllowSeamlessRotationDespiteNavBarMoving;
+    }
+
+    /** @return whether the navigation bar will change sides during rotation. */
+    public boolean navigationBarCanMove() {
+        return mNavigationBarCanMove;
+    }
+
+    /** @return the rotation that would make the physical display "upside down". */
+    public int getUpsideDownRotation() {
+        boolean displayHardwareIsLandscape = mWidth > mHeight;
+        if ((mRotation % 2) != 0) {
+            displayHardwareIsLandscape = !displayHardwareIsLandscape;
+        }
+        if (displayHardwareIsLandscape) {
+            return mReverseDefaultRotation ? Surface.ROTATION_270 : Surface.ROTATION_90;
+        }
+        return Surface.ROTATION_180;
+    }
+
     /** Gets the orientation of this layout */
     public int getOrientation() {
         return (mWidth > mHeight) ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
@@ -291,21 +343,29 @@
      * @param outInsets the insets to return
      */
     static void computeNonDecorInsets(Resources res, int displayRotation, int displayWidth,
-            int displayHeight, DisplayCutout displayCutout, int uiMode, Rect outInsets,
-            boolean hasNavigationBar) {
+            int displayHeight, DisplayCutout displayCutout, InsetsState insetsState, int uiMode,
+            Rect outInsets, boolean hasNavigationBar) {
         outInsets.setEmpty();
 
         // Only navigation bar
         if (hasNavigationBar) {
+            final InsetsSource extraNavBar = insetsState.getSource(ITYPE_EXTRA_NAVIGATION_BAR);
+            final boolean hasExtraNav = extraNavBar != null && extraNavBar.isVisible();
             int position = navigationBarPosition(res, displayWidth, displayHeight, displayRotation);
             int navBarSize =
                     getNavigationBarSize(res, position, displayWidth > displayHeight, uiMode);
             if (position == NAV_BAR_BOTTOM) {
-                outInsets.bottom = navBarSize;
+                outInsets.bottom = hasExtraNav
+                        ? Math.max(navBarSize, extraNavBar.getFrame().height())
+                        : navBarSize;
             } else if (position == NAV_BAR_RIGHT) {
-                outInsets.right = navBarSize;
+                outInsets.right = hasExtraNav
+                        ? Math.max(navBarSize, extraNavBar.getFrame().width())
+                        : navBarSize;
             } else if (position == NAV_BAR_LEFT) {
-                outInsets.left = navBarSize;
+                outInsets.left = hasExtraNav
+                        ? Math.max(navBarSize, extraNavBar.getFrame().width())
+                        : navBarSize;
             }
         }
 
@@ -327,13 +387,13 @@
      * @param outInsets the insets to return
      */
     static void computeStableInsets(Resources res, int displayRotation, int displayWidth,
-            int displayHeight, DisplayCutout displayCutout, int uiMode, Rect outInsets,
-            boolean hasNavigationBar, boolean hasStatusBar) {
+            int displayHeight, DisplayCutout displayCutout, InsetsState insetsState, int uiMode,
+            Rect outInsets, boolean hasNavigationBar, boolean hasStatusBar) {
         outInsets.setEmpty();
 
         // Navigation bar and status bar.
         computeNonDecorInsets(res, displayRotation, displayWidth, displayHeight, displayCutout,
-                uiMode, outInsets, hasNavigationBar);
+                insetsState, uiMode, outInsets, hasNavigationBar);
         convertNonDecorInsetsToStableInsets(res, outInsets, displayWidth, displayHeight,
                 hasStatusBar);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java
index 218bf47..c76937d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java
@@ -70,7 +70,8 @@
     private final Paint mPaint = new Paint();
     private final int mWidth;
     private final int mHeight;
-    private final int mCircleDiameter;
+    private final int mTouchingWidth;
+    private final int mTouchingHeight;
     private int mCurrentWidth;
     private int mCurrentHeight;
     private AnimatorSet mAnimator;
@@ -80,11 +81,12 @@
         super(context, attrs);
         mPaint.setColor(getResources().getColor(R.color.docked_divider_handle, null));
         mPaint.setAntiAlias(true);
-        mWidth = getResources().getDimensionPixelSize(R.dimen.docked_divider_handle_width);
-        mHeight = getResources().getDimensionPixelSize(R.dimen.docked_divider_handle_height);
+        mWidth = getResources().getDimensionPixelSize(R.dimen.split_divider_handle_width);
+        mHeight = getResources().getDimensionPixelSize(R.dimen.split_divider_handle_height);
         mCurrentWidth = mWidth;
         mCurrentHeight = mHeight;
-        mCircleDiameter = (mWidth + mHeight) / 3;
+        mTouchingWidth = mWidth > mHeight ? mWidth / 2 : mWidth;
+        mTouchingHeight = mHeight > mWidth ? mHeight / 2 : mHeight;
     }
 
     /** Sets touching state for this handle view. */
@@ -98,16 +100,16 @@
         }
         if (!animate) {
             if (touching) {
-                mCurrentWidth = mCircleDiameter;
-                mCurrentHeight = mCircleDiameter;
+                mCurrentWidth = mTouchingWidth;
+                mCurrentHeight = mTouchingHeight;
             } else {
                 mCurrentWidth = mWidth;
                 mCurrentHeight = mHeight;
             }
             invalidate();
         } else {
-            animateToTarget(touching ? mCircleDiameter : mWidth,
-                    touching ? mCircleDiameter : mHeight, touching);
+            animateToTarget(touching ? mTouchingWidth : mWidth,
+                    touching ? mTouchingHeight : mHeight, touching);
         }
         mTouching = touching;
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 8adfac0..3b9bcd3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -16,11 +16,19 @@
 
 package com.android.wm.shell.common.split;
 
+import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
+import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED;
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_INVALID;
 import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
 import static android.view.WindowManager.DOCKED_TOP;
+import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER;
 
 import static com.android.internal.policy.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END;
 import static com.android.internal.policy.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START;
+import static com.android.wm.shell.animation.Interpolators.DIM_INTERPOLATOR;
+import static com.android.wm.shell.animation.Interpolators.SLOWDOWN_INTERPOLATOR;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -30,6 +38,7 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.view.SurfaceControl;
 import android.view.WindowInsets;
@@ -39,6 +48,7 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.animation.Interpolators;
@@ -78,14 +88,17 @@
     private final int mDividerInsets;
     private final int mDividerSize;
 
+    private final Rect mTempRect = new Rect();
     private final Rect mRootBounds = new Rect();
     private final Rect mDividerBounds = new Rect();
     private final Rect mBounds1 = new Rect();
     private final Rect mBounds2 = new Rect();
+    private final Rect mTmpBounds = new Rect();
     private final SplitLayoutHandler mSplitLayoutHandler;
     private final SplitWindowManager mSplitWindowManager;
     private final DisplayImeController mDisplayImeController;
     private final ImePositionProcessor mImePositionProcessor;
+    private final DismissingParallaxPolicy mDismissingParallaxPolicy;
     private final ShellTaskOrganizer mTaskOrganizer;
 
     private Context mContext;
@@ -108,6 +121,7 @@
                 windowName, mContext, configuration, parentContainerCallbacks);
         mTaskOrganizer = taskOrganizer;
         mImePositionProcessor = new ImePositionProcessor(mContext.getDisplayId());
+        mDismissingParallaxPolicy = new DismissingParallaxPolicy();
 
         final Resources resources = context.getResources();
         mDividerWindowWidth = resources.getDimensionPixelSize(
@@ -146,6 +160,15 @@
         return mDividePosition;
     }
 
+    /**
+     * Returns the divider position as a fraction from 0 to 1.
+     */
+    public float getDividerPositionAsFraction() {
+        return Math.min(1f, Math.max(0f, isLandscape()
+                ? (float) ((mBounds1.right + mBounds2.left) / 2f) / mBounds2.right
+                : (float) ((mBounds1.bottom + mBounds2.top) / 2f) / mBounds2.bottom));
+    }
+
     /** Applies new configuration, returns {@code false} if there's no effect to the layout. */
     public boolean updateConfiguration(Configuration configuration) {
         boolean affectsLayout = false;
@@ -166,9 +189,10 @@
         final int rotation = configuration.windowConfiguration.getRotation();
         final Rect rootBounds = configuration.windowConfiguration.getBounds();
         if (rotation != mRotation || !mRootBounds.equals(rootBounds)) {
+            mTmpBounds.set(mRootBounds);
             mRootBounds.set(rootBounds);
             mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds);
-            resetDividerPosition();
+            initDividerPosition(mTmpBounds);
             affectsLayout = true;
         }
 
@@ -180,12 +204,26 @@
         return affectsLayout;
     }
 
+    private void initDividerPosition(Rect oldBounds) {
+        final float snapRatio = (float) mDividePosition
+                / (float) (isLandscape(oldBounds) ? oldBounds.width() : oldBounds.height());
+        // Estimate position by previous ratio.
+        final float length =
+                (float) (isLandscape() ? mRootBounds.width() : mRootBounds.height());
+        final int estimatePosition = (int) (length * snapRatio);
+        // Init divider position by estimated position using current bounds snap algorithm.
+        mDividePosition = mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(
+                estimatePosition).position;
+        updateBounds(mDividePosition);
+    }
+
     /** Updates recording bounds of divider window and both of the splits. */
     private void updateBounds(int position) {
         mDividerBounds.set(mRootBounds);
         mBounds1.set(mRootBounds);
         mBounds2.set(mRootBounds);
-        if (isLandscape(mRootBounds)) {
+        final boolean isLandscape = isLandscape(mRootBounds);
+        if (isLandscape) {
             position += mRootBounds.left;
             mDividerBounds.left = position - mDividerInsets;
             mDividerBounds.right = mDividerBounds.left + mDividerWindowWidth;
@@ -198,6 +236,7 @@
             mBounds1.bottom = position;
             mBounds2.top = mBounds1.bottom + mDividerSize;
         }
+        mDismissingParallaxPolicy.applyDividerPosition(position, isLandscape);
     }
 
     /** Inflates {@link DividerView} on the root surface. */
@@ -224,19 +263,20 @@
     void updateDivideBounds(int position) {
         updateBounds(position);
         mSplitWindowManager.setResizingSplits(true);
-        mSplitLayoutHandler.onBoundsChanging(this);
+        mSplitLayoutHandler.onLayoutChanging(this);
     }
 
     void setDividePosition(int position) {
         mDividePosition = position;
         updateBounds(mDividePosition);
-        mSplitLayoutHandler.onBoundsChanged(this);
+        mSplitLayoutHandler.onLayoutChanged(this);
         mSplitWindowManager.setResizingSplits(false);
     }
 
     /** Resets divider position. */
     public void resetDividerPosition() {
         mDividePosition = mDividerSnapAlgorithm.getMiddleTarget().position;
+        mSplitWindowManager.setResizingSplits(false);
         updateBounds(mDividePosition);
     }
 
@@ -247,15 +287,15 @@
     public void snapToTarget(int currentPosition, DividerSnapAlgorithm.SnapTarget snapTarget) {
         switch (snapTarget.flag) {
             case FLAG_DISMISS_START:
-                mSplitLayoutHandler.onSnappedToDismiss(false /* bottomOrRight */);
-                mSplitWindowManager.setResizingSplits(false);
+                flingDividePosition(currentPosition, snapTarget.position,
+                        () -> mSplitLayoutHandler.onSnappedToDismiss(false /* bottomOrRight */));
                 break;
             case FLAG_DISMISS_END:
-                mSplitLayoutHandler.onSnappedToDismiss(true /* bottomOrRight */);
-                mSplitWindowManager.setResizingSplits(false);
+                flingDividePosition(currentPosition, snapTarget.position,
+                        () -> mSplitLayoutHandler.onSnappedToDismiss(true /* bottomOrRight */));
                 break;
             default:
-                flingDividePosition(currentPosition, snapTarget.position);
+                flingDividePosition(currentPosition, snapTarget.position, null);
                 break;
         }
     }
@@ -285,7 +325,8 @@
                 isLandscape ? DOCKED_LEFT : DOCKED_TOP /* dockSide */);
     }
 
-    private void flingDividePosition(int from, int to) {
+    @VisibleForTesting
+    void flingDividePosition(int from, int to, @Nullable Runnable flingFinishedCallback) {
         if (from == to) {
             // No animation run, it should stop resizing here.
             mSplitWindowManager.setResizingSplits(false);
@@ -301,6 +342,9 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 setDividePosition(to);
+                if (flingFinishedCallback != null) {
+                    flingFinishedCallback.run();
+                }
             }
 
             @Override
@@ -323,33 +367,83 @@
         return bounds.width() > bounds.height();
     }
 
+    /**
+     * Return if this layout is landscape.
+     */
+    public boolean isLandscape() {
+        return isLandscape(mRootBounds);
+    }
+
     /** Apply recorded surface layout to the {@link SurfaceControl.Transaction}. */
     public void applySurfaceChanges(SurfaceControl.Transaction t, SurfaceControl leash1,
             SurfaceControl leash2, SurfaceControl dimLayer1, SurfaceControl dimLayer2) {
-        final Rect dividerBounds = mImePositionProcessor.adjustForIme(mDividerBounds);
-        final Rect bounds1 = mImePositionProcessor.adjustForIme(mBounds1);
-        final Rect bounds2 = mImePositionProcessor.adjustForIme(mBounds2);
         final SurfaceControl dividerLeash = getDividerLeash();
         if (dividerLeash != null) {
-            t.setPosition(dividerLeash, dividerBounds.left, dividerBounds.top)
-                    // Resets layer of divider bar to make sure it is always on top.
-                    .setLayer(dividerLeash, Integer.MAX_VALUE);
+            t.setPosition(dividerLeash, mDividerBounds.left, mDividerBounds.top);
+            // Resets layer of divider bar to make sure it is always on top.
+            t.setLayer(dividerLeash, SPLIT_DIVIDER_LAYER);
+        }
+        t.setPosition(leash1, mBounds1.left, mBounds1.top)
+                .setWindowCrop(leash1, mBounds1.width(), mBounds1.height());
+        t.setPosition(leash2, mBounds2.left, mBounds2.top)
+                .setWindowCrop(leash2, mBounds2.width(), mBounds2.height());
+
+        if (mImePositionProcessor.adjustSurfaceLayoutForIme(
+                t, dividerLeash, leash1, leash2, dimLayer1, dimLayer2)) {
+            return;
         }
 
-        t.setPosition(leash1, bounds1.left, bounds1.top)
-                .setWindowCrop(leash1, bounds1.width(), bounds1.height());
-
-        t.setPosition(leash2, bounds2.left, bounds2.top)
-                .setWindowCrop(leash2, bounds2.width(), bounds2.height());
-
-        mImePositionProcessor.applySurfaceDimValues(t, dimLayer1, dimLayer2);
+        mDismissingParallaxPolicy.adjustDismissingSurface(t, leash1, leash2, dimLayer1, dimLayer2);
     }
 
     /** Apply recorded task layout to the {@link WindowContainerTransaction}. */
     public void applyTaskChanges(WindowContainerTransaction wct,
             ActivityManager.RunningTaskInfo task1, ActivityManager.RunningTaskInfo task2) {
-        wct.setBounds(task1.token, mImePositionProcessor.adjustForIme(mBounds1))
-                .setBounds(task2.token, mImePositionProcessor.adjustForIme(mBounds2));
+        if (mImePositionProcessor.applyTaskLayoutForIme(wct, task1.token, task2.token)) {
+            return;
+        }
+
+        wct.setBounds(task1.token, mBounds1)
+                .setBounds(task2.token, mBounds2);
+    }
+
+    /**
+     * Shift configuration bounds to prevent client apps get configuration changed or relaunch. And
+     * restore shifted configuration bounds if it's no longer shifted.
+     */
+    public void applyLayoutShifted(WindowContainerTransaction wct, int offsetX, int offsetY,
+            ActivityManager.RunningTaskInfo taskInfo1, ActivityManager.RunningTaskInfo taskInfo2) {
+        if (offsetX == 0 && offsetY == 0) {
+            wct.setBounds(taskInfo1.token, mBounds1);
+            wct.setAppBounds(taskInfo1.token, null);
+            wct.setScreenSizeDp(taskInfo1.token,
+                    SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED);
+
+            wct.setBounds(taskInfo2.token, mBounds2);
+            wct.setAppBounds(taskInfo2.token, null);
+            wct.setScreenSizeDp(taskInfo2.token,
+                    SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED);
+        } else {
+            mTempRect.set(taskInfo1.configuration.windowConfiguration.getBounds());
+            mTempRect.offset(offsetX, offsetY);
+            wct.setBounds(taskInfo1.token, mTempRect);
+            mTempRect.set(taskInfo1.configuration.windowConfiguration.getAppBounds());
+            mTempRect.offset(offsetX, offsetY);
+            wct.setAppBounds(taskInfo1.token, mTempRect);
+            wct.setScreenSizeDp(taskInfo1.token,
+                    taskInfo1.configuration.screenWidthDp,
+                    taskInfo1.configuration.screenHeightDp);
+
+            mTempRect.set(taskInfo2.configuration.windowConfiguration.getBounds());
+            mTempRect.offset(offsetX, offsetY);
+            wct.setBounds(taskInfo2.token, mTempRect);
+            mTempRect.set(taskInfo2.configuration.windowConfiguration.getAppBounds());
+            mTempRect.offset(offsetX, offsetY);
+            wct.setAppBounds(taskInfo2.token, mTempRect);
+            wct.setScreenSizeDp(taskInfo2.token,
+                    taskInfo2.configuration.screenWidthDp,
+                    taskInfo2.configuration.screenHeightDp);
+        }
     }
 
     /** Handles layout change event. */
@@ -359,10 +453,18 @@
         void onSnappedToDismiss(boolean snappedToEnd);
 
         /** Calls when the bounds is changing due to animation or dragging divider bar. */
-        void onBoundsChanging(SplitLayout layout);
+        void onLayoutChanging(SplitLayout layout);
 
         /** Calls when the target bounds changed. */
-        void onBoundsChanged(SplitLayout layout);
+        void onLayoutChanged(SplitLayout layout);
+
+        /**
+         * Notifies when the layout shifted. So the layout handler can shift configuration
+         * bounds correspondingly to make sure client apps won't get configuration changed or
+         * relaunch. If the layout is no longer shifted, layout handler should restore shifted
+         * configuration bounds.
+         */
+        void onLayoutShifted(int offsetX, int offsetY, SplitLayout layout);
 
         /** Calls when user double tapped on the divider bar. */
         default void onDoubleTappedDivider() {
@@ -373,6 +475,106 @@
         int getSplitItemPosition(WindowContainerToken token);
     }
 
+    /**
+     * Calculates and applies proper dismissing parallax offset and dimming value to hint users
+     * dismissing gesture.
+     */
+    private class DismissingParallaxPolicy {
+        // The current dismissing side.
+        int mDismissingSide = DOCKED_INVALID;
+
+        // The parallax offset to hint the dismissing side and progress.
+        final Point mDismissingParallaxOffset = new Point();
+
+        // The dimming value to hint the dismissing side and progress.
+        float mDismissingDimValue = 0.0f;
+
+        /**
+         * Applies a parallax to the task to hint dismissing progress.
+         *
+         * @param position the split position to apply dismissing parallax effect
+         * @param isLandscape indicates whether it's splitting horizontally or vertically
+         */
+        void applyDividerPosition(int position, boolean isLandscape) {
+            mDismissingSide = DOCKED_INVALID;
+            mDismissingParallaxOffset.set(0, 0);
+            mDismissingDimValue = 0;
+
+            int totalDismissingDistance = 0;
+            if (position <= mDividerSnapAlgorithm.getFirstSplitTarget().position) {
+                mDismissingSide = isLandscape ? DOCKED_LEFT : DOCKED_TOP;
+                totalDismissingDistance = mDividerSnapAlgorithm.getDismissStartTarget().position
+                                - mDividerSnapAlgorithm.getFirstSplitTarget().position;
+            } else if (position >= mDividerSnapAlgorithm.getLastSplitTarget().position) {
+                mDismissingSide = isLandscape ? DOCKED_RIGHT : DOCKED_BOTTOM;
+                totalDismissingDistance = mDividerSnapAlgorithm.getLastSplitTarget().position
+                                - mDividerSnapAlgorithm.getDismissEndTarget().position;
+            }
+
+            if (mDismissingSide != DOCKED_INVALID) {
+                float fraction = Math.max(0,
+                        Math.min(mDividerSnapAlgorithm.calculateDismissingFraction(position), 1f));
+                mDismissingDimValue = DIM_INTERPOLATOR.getInterpolation(fraction);
+                fraction = calculateParallaxDismissingFraction(fraction, mDismissingSide);
+                if (isLandscape) {
+                    mDismissingParallaxOffset.x = (int) (fraction * totalDismissingDistance);
+                } else {
+                    mDismissingParallaxOffset.y = (int) (fraction * totalDismissingDistance);
+                }
+            }
+        }
+
+        /**
+         * @return for a specified {@code fraction}, this returns an adjusted value that simulates a
+         * slowing down parallax effect
+         */
+        private float calculateParallaxDismissingFraction(float fraction, int dockSide) {
+            float result = SLOWDOWN_INTERPOLATOR.getInterpolation(fraction) / 3.5f;
+
+            // Less parallax at the top, just because.
+            if (dockSide == WindowManager.DOCKED_TOP) {
+                result /= 2f;
+            }
+            return result;
+        }
+
+        /** Applies parallax offset and dimming value to the root surface at the dismissing side. */
+        boolean adjustDismissingSurface(SurfaceControl.Transaction t,
+                SurfaceControl leash1, SurfaceControl leash2,
+                SurfaceControl dimLayer1, SurfaceControl dimLayer2) {
+            SurfaceControl targetLeash, targetDimLayer;
+            switch (mDismissingSide) {
+                case DOCKED_TOP:
+                case DOCKED_LEFT:
+                    targetLeash = leash1;
+                    targetDimLayer = dimLayer1;
+                    mTempRect.set(mBounds1);
+                    break;
+                case DOCKED_BOTTOM:
+                case DOCKED_RIGHT:
+                    targetLeash = leash2;
+                    targetDimLayer = dimLayer2;
+                    mTempRect.set(mBounds2);
+                    break;
+                case DOCKED_INVALID:
+                default:
+                    t.setAlpha(dimLayer1, 0).hide(dimLayer1);
+                    t.setAlpha(dimLayer2, 0).hide(dimLayer2);
+                    return false;
+            }
+
+            t.setPosition(targetLeash,
+                    mTempRect.left + mDismissingParallaxOffset.x,
+                    mTempRect.top + mDismissingParallaxOffset.y);
+            // Transform the screen-based split bounds to surface-based crop bounds.
+            mTempRect.offsetTo(-mDismissingParallaxOffset.x, -mDismissingParallaxOffset.y);
+            t.setWindowCrop(targetLeash, mTempRect);
+            t.setAlpha(targetDimLayer, mDismissingDimValue)
+                    .setVisibility(targetDimLayer, mDismissingDimValue > 0.001f);
+            return true;
+        }
+    }
+
     /** Records IME top offset changes and updates SplitLayout correspondingly. */
     private class ImePositionProcessor implements DisplayImeController.ImePositionProcessor {
         /**
@@ -427,6 +629,18 @@
                     && !isFloating && !isLandscape(mRootBounds) && showing;
             mTargetYOffset = needOffset ? getTargetYOffset() : 0;
 
+            if (mTargetYOffset != mLastYOffset) {
+                // Freeze the configuration size with offset to prevent app get a configuration
+                // changed or relaunch. This is required to make sure client apps will calculate
+                // insets properly after layout shifted.
+                if (mTargetYOffset == 0) {
+                    mSplitLayoutHandler.onLayoutShifted(0, 0, SplitLayout.this);
+                } else {
+                    mSplitLayoutHandler.onLayoutShifted(0, mTargetYOffset - mLastYOffset,
+                            SplitLayout.this);
+                }
+            }
+
             // Make {@link DividerView} non-interactive while IME showing in split mode. Listen to
             // ImePositionProcessor#onImeVisibilityChanged directly in DividerView is not enough
             // because DividerView won't receive onImeVisibilityChanged callback after it being
@@ -441,7 +655,7 @@
         public void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t) {
             if (displayId != mDisplayId) return;
             onProgress(getProgress(imeTop));
-            mSplitLayoutHandler.onBoundsChanging(SplitLayout.this);
+            mSplitLayoutHandler.onLayoutChanging(SplitLayout.this);
         }
 
         @Override
@@ -449,7 +663,7 @@
                 SurfaceControl.Transaction t) {
             if (displayId != mDisplayId || cancel) return;
             onProgress(1.0f);
-            mSplitLayoutHandler.onBoundsChanging(SplitLayout.this);
+            mSplitLayoutHandler.onLayoutChanging(SplitLayout.this);
         }
 
         @Override
@@ -459,7 +673,7 @@
             if (!controlling && mImeShown) {
                 reset();
                 mSplitWindowManager.setInteractive(true);
-                mSplitLayoutHandler.onBoundsChanging(SplitLayout.this);
+                mSplitLayoutHandler.onLayoutChanging(SplitLayout.this);
             }
         }
 
@@ -491,24 +705,61 @@
             return start + (end - start) * progress;
         }
 
-        private void reset() {
+        void reset() {
             mImeShown = false;
             mYOffsetForIme = mLastYOffset = mTargetYOffset = 0;
             mDimValue1 = mLastDim1 = mTargetDim1 = 0.0f;
             mDimValue2 = mLastDim2 = mTargetDim2 = 0.0f;
         }
 
-        /* Adjust bounds with IME offset. */
-        private Rect adjustForIme(Rect bounds) {
-            final Rect temp = new Rect(bounds);
-            if (mYOffsetForIme != 0) temp.offset(0, mYOffsetForIme);
-            return temp;
+        /**
+         * Applies adjusted task layout for showing IME.
+         *
+         * @return {@code false} if there's no need to adjust, otherwise {@code true}
+         */
+        boolean applyTaskLayoutForIme(WindowContainerTransaction wct,
+                WindowContainerToken token1, WindowContainerToken token2) {
+            if (mYOffsetForIme == 0) return false;
+
+            mTempRect.set(mBounds1);
+            mTempRect.offset(0, mYOffsetForIme);
+            wct.setBounds(token1, mTempRect);
+
+            mTempRect.set(mBounds2);
+            mTempRect.offset(0, mYOffsetForIme);
+            wct.setBounds(token2, mTempRect);
+
+            return true;
         }
 
-        private void applySurfaceDimValues(SurfaceControl.Transaction t, SurfaceControl dimLayer1,
-                SurfaceControl dimLayer2) {
+        /**
+         * Adjusts surface layout while showing IME.
+         *
+         * @return {@code false} if there's no need to adjust, otherwise {@code true}
+         */
+        boolean adjustSurfaceLayoutForIme(SurfaceControl.Transaction t,
+                SurfaceControl dividerLeash, SurfaceControl leash1, SurfaceControl leash2,
+                SurfaceControl dimLayer1, SurfaceControl dimLayer2) {
+            if (mYOffsetForIme == 0) return false;
+
+            if (dividerLeash != null) {
+                mTempRect.set(mDividerBounds);
+                mTempRect.offset(0, mYOffsetForIme);
+                t.setPosition(dividerLeash, mTempRect.left, mTempRect.top);
+            }
+
+            mTempRect.set(mBounds1);
+            mTempRect.offset(0, mYOffsetForIme);
+            t.setPosition(leash1, mTempRect.left, mTempRect.top);
+
+            mTempRect.set(mBounds2);
+            mTempRect.offset(0, mYOffsetForIme);
+            t.setPosition(leash2, mTempRect.left, mTempRect.top);
+
             t.setAlpha(dimLayer1, mDimValue1).setVisibility(dimLayer1, mDimValue1 > 0.001f);
             t.setAlpha(dimLayer2, mDimValue2).setVisibility(dimLayer2, mDimValue2 > 0.001f);
+
+            return true;
         }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index 58bf22a..0c12d6c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -34,6 +34,7 @@
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 
+import android.content.ClipData;
 import android.content.ClipDescription;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -48,6 +49,8 @@
 import android.view.WindowManager;
 import android.widget.FrameLayout;
 
+import com.android.internal.logging.InstanceId;
+import com.android.internal.logging.UiEventLogger;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.DisplayController;
@@ -67,14 +70,17 @@
 
     private final Context mContext;
     private final DisplayController mDisplayController;
+    private final DragAndDropEventLogger mLogger;
     private SplitScreenController mSplitScreen;
 
     private final SparseArray<PerDisplay> mDisplayDropTargets = new SparseArray<>();
     private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
 
-    public DragAndDropController(Context context, DisplayController displayController) {
+    public DragAndDropController(Context context, DisplayController displayController,
+            UiEventLogger uiEventLogger) {
         mContext = context;
         mDisplayController = displayController;
+        mLogger = new DragAndDropEventLogger(uiEventLogger);
     }
 
     public void initialize(Optional<SplitScreenController> splitscreen) {
@@ -175,9 +181,10 @@
                     Slog.w(TAG, "Unexpected drag start during an active drag");
                     return false;
                 }
+                InstanceId loggerSessionId = mLogger.logStart(event);
                 pd.activeDragCount++;
                 pd.dragLayout.prepare(mDisplayController.getDisplayLayout(displayId),
-                        event.getClipData());
+                        event.getClipData(), loggerSessionId);
                 setDropTargetWindowVisibility(pd, View.VISIBLE);
                 break;
             case ACTION_DRAG_ENTERED:
@@ -198,7 +205,9 @@
             case ACTION_DRAG_ENDED:
                 // TODO(b/169894807): Ensure sure it's not possible to get ENDED without DROP
                 // or EXITED
-                if (!pd.dragLayout.hasDropped()) {
+                if (pd.dragLayout.hasDropped()) {
+                    mLogger.logDrop();
+                } else {
                     pd.activeDragCount--;
                     pd.dragLayout.hide(event, () -> {
                         if (pd.activeDragCount == 0) {
@@ -208,6 +217,7 @@
                         }
                     });
                 }
+                mLogger.logEnd();
                 break;
         }
         return true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropEventLogger.java
new file mode 100644
index 0000000..6e4b815
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropEventLogger.java
@@ -0,0 +1,132 @@
+/*
+ * 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.wm.shell.draganddrop;
+
+import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
+import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
+import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
+
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.pm.ActivityInfo;
+import android.view.DragEvent;
+
+import com.android.internal.logging.InstanceId;
+import com.android.internal.logging.InstanceIdSequence;
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
+
+/**
+ * Helper class that to log Drag & Drop UIEvents for a single session, see also go/uievent
+ */
+public class DragAndDropEventLogger {
+
+    private final UiEventLogger mUiEventLogger;
+    // Used to generate instance ids for this drag if one is not provided
+    private final InstanceIdSequence mIdSequence;
+
+    // Tracks the current drag session
+    private ActivityInfo mActivityInfo;
+    private InstanceId mInstanceId;
+
+    public DragAndDropEventLogger(UiEventLogger uiEventLogger) {
+        mUiEventLogger = uiEventLogger;
+        mIdSequence = new InstanceIdSequence(Integer.MAX_VALUE);
+    }
+
+    /**
+     * Logs the start of a drag.
+     */
+    public InstanceId logStart(DragEvent event) {
+        final ClipDescription description = event.getClipDescription();
+        final ClipData data = event.getClipData();
+        final ClipData.Item item = data.getItemAt(0);
+        mInstanceId = item.getIntent().getParcelableExtra(
+                ClipDescription.EXTRA_LOGGING_INSTANCE_ID);
+        if (mInstanceId == null) {
+            mInstanceId = mIdSequence.newInstanceId();
+        }
+        mActivityInfo = item.getActivityInfo();
+        mUiEventLogger.logWithInstanceId(getStartEnum(description),
+                mActivityInfo.applicationInfo.uid,
+                mActivityInfo.applicationInfo.packageName, mInstanceId);
+        return mInstanceId;
+    }
+
+    /**
+     * Logs a successful drop.
+     */
+    public void logDrop() {
+        mUiEventLogger.logWithInstanceId(DragAndDropUiEventEnum.GLOBAL_APP_DRAG_DROPPED,
+                mActivityInfo.applicationInfo.uid,
+                mActivityInfo.applicationInfo.packageName, mInstanceId);
+    }
+
+    /**
+     * Logs the end of a drag.
+     */
+    public void logEnd() {
+        mUiEventLogger.logWithInstanceId(DragAndDropUiEventEnum.GLOBAL_APP_DRAG_END,
+                mActivityInfo.applicationInfo.uid,
+                mActivityInfo.applicationInfo.packageName, mInstanceId);
+    }
+
+    /**
+     * Returns the start logging enum for the given drag description.
+     */
+    private DragAndDropUiEventEnum getStartEnum(ClipDescription description) {
+        if (description.hasMimeType(MIMETYPE_APPLICATION_ACTIVITY)) {
+            return DragAndDropUiEventEnum.GLOBAL_APP_DRAG_START_ACTIVITY;
+        } else if (description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT)) {
+            return DragAndDropUiEventEnum.GLOBAL_APP_DRAG_START_SHORTCUT;
+        } else if (description.hasMimeType(MIMETYPE_APPLICATION_TASK)) {
+            return DragAndDropUiEventEnum.GLOBAL_APP_DRAG_START_TASK;
+        }
+        throw new IllegalArgumentException("Not an app drag");
+    }
+
+    /**
+     * Enums for logging Drag & Drop UiEvents
+     */
+    public enum DragAndDropUiEventEnum implements UiEventLogger.UiEventEnum {
+        @UiEvent(doc = "Starting a global drag and drop of an activity")
+        GLOBAL_APP_DRAG_START_ACTIVITY(884),
+
+        @UiEvent(doc = "Starting a global drag and drop of a shortcut")
+        GLOBAL_APP_DRAG_START_SHORTCUT(885),
+
+        @UiEvent(doc = "Starting a global drag and drop of a task")
+        GLOBAL_APP_DRAG_START_TASK(888),
+
+        @UiEvent(doc = "A global app drag was successfully dropped")
+        GLOBAL_APP_DRAG_DROPPED(887),
+
+        @UiEvent(doc = "Ending a global app drag and drop")
+        GLOBAL_APP_DRAG_END(886);
+
+        private final int mId;
+
+        DragAndDropUiEventEnum(int id) {
+            mId = id;
+        }
+
+        @Override
+        public int getId() {
+            return mId;
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index 5b9d7b80..102b90f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -63,6 +63,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
+import com.android.internal.logging.InstanceId;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
 import com.android.wm.shell.splitscreen.SplitScreen.StageType;
@@ -86,6 +87,7 @@
     private final SplitScreenController mSplitScreen;
     private final ArrayList<DragAndDropPolicy.Target> mTargets = new ArrayList<>();
 
+    private InstanceId mLoggerSessionId;
     private DragSession mSession;
 
     public DragAndDropPolicy(Context context, SplitScreenController splitScreen) {
@@ -104,7 +106,8 @@
     /**
      * Starts a new drag session with the given initial drag data.
      */
-    void start(DisplayLayout displayLayout, ClipData data) {
+    void start(DisplayLayout displayLayout, ClipData data, InstanceId loggerSessionId) {
+        mLoggerSessionId = loggerSessionId;
         mSession = new DragSession(mContext, mActivityTaskManager, displayLayout, data);
         // TODO(b/169894807): Also update the session data with task stack changes
         mSession.update();
@@ -207,6 +210,8 @@
                 // Launch in the side stage if we are not in split-screen already.
                 stage = STAGE_TYPE_SIDE;
             }
+            // Add some data for logging splitscreen once it is invoked
+            mSplitScreen.logOnDroppedToSplit(position, mLoggerSessionId);
         }
 
         final ClipDescription description = data.getDescription();
@@ -294,7 +299,12 @@
                 @StageType int stage, @SplitPosition int position,
                 @Nullable Bundle options);
         void enterSplitScreen(int taskId, boolean leftOrTop);
-        void exitSplitScreen();
+
+        /**
+         * Exits splitscreen, with an associated exit trigger from the SplitscreenUIChanged proto
+         * for logging.
+         */
+        void exitSplitScreen(int exitTrigger);
     }
 
     /**
@@ -347,7 +357,7 @@
         }
 
         @Override
-        public void exitSplitScreen() {
+        public void exitSplitScreen(int exitTrigger) {
             throw new UnsupportedOperationException("exitSplitScreen not implemented by starter");
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
index b342336..efc9ed0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -38,6 +38,7 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.internal.logging.InstanceId;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.DisplayLayout;
@@ -98,8 +99,9 @@
         return mHasDropped;
     }
 
-    public void prepare(DisplayLayout displayLayout, ClipData initialData) {
-        mPolicy.start(displayLayout, initialData);
+    public void prepare(DisplayLayout displayLayout, ClipData initialData,
+            InstanceId loggerSessionId) {
+        mPolicy.start(displayLayout, initialData, loggerSessionId);
         mHasDropped = false;
         mCurrentTarget = null;
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
new file mode 100644
index 0000000..5fb3297
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -0,0 +1,147 @@
+/*
+ * 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.wm.shell.freeform;
+
+import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
+import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.provider.Settings;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.SurfaceControl;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
+
+import java.io.PrintWriter;
+
+/**
+ * {@link ShellTaskOrganizer.TaskListener} for {@link
+ * ShellTaskOrganizer#TASK_LISTENER_TYPE_FREEFORM}.
+ */
+public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener {
+    private static final String TAG = "FreeformTaskListener";
+
+    private final SyncTransactionQueue mSyncQueue;
+
+    private final SparseArray<State> mTasks = new SparseArray<>();
+
+    private static class State {
+        RunningTaskInfo mTaskInfo;
+        SurfaceControl mLeash;
+    }
+
+    public FreeformTaskListener(SyncTransactionQueue syncQueue) {
+        mSyncQueue = syncQueue;
+    }
+
+    @Override
+    public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
+        if (mTasks.get(taskInfo.taskId) != null) {
+            throw new RuntimeException("Task appeared more than once: #" + taskInfo.taskId);
+        }
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Appeared: #%d",
+                taskInfo.taskId);
+        final State state = new State();
+        state.mTaskInfo = taskInfo;
+        state.mLeash = leash;
+        mTasks.put(taskInfo.taskId, state);
+
+        final Rect taskBounds = taskInfo.configuration.windowConfiguration.getBounds();
+        mSyncQueue.runInSync(t -> {
+            Point taskPosition = taskInfo.positionInParent;
+            t.setPosition(leash, taskPosition.x, taskPosition.y)
+                    .setWindowCrop(leash, taskBounds.width(), taskBounds.height())
+                    .show(leash);
+        });
+    }
+
+    @Override
+    public void onTaskVanished(RunningTaskInfo taskInfo) {
+        State state = mTasks.get(taskInfo.taskId);
+        if (state == null) {
+            Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId);
+            return;
+        }
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Vanished: #%d",
+                taskInfo.taskId);
+        mTasks.remove(taskInfo.taskId);
+    }
+
+    @Override
+    public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
+        State state = mTasks.get(taskInfo.taskId);
+        if (state == null) {
+            throw new RuntimeException(
+                    "Task info changed before appearing: #" + taskInfo.taskId);
+        }
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Info Changed: #%d",
+                taskInfo.taskId);
+        state.mTaskInfo = taskInfo;
+
+        final Rect taskBounds = taskInfo.configuration.windowConfiguration.getBounds();
+        final SurfaceControl leash = state.mLeash;
+        mSyncQueue.runInSync(t -> {
+            Point taskPosition = taskInfo.positionInParent;
+            t.setPosition(leash, taskPosition.x, taskPosition.y)
+                    .setWindowCrop(leash, taskBounds.width(), taskBounds.height())
+                    .show(leash);
+        });
+    }
+
+    @Override
+    public void dump(PrintWriter pw, String prefix) {
+        final String innerPrefix = prefix + "  ";
+        pw.println(prefix + this);
+        pw.println(innerPrefix + mTasks.size() + " tasks");
+    }
+
+    @Override
+    public String toString() {
+        return TAG;
+    }
+
+    /**
+     * Checks if freeform support is enabled in system.
+     *
+     * @param context context used to check settings and package manager.
+     * @return {@code true} if freeform is enabled, {@code false} if not.
+     */
+    public static boolean isFreeformEnabled(Context context) {
+        return context.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT)
+                || Settings.Global.getInt(context.getContentResolver(),
+                DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
+    }
+
+    /**
+     * Creates {@link FreeformTaskListener} if freeform is enabled.
+     */
+    public static FreeformTaskListener create(Context context,
+            SyncTransactionQueue syncQueue) {
+        if (!isFreeformEnabled(context)) {
+            return null;
+        }
+
+        return new FreeformTaskListener(syncQueue);
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
index 9bed40d..067f808 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
@@ -20,6 +20,8 @@
 import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
 import static android.view.WindowManager.DOCKED_RIGHT;
 
+import static com.android.wm.shell.animation.Interpolators.DIM_INTERPOLATOR;
+import static com.android.wm.shell.animation.Interpolators.SLOWDOWN_INTERPOLATOR;
 import static com.android.wm.shell.common.split.DividerView.TOUCH_ANIMATION_DURATION;
 import static com.android.wm.shell.common.split.DividerView.TOUCH_RELEASE_ANIMATION_DURATION;
 
@@ -100,10 +102,6 @@
     private static final float MINIMIZE_DOCK_SCALE = 0f;
     private static final float ADJUSTED_FOR_IME_SCALE = 0.5f;
 
-    private static final PathInterpolator SLOWDOWN_INTERPOLATOR =
-            new PathInterpolator(0.5f, 1f, 0.5f, 1f);
-    private static final PathInterpolator DIM_INTERPOLATOR =
-            new PathInterpolator(.23f, .87f, .52f, -0.11f);
     private static final Interpolator IME_ADJUST_INTERPOLATOR =
             new PathInterpolator(0.2f, 0f, 0.1f, 1f);
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationCallback.java
index 24e5111..8969cc8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationCallback.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationCallback.java
@@ -46,7 +46,7 @@
     /**
      * Called when OneHanded animator is updating position
      */
-    default void onAnimationUpdate(float xPos, float yPos) {
+    default void onAnimationUpdate(SurfaceControl.Transaction tx, float xPos, float yPos) {
     }
 
 }
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 bfb2cc6..4b78328 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
@@ -182,10 +182,10 @@
         @Override
         public void onAnimationUpdate(ValueAnimator animation) {
             final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
-            applySurfaceControlTransaction(mLeash, tx, animation.getAnimatedFraction());
             mOneHandedAnimationCallbacks.forEach(
-                    (callback) -> callback.onAnimationUpdate(0f, mCurrentValue)
+                    (callback) -> callback.onAnimationUpdate(tx, 0f, mCurrentValue)
             );
+            applySurfaceControlTransaction(mLeash, tx, animation.getAnimatedFraction());
         }
 
         void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
index 3ccb9e7..9e1c61a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
@@ -16,12 +16,17 @@
 
 package com.android.wm.shell.onehanded;
 
+import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE;
+
+import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Color;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
+import android.view.ContextThemeWrapper;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
+import android.view.animation.LinearInterpolator;
 import android.window.DisplayAreaAppearedInfo;
 import android.window.DisplayAreaInfo;
 import android.window.DisplayAreaOrganizer;
@@ -30,7 +35,6 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.DisplayLayout;
 
@@ -44,194 +48,225 @@
  * the screen has entered one handed mode.
  */
 public class OneHandedBackgroundPanelOrganizer extends DisplayAreaOrganizer
-        implements OneHandedTransitionCallback {
+        implements OneHandedAnimationCallback, OneHandedState.OnStateChangedListener {
     private static final String TAG = "OneHandedBackgroundPanelOrganizer";
     private static final int THEME_COLOR_OFFSET = 10;
+    private static final int ALPHA_ANIMATION_DURATION = 200;
 
     private final Context mContext;
-    private final Object mLock = new Object();
     private final SurfaceSession mSurfaceSession = new SurfaceSession();
-    private final Executor mMainExecutor;
     private final OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
-            mSurfaceControlTransactionFactory;
+            mTransactionFactory;
 
-    private float[] mDefaultColor;
+    private @OneHandedState.State int mCurrentState;
+    private ValueAnimator mAlphaAnimator;
+
+    private float mTranslationFraction;
+    private float[] mThemeColor;
 
     /**
      * The background to distinguish the boundary of translated windows and empty region when
      * one handed mode triggered.
      */
     private Rect mBkgBounds;
+    private Rect mStableInsets;
+
+    @Nullable
     @VisibleForTesting
-    @GuardedBy("mLock")
-    boolean mIsShowing;
+    SurfaceControl mBackgroundSurface;
     @Nullable
-    @GuardedBy("mLock")
-    private SurfaceControl mBackgroundSurface;
-    @Nullable
-    @GuardedBy("mLock")
     private SurfaceControl mParentLeash;
 
-    private final OneHandedAnimationCallback mOneHandedAnimationCallback =
-            new OneHandedAnimationCallback() {
-                @Override
-                public void onOneHandedAnimationStart(
-                        OneHandedAnimationController.OneHandedTransitionAnimator animator) {
-                    mMainExecutor.execute(() -> showBackgroundPanelLayer());
-                }
-            };
-
-    @Override
-    public void onStopFinished(Rect bounds) {
-        mMainExecutor.execute(() -> removeBackgroundPanelLayer());
-    }
-
     public OneHandedBackgroundPanelOrganizer(Context context, DisplayLayout displayLayout,
-            Executor executor) {
+            OneHandedSettingsUtil settingsUtil, Executor executor) {
         super(executor);
         mContext = context;
-        // Ensure the mBkgBounds is portrait, due to OHM only support on portrait
-        if (displayLayout.height() > displayLayout.width()) {
-            mBkgBounds = new Rect(0, 0, displayLayout.width(), displayLayout.height());
-        } else {
-            mBkgBounds = new Rect(0, 0, displayLayout.height(), displayLayout.width());
-        }
+        mTranslationFraction = settingsUtil.getTranslationFraction(context);
+        mTransactionFactory = SurfaceControl.Transaction::new;
         updateThemeColors();
-        mMainExecutor = executor;
-        mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
     }
 
     @Override
     public void onDisplayAreaAppeared(@NonNull DisplayAreaInfo displayAreaInfo,
             @NonNull SurfaceControl leash) {
-        synchronized (mLock) {
-            if (mParentLeash == null) {
-                mParentLeash = leash;
-            } else {
-                throw new RuntimeException("There should be only one DisplayArea for "
-                        + "the one-handed mode background panel");
-            }
-        }
-    }
-
-    OneHandedAnimationCallback getOneHandedAnimationCallback() {
-        return mOneHandedAnimationCallback;
+        mParentLeash = leash;
     }
 
     @Override
     public List<DisplayAreaAppearedInfo> registerOrganizer(int displayAreaFeature) {
-        synchronized (mLock) {
-            final List<DisplayAreaAppearedInfo> displayAreaInfos;
-            displayAreaInfos = super.registerOrganizer(displayAreaFeature);
-            for (int i = 0; i < displayAreaInfos.size(); i++) {
-                final DisplayAreaAppearedInfo info = displayAreaInfos.get(i);
-                onDisplayAreaAppeared(info.getDisplayAreaInfo(), info.getLeash());
-            }
-            return displayAreaInfos;
+        final List<DisplayAreaAppearedInfo> displayAreaInfos;
+        displayAreaInfos = super.registerOrganizer(displayAreaFeature);
+        for (int i = 0; i < displayAreaInfos.size(); i++) {
+            final DisplayAreaAppearedInfo info = displayAreaInfos.get(i);
+            onDisplayAreaAppeared(info.getDisplayAreaInfo(), info.getLeash());
         }
+        return displayAreaInfos;
     }
 
     @Override
     public void unregisterOrganizer() {
-        synchronized (mLock) {
-            super.unregisterOrganizer();
-            mParentLeash = null;
-        }
+        super.unregisterOrganizer();
+        removeBackgroundPanelLayer();
+        mParentLeash = null;
+    }
+
+    @Override
+    public void onAnimationUpdate(SurfaceControl.Transaction tx, float xPos, float yPos) {
+        final int yTopPos = (mStableInsets.top - mBkgBounds.height()) + Math.round(yPos);
+        tx.setPosition(mBackgroundSurface, 0, yTopPos);
     }
 
     @Nullable
     @VisibleForTesting
-    SurfaceControl getBackgroundSurface() {
-        synchronized (mLock) {
-            if (mParentLeash == null) {
-                return null;
-            }
+    boolean isRegistered() {
+        return mParentLeash != null;
+    }
 
-            if (mBackgroundSurface == null) {
-                mBackgroundSurface = new SurfaceControl.Builder(mSurfaceSession)
-                        .setParent(mParentLeash)
-                        .setBufferSize(mBkgBounds.width(), mBkgBounds.height())
-                        .setColorLayer()
-                        .setFormat(PixelFormat.RGB_888)
-                        .setOpaque(true)
-                        .setName("one-handed-background-panel")
-                        .setCallsite("OneHandedBackgroundPanelOrganizer")
-                        .build();
-            }
-            return mBackgroundSurface;
+    void createBackgroundSurface() {
+        mBackgroundSurface = new SurfaceControl.Builder(mSurfaceSession)
+                .setBufferSize(mBkgBounds.width(), mBkgBounds.height())
+                .setColorLayer()
+                .setFormat(PixelFormat.RGB_888)
+                .setOpaque(true)
+                .setName("one-handed-background-panel")
+                .setCallsite("OneHandedBackgroundPanelOrganizer")
+                .build();
+
+        // TODO(185890335) Avoid Dimming for mid-range luminance wallpapers flash.
+        mAlphaAnimator = ValueAnimator.ofFloat(1.0f, 0.0f);
+        mAlphaAnimator.setInterpolator(new LinearInterpolator());
+        mAlphaAnimator.setDuration(ALPHA_ANIMATION_DURATION);
+        mAlphaAnimator.addUpdateListener(
+                animator -> detachBackgroundFromParent(animator));
+    }
+
+    void detachBackgroundFromParent(ValueAnimator animator) {
+        if (mBackgroundSurface == null || mParentLeash == null) {
+            return;
         }
+        // TODO(185890335) Avoid Dimming for mid-range luminance wallpapers flash.
+        final float currentValue = (float) animator.getAnimatedValue();
+        final SurfaceControl.Transaction tx = mTransactionFactory.getTransaction();
+        if (currentValue == 0.0f) {
+            tx.reparent(mBackgroundSurface, null).apply();
+        } else {
+            tx.setAlpha(mBackgroundSurface, (float) animator.getAnimatedValue()).apply();
+        }
+    }
+
+    /**
+     * Called when onDisplayAdded() or onDisplayRemoved() callback.
+     *
+     * @param displayLayout The latest {@link DisplayLayout} representing current displayId
+     */
+    public void onDisplayChanged(DisplayLayout displayLayout) {
+        mStableInsets = displayLayout.stableInsets();
+        // Ensure the mBkgBounds is portrait, due to OHM only support on portrait
+        if (displayLayout.height() > displayLayout.width()) {
+            mBkgBounds = new Rect(0, 0, displayLayout.width(),
+                    Math.round(displayLayout.height() * mTranslationFraction) + mStableInsets.top);
+        } else {
+            mBkgBounds = new Rect(0, 0, displayLayout.height(),
+                    Math.round(displayLayout.width() * mTranslationFraction) + mStableInsets.top);
+        }
+    }
+
+    @VisibleForTesting
+    void onStart() {
+        if (mBackgroundSurface == null) {
+            createBackgroundSurface();
+        }
+        showBackgroundPanelLayer();
+    }
+
+    /**
+     * Called when transition finished.
+     */
+    public void onStopFinished() {
+        if (mAlphaAnimator == null) {
+            return;
+        }
+        mAlphaAnimator.start();
     }
 
     @VisibleForTesting
     void showBackgroundPanelLayer() {
-        synchronized (mLock) {
-            if (mIsShowing) {
-                return;
-            }
-
-            if (getBackgroundSurface() == null) {
-                return;
-            }
-
-            SurfaceControl.Transaction transaction =
-                    mSurfaceControlTransactionFactory.getTransaction();
-            transaction.setLayer(mBackgroundSurface, -1 /* at bottom-most layer */)
-                    .setColor(mBackgroundSurface, mDefaultColor)
-                    .show(mBackgroundSurface)
-                    .apply();
-            transaction.close();
-            mIsShowing = true;
+        if (mParentLeash == null) {
+            return;
         }
+
+        if (mBackgroundSurface == null) {
+            createBackgroundSurface();
+        }
+
+        // TODO(185890335) Avoid Dimming for mid-range luminance wallpapers flash.
+        if (mAlphaAnimator.isRunning()) {
+            mAlphaAnimator.end();
+        }
+
+        mTransactionFactory.getTransaction()
+                .reparent(mBackgroundSurface, mParentLeash)
+                .setAlpha(mBackgroundSurface, 1.0f)
+                .setLayer(mBackgroundSurface, -1 /* at bottom-most layer */)
+                .setColor(mBackgroundSurface, mThemeColor)
+                .show(mBackgroundSurface)
+                .apply();
     }
 
     @VisibleForTesting
     void removeBackgroundPanelLayer() {
-        synchronized (mLock) {
-            if (mBackgroundSurface == null) {
-                return;
-            }
-
-            SurfaceControl.Transaction transaction =
-                    mSurfaceControlTransactionFactory.getTransaction();
-            transaction.remove(mBackgroundSurface).apply();
-            transaction.close();
-            mBackgroundSurface = null;
-            mIsShowing = false;
+        if (mBackgroundSurface == null) {
+            return;
         }
+
+        mTransactionFactory.getTransaction()
+                .remove(mBackgroundSurface)
+                .apply();
+        mBackgroundSurface = null;
     }
 
     /**
      * onConfigurationChanged events for updating tutorial text.
      */
     public void onConfigurationChanged() {
-        synchronized (mLock) {
-            if (mBackgroundSurface == null) {
-                getBackgroundSurface();
-            } else {
-                removeBackgroundPanelLayer();
-            }
-            updateThemeColors();
-            showBackgroundPanelLayer();
+        updateThemeColors();
+
+        if (mCurrentState != STATE_ACTIVE) {
+            return;
         }
+        showBackgroundPanelLayer();
     }
 
     private void updateThemeColors() {
-        synchronized (mLock) {
-            final int themeColor = mContext.getColor(R.color.one_handed_tutorial_background_color);
-            mDefaultColor = new float[]{(Color.red(themeColor) - THEME_COLOR_OFFSET) / 255.0f,
-                    (Color.green(themeColor) - THEME_COLOR_OFFSET) / 255.0f,
-                    (Color.blue(themeColor) - THEME_COLOR_OFFSET) / 255.0f};
-        }
+        final Context themedContext = new ContextThemeWrapper(mContext,
+                com.android.internal.R.style.Theme_DeviceDefault_DayNight);
+        final int themeColor = themedContext.getColor(
+                R.color.one_handed_tutorial_background_color);
+        mThemeColor = new float[]{
+                adjustColor(Color.red(themeColor)),
+                adjustColor(Color.green(themeColor)),
+                adjustColor(Color.blue(themeColor))};
+    }
+
+    private float adjustColor(int origColor) {
+        return Math.max(origColor - THEME_COLOR_OFFSET, 0) / 255.0f;
+    }
+
+    @Override
+    public void onStateChanged(int newState) {
+        mCurrentState = newState;
     }
 
     void dump(@NonNull PrintWriter pw) {
         final String innerPrefix = "  ";
         pw.println(TAG);
-        pw.print(innerPrefix + "mIsShowing=");
-        pw.println(mIsShowing);
+        pw.print(innerPrefix + "mBackgroundSurface=");
+        pw.println(mBackgroundSurface);
         pw.print(innerPrefix + "mBkgBounds=");
         pw.println(mBkgBounds);
-        pw.print(innerPrefix + "mDefaultColor=");
-        pw.println(mDefaultColor);
+        pw.print(innerPrefix + "mThemeColor=");
+        pw.println(mThemeColor);
+        pw.print(innerPrefix + "mTranslationFraction=");
+        pw.println(mTranslationFraction);
     }
 }
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 09cde38..954ca14 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
@@ -180,6 +180,7 @@
                 public void onStopFinished(Rect bounds) {
                     mState.setState(STATE_NONE);
                     notifyShortcutStateChanged(STATE_NONE);
+                    mBackgroundPanelOrganizer.onStopFinished();
                 }
             };
 
@@ -223,13 +224,14 @@
         OneHandedTimeoutHandler timeoutHandler = new OneHandedTimeoutHandler(mainExecutor);
         OneHandedState transitionState = new OneHandedState();
         OneHandedTutorialHandler tutorialHandler = new OneHandedTutorialHandler(context,
-                windowManager);
+                settingsUtil, windowManager);
         OneHandedAnimationController animationController =
                 new OneHandedAnimationController(context);
         OneHandedTouchHandler touchHandler = new OneHandedTouchHandler(timeoutHandler,
                 mainExecutor);
         OneHandedBackgroundPanelOrganizer oneHandedBackgroundPanelOrganizer =
-                new OneHandedBackgroundPanelOrganizer(context, displayLayout, mainExecutor);
+                new OneHandedBackgroundPanelOrganizer(context, displayLayout, settingsUtil,
+                        mainExecutor);
         OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer(
                 context, displayLayout, settingsUtil, animationController, tutorialHandler,
                 oneHandedBackgroundPanelOrganizer, mainExecutor);
@@ -305,6 +307,7 @@
         mAccessibilityManager.addAccessibilityStateChangeListener(
                 mAccessibilityStateChangeListener);
 
+        mState.addSListeners(mBackgroundPanelOrganizer);
         mState.addSListeners(mTutorialHandler);
     }
 
@@ -386,6 +389,7 @@
                 mDisplayAreaOrganizer.getDisplayLayout().height() * mOffSetFraction);
         mOneHandedAccessibilityUtil.announcementForScreenReader(
                 mOneHandedAccessibilityUtil.getOneHandedStartDescription());
+        mBackgroundPanelOrganizer.onStart();
         mDisplayAreaOrganizer.scheduleOffset(0, yOffSet);
         mTimeoutHandler.resetTimer();
         mOneHandedUiEventLogger.writeEvent(
@@ -423,7 +427,6 @@
                 stopOneHanded(OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_OVERSPACE_OUT));
         mDisplayAreaOrganizer.registerTransitionCallback(mTouchHandler);
         mDisplayAreaOrganizer.registerTransitionCallback(mTutorialHandler);
-        mDisplayAreaOrganizer.registerTransitionCallback(mBackgroundPanelOrganizer);
         mDisplayAreaOrganizer.registerTransitionCallback(mTransitionCallBack);
         if (mTaskChangeToExit) {
             mTaskStackListener.addListener(mTaskStackListenerCallback);
@@ -469,6 +472,7 @@
         final DisplayLayout newDisplayLayout = mDisplayController.getDisplayLayout(displayId);
         mDisplayAreaOrganizer.setDisplayLayout(newDisplayLayout);
         mTutorialHandler.onDisplayChanged(newDisplayLayout);
+        mBackgroundPanelOrganizer.onDisplayChanged(newDisplayLayout);
     }
 
     private ContentObserver getObserver(Runnable onChangeRunnable) {
@@ -606,7 +610,7 @@
                     OneHandedDisplayAreaOrganizer.FEATURE_ONE_HANDED);
         }
 
-        if (mBackgroundPanelOrganizer.getBackgroundSurface() == null) {
+        if (!mBackgroundPanelOrganizer.isRegistered()) {
             mBackgroundPanelOrganizer.registerOrganizer(
                     OneHandedBackgroundPanelOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL);
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index 03a90c6..c2bbd9e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -135,8 +135,8 @@
                 SystemProperties.getInt(ONE_HANDED_MODE_TRANSLATE_ANIMATION_DURATION,
                         animationDurationConfig);
         mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
-        mTutorialHandler = tutorialHandler;
         mBackgroundPanelOrganizer = oneHandedBackgroundGradientOrganizer;
+        mTutorialHandler = tutorialHandler;
     }
 
     @Override
@@ -249,9 +249,8 @@
         if (animator != null) {
             animator.setTransitionDirection(direction)
                     .addOneHandedAnimationCallback(mOneHandedAnimationCallback)
-                    .addOneHandedAnimationCallback(mTutorialHandler.getAnimationCallback())
-                    .addOneHandedAnimationCallback(
-                            mBackgroundPanelOrganizer.getOneHandedAnimationCallback())
+                    .addOneHandedAnimationCallback(mTutorialHandler)
+                    .addOneHandedAnimationCallback(mBackgroundPanelOrganizer)
                     .setDuration(durationMs)
                     .start();
         }
@@ -267,7 +266,6 @@
         mLastVisualDisplayBounds.offsetTo(0, Math.round(mLastVisualOffset));
         for (int i = mTransitionCallbacks.size() - 1; i >= 0; i--) {
             final OneHandedTransitionCallback cb = mTransitionCallbacks.get(i);
-            cb.onStartTransition(false /* isTransitioning */);
             if (direction == TRANSITION_DIRECTION_TRIGGER) {
                 cb.onStartFinished(getLastVisualDisplayBounds());
             } else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
index aa6961a..ff333c8c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
@@ -20,6 +20,7 @@
 
 import android.annotation.IntDef;
 import android.content.ContentResolver;
+import android.content.Context;
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.provider.Settings;
@@ -27,6 +28,8 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.wm.shell.R;
+
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -36,7 +39,6 @@
  */
 public final class OneHandedSettingsUtil {
     private static final String TAG = "OneHandedSettingsUtil";
-
     private static final String ONE_HANDED_MODE_TARGET_NAME =
             ONE_HANDED_COMPONENT_NAME.getShortClassName();
 
@@ -128,7 +130,7 @@
      */
     public boolean getSettingsTapsAppToExit(ContentResolver resolver, int userId) {
         return Settings.Secure.getIntForUser(resolver,
-                Settings.Secure.TAPS_APP_TO_EXIT, 0, userId) == 1;
+                Settings.Secure.TAPS_APP_TO_EXIT, 1, userId) == 1;
     }
 
     /**
@@ -217,6 +219,25 @@
                 Settings.Secure.ONE_HANDED_MODE_ACTIVATED, state, userId);
     }
 
+    /**
+     * Obtains one-handed mode transition duration from resource config.
+     *
+     * @return durationMs The duration in milli-seconds
+     */
+    public int getTransitionDuration(Context context) {
+        return context.getResources().getInteger(
+                R.integer.config_one_handed_translate_animation_duration);
+    }
+
+    /**
+     * Obtains one-handed mode offset fraction from resource config.
+     *
+     * @return fraction The fraction of offset of the whole screen.
+     */
+    public float getTranslationFraction(Context context) {
+        return context.getResources().getFraction(R.fraction.config_one_handed_offset, 1, 1);
+    }
+
     void dump(PrintWriter pw, String prefix, ContentResolver resolver,
             int userId) {
         final String innerPrefix = "  ";
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
index 97e04b5..81dd60d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
@@ -32,9 +32,10 @@
 import android.content.res.TypedArray;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
-import android.os.SystemProperties;
+import android.view.ContextThemeWrapper;
 import android.view.Gravity;
 import android.view.LayoutInflater;
+import android.view.SurfaceControl;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
@@ -44,7 +45,6 @@
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
-import androidx.appcompat.view.ContextThemeWrapper;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.wm.shell.R;
@@ -58,66 +58,54 @@
  * detach TargetViewContainer from window after exiting one handed mode.
  */
 public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
-        OneHandedState.OnStateChangedListener {
+        OneHandedState.OnStateChangedListener, OneHandedAnimationCallback {
     private static final String TAG = "OneHandedTutorialHandler";
-    private static final String OFFSET_PERCENTAGE = "persist.debug.one_handed_offset_percentage";
-    private static final String TRANSLATE_ANIMATION_DURATION =
-            "persist.debug.one_handed_translate_animation_duration";
-    private static final float START_TRANSITION_FRACTION = 0.7f;
+    private static final float START_TRANSITION_FRACTION = 0.6f;
 
     private final float mTutorialHeightRatio;
     private final WindowManager mWindowManager;
-    private final OneHandedAnimationCallback mAnimationCallback;
 
     private @OneHandedState.State int mCurrentState;
     private int mTutorialAreaHeight;
 
     private Context mContext;
     private Rect mDisplayBounds;
+    private ValueAnimator mAlphaAnimator;
     private @Nullable View mTutorialView;
     private @Nullable ViewGroup mTargetViewContainer;
 
     private float mAlphaTransitionStart;
-    private ValueAnimator mAlphaAnimator;
     private int mAlphaAnimationDurationMs;
 
-    public OneHandedTutorialHandler(Context context, WindowManager windowManager) {
+    public OneHandedTutorialHandler(Context context, OneHandedSettingsUtil settingsUtil,
+            WindowManager windowManager) {
         mContext = context;
         mWindowManager = windowManager;
-        final float offsetPercentageConfig = context.getResources().getFraction(
-                R.fraction.config_one_handed_offset, 1, 1);
-        final int sysPropPercentageConfig = SystemProperties.getInt(
-                OFFSET_PERCENTAGE, Math.round(offsetPercentageConfig * 100.0f));
-        mTutorialHeightRatio = sysPropPercentageConfig / 100.0f;
-        final int animationDuration = context.getResources().getInteger(
-                R.integer.config_one_handed_translate_animation_duration);
-        mAlphaAnimationDurationMs = SystemProperties.getInt(TRANSLATE_ANIMATION_DURATION,
-                animationDuration);
-        mAnimationCallback = new OneHandedAnimationCallback() {
-            @Override
-            public void onOneHandedAnimationCancel(
-                    OneHandedAnimationController.OneHandedTransitionAnimator animator) {
-                if (mAlphaAnimator != null) {
-                    mAlphaAnimator.cancel();
-                }
-            }
+        mTutorialHeightRatio = settingsUtil.getTranslationFraction(context);
+        mAlphaAnimationDurationMs = settingsUtil.getTransitionDuration(context);
+    }
 
-            @Override
-            public void onAnimationUpdate(float xPos, float yPos) {
-                if (!isAttached()) {
-                    return;
-                }
-                if (yPos < mAlphaTransitionStart) {
-                    checkTransitionEnd();
-                    return;
-                }
-                if (mAlphaAnimator == null || mAlphaAnimator.isStarted()
-                        || mAlphaAnimator.isRunning()) {
-                    return;
-                }
-                mAlphaAnimator.start();
-            }
-        };
+    @Override
+    public void onOneHandedAnimationCancel(
+            OneHandedAnimationController.OneHandedTransitionAnimator animator) {
+        if (mAlphaAnimator != null) {
+            mAlphaAnimator.cancel();
+        }
+    }
+
+    @Override
+    public void onAnimationUpdate(SurfaceControl.Transaction tx, float xPos, float yPos) {
+        if (!isAttached()) {
+            return;
+        }
+        if (yPos < mAlphaTransitionStart) {
+            checkTransitionEnd();
+            return;
+        }
+        if (mAlphaAnimator == null || mAlphaAnimator.isStarted() || mAlphaAnimator.isRunning()) {
+            return;
+        }
+        mAlphaAnimator.start();
     }
 
     @Override
@@ -145,6 +133,7 @@
 
     /**
      * Called when onDisplayAdded() or onDisplayRemoved() callback.
+     *
      * @param displayLayout The latest {@link DisplayLayout} representing current displayId
      */
     public void onDisplayChanged(DisplayLayout displayLayout) {
@@ -196,10 +185,6 @@
         mTargetViewContainer = null;
     }
 
-    @Nullable OneHandedAnimationCallback getAnimationCallback() {
-        return mAnimationCallback;
-    }
-
     /**
      * Returns layout params for the dismiss target, using the latest display metrics.
      */
@@ -264,15 +249,17 @@
     private void setupAlphaTransition(boolean isEntering) {
         final float start = isEntering ? 0.0f : 1.0f;
         final float end = isEntering ? 1.0f : 0.0f;
+        final int duration = isEntering ? mAlphaAnimationDurationMs : Math.round(
+                mAlphaAnimationDurationMs * (1.0f - mTutorialHeightRatio));
         mAlphaAnimator = ValueAnimator.ofFloat(start, end);
         mAlphaAnimator.setInterpolator(new LinearInterpolator());
-        mAlphaAnimator.setDuration(mAlphaAnimationDurationMs);
+        mAlphaAnimator.setDuration(duration);
         mAlphaAnimator.addUpdateListener(
                 animator -> mTargetViewContainer.setAlpha((float) animator.getAnimatedValue()));
     }
 
     private void checkTransitionEnd() {
-        if (mAlphaAnimator != null && mAlphaAnimator.isRunning()) {
+        if (mAlphaAnimator != null && (mAlphaAnimator.isRunning() || mAlphaAnimator.isStarted())) {
             mAlphaAnimator.end();
             mAlphaAnimator.removeAllUpdateListeners();
             mAlphaAnimator = null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index d77619a..9686776 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -137,11 +137,6 @@
         public void onPipAnimationStart(TaskInfo taskInfo,
                 PipAnimationController.PipTransitionAnimator animator) {
             final int direction = animator.getTransitionDirection();
-            if (direction == TRANSITION_DIRECTION_TO_PIP) {
-                // TODO (b//169221267): Add jank listener for transactions without buffer updates.
-                //InteractionJankMonitor.getInstance().begin(
-                //        InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP, 2000);
-            }
             sendOnPipTransitionStarted(direction);
         }
 
@@ -618,7 +613,6 @@
 
     private void onEndOfSwipePipToHomeTransition() {
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
-            mPipTransitionState.setInSwipePipToHomeTransition(false);
             mSwipePipToHomeOverlay = null;
             return;
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index ae17a93..6fec1fb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -19,6 +19,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.util.RotationUtils.deltaRotation;
+import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_PIP;
 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
 
@@ -126,6 +127,12 @@
             return true;
         }
 
+        // We only support TRANSIT_PIP type (from RootWindowContainer) or TRANSIT_OPEN (from apps
+        // that enter PiP instantly on opening, mostly from CTS/Flicker tests)
+        if (info.getType() != TRANSIT_PIP && info.getType() != TRANSIT_OPEN) {
+            return false;
+        }
+
         // Search for an Enter PiP transition (along with a show wallpaper one)
         TransitionInfo.Change enterPip = null;
         TransitionInfo.Change wallpaper = null;
@@ -163,9 +170,12 @@
         if (request.getType() == TRANSIT_PIP) {
             WindowContainerTransaction wct = new WindowContainerTransaction();
             mPipTransitionState.setTransitionState(PipTransitionState.ENTRY_SCHEDULED);
-            final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
-            wct.setActivityWindowingMode(request.getTriggerTask().token, WINDOWING_MODE_UNDEFINED);
-            wct.setBounds(request.getTriggerTask().token, destinationBounds);
+            if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
+                wct.setActivityWindowingMode(request.getTriggerTask().token,
+                        WINDOWING_MODE_UNDEFINED);
+                final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
+                wct.setBounds(request.getTriggerTask().token, destinationBounds);
+            }
             return wct;
         } else {
             return null;
@@ -236,6 +246,7 @@
             onFinishResize(taskInfo, destinationBounds, TRANSITION_DIRECTION_TO_PIP, tx);
             sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
             mFinishCallback = null;
+            mPipTransitionState.setInSwipePipToHomeTransition(false);
             return true;
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index dedc566..dbf603c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -19,7 +19,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
 import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK;
-import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
 
 import android.app.PictureInPictureParams;
 import android.app.TaskInfo;
@@ -57,12 +56,6 @@
                 public void onPipAnimationStart(TaskInfo taskInfo,
                         PipAnimationController.PipTransitionAnimator animator) {
                     final int direction = animator.getTransitionDirection();
-                    if (direction == TRANSITION_DIRECTION_TO_PIP) {
-                        // TODO (b//169221267): Add jank listener for transactions without buffer
-                        //  updates.
-                        //InteractionJankMonitor.getInstance().begin(
-                        //        InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP, 2000);
-                    }
                     sendOnPipTransitionStarted(direction);
                 }
 
@@ -76,12 +69,6 @@
                     }
                     onFinishResize(taskInfo, animator.getDestinationBounds(), direction, tx);
                     sendOnPipTransitionFinished(direction);
-                    if (direction == TRANSITION_DIRECTION_TO_PIP) {
-                        // TODO (b//169221267): Add jank listener for transactions without buffer
-                        //  updates.
-                        //InteractionJankMonitor.getInstance().end(
-                        //        InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP);
-                    }
                 }
 
                 @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 62b50c5..ac02075 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -21,7 +21,16 @@
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
 import static android.view.WindowManager.INPUT_CONSUMER_PIP;
 
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_PIP_TRANSITION;
 import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_EXPAND_OR_UNEXPAND;
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP;
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN;
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK;
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_SAME;
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_SNAP_AFTER_RESIZE;
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
+import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_USER_RESIZE;
 import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
 
 import android.app.ActivityManager;
@@ -52,6 +61,7 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
 import com.android.wm.shell.R;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.common.DisplayChangeController;
@@ -75,6 +85,7 @@
 import com.android.wm.shell.pip.PipTaskOrganizer;
 import com.android.wm.shell.pip.PipTransitionController;
 import com.android.wm.shell.pip.PipUtils;
+import com.android.wm.shell.transition.Transitions;
 
 import java.io.PrintWriter;
 import java.util.Objects;
@@ -446,11 +457,18 @@
             return;
         }
         Runnable updateDisplayLayout = () -> {
+            final boolean fromRotation = Transitions.ENABLE_SHELL_TRANSITIONS
+                    && mPipBoundsState.getDisplayLayout().rotation() != layout.rotation();
             mPipBoundsState.setDisplayLayout(layout);
+            final WindowContainerTransaction wct =
+                    fromRotation ? new WindowContainerTransaction() : null;
             updateMovementBounds(null /* toBounds */,
-                    false /* fromRotation */, false /* fromImeAdjustment */,
+                    fromRotation, false /* fromImeAdjustment */,
                     false /* fromShelfAdjustment */,
-                    null /* windowContainerTransaction */);
+                    wct /* windowContainerTransaction */);
+            if (wct != null) {
+                mPipTaskOrganizer.applyFinishBoundsResize(wct, TRANSITION_DIRECTION_SAME);
+            }
         };
 
         if (mPipTaskOrganizer.isInPip() && saveRestoreSnapFraction) {
@@ -567,8 +585,37 @@
         mPipTaskOrganizer.stopSwipePipToHome(componentName, destinationBounds, overlay);
     }
 
+    private String getTransitionTag(int direction) {
+        switch (direction) {
+            case TRANSITION_DIRECTION_TO_PIP:
+                return "TRANSITION_TO_PIP";
+            case TRANSITION_DIRECTION_LEAVE_PIP:
+                return "TRANSITION_LEAVE_PIP";
+            case TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN:
+                return "TRANSITION_LEAVE_PIP_TO_SPLIT_SCREEN";
+            case TRANSITION_DIRECTION_REMOVE_STACK:
+                return "TRANSITION_REMOVE_STACK";
+            case TRANSITION_DIRECTION_SNAP_AFTER_RESIZE:
+                return "TRANSITION_SNAP_AFTER_RESIZE";
+            case TRANSITION_DIRECTION_USER_RESIZE:
+                return "TRANSITION_USER_RESIZE";
+            case TRANSITION_DIRECTION_EXPAND_OR_UNEXPAND:
+                return "TRANSITION_EXPAND_OR_UNEXPAND";
+            default:
+                return "TRANSITION_LEAVE_UNKNOWN";
+        }
+    }
+
     @Override
     public void onPipTransitionStarted(int direction, Rect pipBounds) {
+        // Begin InteractionJankMonitor with PIP transition CUJs
+        final InteractionJankMonitor.Configuration.Builder builder =
+                InteractionJankMonitor.Configuration.Builder.withSurface(
+                        CUJ_PIP_TRANSITION, mContext, mPipTaskOrganizer.getSurfaceControl())
+                .setTag(getTransitionTag(direction))
+                .setTimeout(2000);
+        InteractionJankMonitor.getInstance().begin(builder);
+
         if (isOutPipDirection(direction)) {
             // Exiting PIP, save the reentry state to restore to when re-entering.
             saveReentryState(pipBounds);
@@ -607,6 +654,9 @@
     }
 
     private void onPipTransitionFinishedOrCanceled(int direction) {
+        // End InteractionJankMonitor with PIP transition by CUJs
+        InteractionJankMonitor.getInstance().end(CUJ_PIP_TRANSITION);
+
         // Re-enable touches after the animation completes
         mTouchHandler.setTouchEnabled(true);
         mTouchHandler.onPinnedStackAnimationEnded(direction);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
index 1fc4d12..ab3cbd6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
@@ -47,6 +47,8 @@
 
     /** Callback for size compat UI interaction. */
     public interface SizeCompatUICallback {
+        /** Called when the size compat restart button appears. */
+        void onSizeCompatRestartButtonAppeared(int taskId);
         /** Called when the size compat restart button is clicked. */
         void onSizeCompatRestartButtonClicked(int taskId);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
index a5e96d1..7cf9559 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
@@ -28,6 +28,7 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.os.Binder;
+import android.util.Log;
 import android.view.SurfaceControl;
 import android.view.View;
 import android.view.WindowManager;
@@ -45,7 +46,7 @@
 class SizeCompatUILayout {
     private static final String TAG = "SizeCompatUILayout";
 
-    private final SyncTransactionQueue mSyncQueue;
+    final SyncTransactionQueue mSyncQueue;
     private final SizeCompatUIController.SizeCompatUICallback mCallback;
     private Context mContext;
     private Configuration mTaskConfig;
@@ -105,6 +106,8 @@
             mShouldShowHint = false;
             createSizeCompatHint();
         }
+
+        mCallback.onSizeCompatRestartButtonAppeared(mTaskId);
     }
 
     /** Creates the restart button hint window. */
@@ -306,6 +309,10 @@
 
     private void updateSurfacePosition(SurfaceControl leash, int positionX, int positionY) {
         mSyncQueue.runInSync(t -> {
+            if (!leash.isValid()) {
+                Log.w(TAG, "The leash has been released.");
+                return;
+            }
             t.setPosition(leash, positionX, positionY);
             // The size compat UI should be the topmost child of the Task in case there can be more
             // than one children.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java
index f634c45..82f69c3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java
@@ -110,7 +110,8 @@
         }
 
         if (mLeash != null) {
-            new SurfaceControl.Transaction().remove(mLeash).apply();
+            final SurfaceControl leash = mLeash;
+            mLayout.mSyncQueue.runInSync(t -> t.remove(leash));
             mLeash = null;
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
index df1f5e6..6ec514b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
@@ -95,6 +95,8 @@
      * Blocking call that notifies and gets additional split-screen targets when entering
      * recents (for example: the dividerBar).
      * @param cancel is true if leaving recents back to split (eg. the gesture was cancelled).
+     * @param appTargets apps that will be re-parented to display area
      */
-    RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel) = 12;
+    RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel,
+                                                   in RemoteAnimationTarget[] appTargets) = 12;
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 7804c77..5bfecf2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -43,6 +43,7 @@
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
+import android.view.SurfaceSession;
 import android.window.IRemoteTransition;
 import android.window.WindowContainerTransaction;
 
@@ -50,6 +51,8 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.internal.logging.InstanceId;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayImeController;
@@ -60,11 +63,12 @@
 import com.android.wm.shell.common.annotations.ExternalThread;
 import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
 import com.android.wm.shell.draganddrop.DragAndDropPolicy;
-import com.android.wm.shell.splitscreen.ISplitScreenListener;
 import com.android.wm.shell.transition.LegacyTransitions;
 import com.android.wm.shell.transition.Transitions;
 
 import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Comparator;
 import java.util.concurrent.Executor;
 
 /**
@@ -85,6 +89,7 @@
     private final DisplayImeController mDisplayImeController;
     private final Transitions mTransitions;
     private final TransactionPool mTransactionPool;
+    private final SplitscreenEventLogger mLogger;
 
     private StageCoordinator mStageCoordinator;
 
@@ -101,6 +106,7 @@
         mDisplayImeController = displayImeController;
         mTransitions = transitions;
         mTransactionPool = transactionPool;
+        mLogger = new SplitscreenEventLogger();
     }
 
     public SplitScreen asSplitScreen() {
@@ -122,7 +128,7 @@
             // TODO: Multi-display
             mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
                     mRootTDAOrganizer, mTaskOrganizer, mDisplayImeController, mTransitions,
-                    mTransactionPool);
+                    mTransactionPool, mLogger);
         }
     }
 
@@ -164,8 +170,8 @@
                 leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT);
     }
 
-    public void exitSplitScreen() {
-        mStageCoordinator.exitSplitScreen();
+    public void exitSplitScreen(int exitReason) {
+        mStageCoordinator.exitSplitScreen(exitReason);
     }
 
     public void onKeyguardOccludedChanged(boolean occluded) {
@@ -281,11 +287,38 @@
         mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
     }
 
-    RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel) {
+    RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel, RemoteAnimationTarget[] apps) {
         if (!isSplitScreenVisible()) return null;
+        final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
+                .setContainerLayer()
+                .setName("RecentsAnimationSplitTasks")
+                .setHidden(false)
+                .setCallsite("SplitScreenController#onGoingtoRecentsLegacy");
+        mRootTDAOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, builder);
+        SurfaceControl sc = builder.build();
+        SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+
+        // Ensure that we order these in the parent in the right z-order as their previous order
+        Arrays.sort(apps, (a1, a2) -> a1.prefixOrderIndex - a2.prefixOrderIndex);
+        int layer = 1;
+        for (RemoteAnimationTarget appTarget : apps) {
+            transaction.reparent(appTarget.leash, sc);
+            transaction.setPosition(appTarget.leash, appTarget.screenSpaceBounds.left,
+                    appTarget.screenSpaceBounds.top);
+            transaction.setLayer(appTarget.leash, layer++);
+        }
+        transaction.apply();
+        transaction.close();
         return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
     }
 
+    /**
+     * Sets drag info to be logged when splitscreen is entered.
+     */
+    public void logOnDroppedToSplit(@SplitPosition int position, InstanceId dragSessionId) {
+        mStageCoordinator.logOnDroppedToSplit(position, dragSessionId);
+    }
+
     public void dump(@NonNull PrintWriter pw, String prefix) {
         pw.println(prefix + TAG);
         if (mStageCoordinator != null) {
@@ -478,7 +511,8 @@
         public void exitSplitScreen() {
             executeRemoteCallWithTaskPermission(mController, "exitSplitScreen",
                     (controller) -> {
-                        controller.exitSplitScreen();
+                        controller.exitSplitScreen(
+                                FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME);
                     });
         }
 
@@ -554,10 +588,11 @@
         }
 
         @Override
-        public RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel) {
+        public RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel,
+                RemoteAnimationTarget[] apps) {
             final RemoteAnimationTarget[][] out = new RemoteAnimationTarget[][]{null};
             executeRemoteCallWithTaskPermission(mController, "onGoingToRecentsLegacy",
-                    (controller) -> out[0] = controller.onGoingToRecentsLegacy(cancel),
+                    (controller) -> out[0] = controller.onGoingToRecentsLegacy(cancel, apps),
                     true /* blocking */);
             return out[0];
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
new file mode 100644
index 0000000..319079b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
@@ -0,0 +1,324 @@
+/*
+ * 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.wm.shell.splitscreen;
+
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__OVERVIEW;
+import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
+
+import com.android.internal.logging.InstanceId;
+import com.android.internal.logging.InstanceIdSequence;
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.wm.shell.common.split.SplitLayout.SplitPosition;
+
+/**
+ * Helper class that to log Drag & Drop UIEvents for a single session, see also go/uievent
+ */
+public class SplitscreenEventLogger {
+
+    // Used to generate instance ids for this drag if one is not provided
+    private final InstanceIdSequence mIdSequence;
+
+    // The instance id for the current splitscreen session (from start to end)
+    private InstanceId mLoggerSessionId;
+
+    // Drag info
+    private @SplitPosition int mDragEnterPosition;
+    private InstanceId mDragEnterSessionId;
+
+    // For deduping async events
+    private int mLastMainStagePosition = -1;
+    private int mLastMainStageUid = -1;
+    private int mLastSideStagePosition = -1;
+    private int mLastSideStageUid = -1;
+    private float mLastSplitRatio = -1f;
+
+    public SplitscreenEventLogger() {
+        mIdSequence = new InstanceIdSequence(Integer.MAX_VALUE);
+    }
+
+    /**
+     * Return whether a splitscreen session has started.
+     */
+    public boolean hasStartedSession() {
+        return mLoggerSessionId != null;
+    }
+
+    /**
+     * May be called before logEnter() to indicate that the session was started from a drag.
+     */
+    public void enterRequestedByDrag(@SplitPosition int position, InstanceId dragSessionId) {
+        mDragEnterPosition = position;
+        mDragEnterSessionId = dragSessionId;
+    }
+
+    /**
+     * Logs when the user enters splitscreen.
+     */
+    public void logEnter(float splitRatio,
+            @SplitPosition int mainStagePosition, int mainStageUid,
+            @SplitPosition int sideStagePosition, int sideStageUid,
+            boolean isLandscape) {
+        mLoggerSessionId = mIdSequence.newInstanceId();
+        int enterReason = mDragEnterPosition != SPLIT_POSITION_UNDEFINED
+                ? getDragEnterReasonFromSplitPosition(mDragEnterPosition, isLandscape)
+                : SPLITSCREEN_UICHANGED__ENTER_REASON__OVERVIEW;
+        updateMainStageState(getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape),
+                mainStageUid);
+        updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape),
+                sideStageUid);
+        updateSplitRatioState(splitRatio);
+        FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
+                FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__ENTER,
+                enterReason,
+                0 /* exitReason */,
+                splitRatio,
+                mLastMainStagePosition,
+                mLastMainStageUid,
+                mLastSideStagePosition,
+                mLastSideStageUid,
+                mDragEnterSessionId != null ? mDragEnterSessionId.getId() : 0,
+                mLoggerSessionId.getId());
+    }
+
+    /**
+     * Logs when the user exits splitscreen.  Only one of the main or side stages should be
+     * specified to indicate which position was focused as a part of exiting (both can be unset).
+     */
+    public void logExit(int exitReason, @SplitPosition int mainStagePosition, int mainStageUid,
+            @SplitPosition int sideStagePosition, int sideStageUid, boolean isLandscape) {
+        if (mLoggerSessionId == null) {
+            // Ignore changes until we've started logging the session
+            return;
+        }
+        if ((mainStagePosition != SPLIT_POSITION_UNDEFINED
+                && sideStagePosition != SPLIT_POSITION_UNDEFINED)
+                        || (mainStageUid != 0 && sideStageUid != 0)) {
+            throw new IllegalArgumentException("Only main or side stage should be set");
+        }
+        FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
+                FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__EXIT,
+                0 /* enterReason */,
+                exitReason,
+                0f /* splitRatio */,
+                getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape),
+                mainStageUid,
+                getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape),
+                sideStageUid,
+                0 /* dragInstanceId */,
+                mLoggerSessionId.getId());
+
+        // Reset states
+        mLoggerSessionId = null;
+        mDragEnterPosition = SPLIT_POSITION_UNDEFINED;
+        mDragEnterSessionId = null;
+        mLastMainStagePosition = -1;
+        mLastMainStageUid = -1;
+        mLastSideStagePosition = -1;
+        mLastSideStageUid = -1;
+    }
+
+    /**
+     * Logs when an app in the main stage changes.
+     */
+    public void logMainStageAppChange(@SplitPosition int mainStagePosition, int mainStageUid,
+            boolean isLandscape) {
+        if (mLoggerSessionId == null) {
+            // Ignore changes until we've started logging the session
+            return;
+        }
+        if (!updateMainStageState(getMainStagePositionFromSplitPosition(mainStagePosition,
+                isLandscape), mainStageUid)) {
+            // Ignore if there are no user perceived changes
+            return;
+        }
+
+        FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
+                FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__APP_CHANGE,
+                0 /* enterReason */,
+                0 /* exitReason */,
+                0f /* splitRatio */,
+                mLastMainStagePosition,
+                mLastMainStageUid,
+                0 /* sideStagePosition */,
+                0 /* sideStageUid */,
+                0 /* dragInstanceId */,
+                mLoggerSessionId.getId());
+    }
+
+    /**
+     * Logs when an app in the side stage changes.
+     */
+    public void logSideStageAppChange(@SplitPosition int sideStagePosition, int sideStageUid,
+            boolean isLandscape) {
+        if (mLoggerSessionId == null) {
+            // Ignore changes until we've started logging the session
+            return;
+        }
+        if (!updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition,
+                isLandscape), sideStageUid)) {
+            // Ignore if there are no user perceived changes
+            return;
+        }
+
+        FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
+                FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__APP_CHANGE,
+                0 /* enterReason */,
+                0 /* exitReason */,
+                0f /* splitRatio */,
+                0 /* mainStagePosition */,
+                0 /* mainStageUid */,
+                mLastSideStagePosition,
+                mLastSideStageUid,
+                0 /* dragInstanceId */,
+                mLoggerSessionId.getId());
+    }
+
+    /**
+     * Logs when the splitscreen ratio changes.
+     */
+    public void logResize(float splitRatio) {
+        if (mLoggerSessionId == null) {
+            // Ignore changes until we've started logging the session
+            return;
+        }
+        if (splitRatio <= 0f || splitRatio >= 1f) {
+            // Don't bother reporting resizes that end up dismissing the split, that will be logged
+            // via the exit event
+            return;
+        }
+        if (!updateSplitRatioState(splitRatio)) {
+            // Ignore if there are no user perceived changes
+            return;
+        }
+        FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
+                FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__RESIZE,
+                0 /* enterReason */,
+                0 /* exitReason */,
+                mLastSplitRatio,
+                0 /* mainStagePosition */, 0 /* mainStageUid */,
+                0 /* sideStagePosition */, 0 /* sideStageUid */,
+                0 /* dragInstanceId */,
+                mLoggerSessionId.getId());
+    }
+
+    /**
+     * Logs when the apps in splitscreen are swapped.
+     */
+    public void logSwap(@SplitPosition int mainStagePosition, int mainStageUid,
+            @SplitPosition int sideStagePosition, int sideStageUid, boolean isLandscape) {
+        if (mLoggerSessionId == null) {
+            // Ignore changes until we've started logging the session
+            return;
+        }
+
+        updateMainStageState(getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape),
+                mainStageUid);
+        updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape),
+                sideStageUid);
+        FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED,
+                FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__SWAP,
+                0 /* enterReason */,
+                0 /* exitReason */,
+                0f /* splitRatio */,
+                mLastMainStagePosition,
+                mLastMainStageUid,
+                mLastSideStagePosition,
+                mLastSideStageUid,
+                0 /* dragInstanceId */,
+                mLoggerSessionId.getId());
+    }
+
+    private boolean updateMainStageState(int mainStagePosition, int mainStageUid) {
+        boolean changed = (mLastMainStagePosition != mainStagePosition)
+                || (mLastMainStageUid != mainStageUid);
+        if (!changed) {
+            return false;
+        }
+
+        mLastMainStagePosition = mainStagePosition;
+        mLastMainStageUid = mainStageUid;
+        return true;
+    }
+
+    private boolean updateSideStageState(int sideStagePosition, int sideStageUid) {
+        boolean changed = (mLastSideStagePosition != sideStagePosition)
+                || (mLastSideStageUid != sideStageUid);
+        if (!changed) {
+            return false;
+        }
+
+        mLastSideStagePosition = sideStagePosition;
+        mLastSideStageUid = sideStageUid;
+        return true;
+    }
+
+    private boolean updateSplitRatioState(float splitRatio) {
+        boolean changed = Float.compare(mLastSplitRatio, splitRatio) != 0;
+        if (!changed) {
+            return false;
+        }
+
+        mLastSplitRatio = splitRatio;
+        return true;
+    }
+
+    public int getDragEnterReasonFromSplitPosition(@SplitPosition int position,
+            boolean isLandscape) {
+        if (isLandscape) {
+            return position == SPLIT_POSITION_TOP_OR_LEFT
+                    ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__DRAG_LEFT
+                    : FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__DRAG_RIGHT;
+        } else {
+            return position == SPLIT_POSITION_TOP_OR_LEFT
+                    ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__DRAG_TOP
+                    : FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__DRAG_BOTTOM;
+        }
+    }
+
+    private int getMainStagePositionFromSplitPosition(@SplitPosition int position,
+            boolean isLandscape) {
+        if (position == SPLIT_POSITION_UNDEFINED) {
+            return 0;
+        }
+        if (isLandscape) {
+            return position == SPLIT_POSITION_TOP_OR_LEFT
+                    ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__MAIN_STAGE_POSITION__LEFT
+                    : FrameworkStatsLog.SPLITSCREEN_UICHANGED__MAIN_STAGE_POSITION__RIGHT;
+        } else {
+            return position == SPLIT_POSITION_TOP_OR_LEFT
+                    ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__MAIN_STAGE_POSITION__TOP
+                    : FrameworkStatsLog.SPLITSCREEN_UICHANGED__MAIN_STAGE_POSITION__BOTTOM;
+        }
+    }
+
+    private int getSideStagePositionFromSplitPosition(@SplitPosition int position,
+            boolean isLandscape) {
+        if (position == SPLIT_POSITION_UNDEFINED) {
+            return 0;
+        }
+        if (isLandscape) {
+            return position == SPLIT_POSITION_TOP_OR_LEFT
+                    ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__SIDE_STAGE_POSITION__LEFT
+                    : FrameworkStatsLog.SPLITSCREEN_UICHANGED__SIDE_STAGE_POSITION__RIGHT;
+        } else {
+            return position == SPLIT_POSITION_TOP_OR_LEFT
+                    ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__SIDE_STAGE_POSITION__TOP
+                    : FrameworkStatsLog.SPLITSCREEN_UICHANGED__SIDE_STAGE_POSITION__BOTTOM;
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 962b5d5..736fae4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -25,7 +25,14 @@
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
 import static android.view.WindowManager.transitTypeToString;
+import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER;
 
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__APP_DOES_NOT_SUPPORT_MULTIWINDOW;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED_SHOW_ON_TOP;
 import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
 import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED;
@@ -73,6 +80,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.InstanceId;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.ShellTaskOrganizer;
@@ -129,6 +137,7 @@
     private final List<SplitScreen.SplitScreenListener> mListeners = new ArrayList<>();
     private final DisplayImeController mDisplayImeController;
     private final SplitScreenTransitions mSplitTransitions;
+    private final SplitscreenEventLogger mLogger;
     private boolean mExitSplitScreenOnHide;
     private boolean mKeyguardOccluded;
 
@@ -155,12 +164,13 @@
     StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
             RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
             DisplayImeController displayImeController, Transitions transitions,
-            TransactionPool transactionPool) {
+            TransactionPool transactionPool, SplitscreenEventLogger logger) {
         mContext = context;
         mDisplayId = displayId;
         mSyncQueue = syncQueue;
         mRootTDAOrganizer = rootTDAOrganizer;
         mTaskOrganizer = taskOrganizer;
+        mLogger = logger;
         mMainStage = new MainStage(
                 mTaskOrganizer,
                 mDisplayId,
@@ -189,7 +199,8 @@
     StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
             RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
             MainStage mainStage, SideStage sideStage, DisplayImeController displayImeController,
-            SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool) {
+            SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool,
+            SplitscreenEventLogger logger) {
         mContext = context;
         mDisplayId = displayId;
         mSyncQueue = syncQueue;
@@ -202,6 +213,7 @@
         mSplitLayout = splitLayout;
         mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions,
                 mOnTransitionAnimationComplete);
+        mLogger = logger;
         transitions.addHandler(this);
     }
 
@@ -375,7 +387,7 @@
                 if (options == null) {
                     options = new Bundle();
                 }
-                updateActivityOptions(options, position, wct);
+                updateActivityOptions(options, position);
                 break;
             }
             case STAGE_TYPE_MAIN: {
@@ -390,7 +402,7 @@
                 if (options == null) {
                     options = new Bundle();
                 }
-                updateActivityOptions(options, position, wct);
+                updateActivityOptions(options, position);
                 break;
             }
             default:
@@ -425,7 +437,7 @@
         if (mSideStageListener.mVisible && updateBounds) {
             if (wct == null) {
                 // onBoundsChanged builds/applies a wct with the contents of updateWindowBounds.
-                onBoundsChanged(mSplitLayout);
+                onLayoutChanged(mSplitLayout);
             } else {
                 updateWindowBounds(mSplitLayout, wct);
             }
@@ -449,19 +461,20 @@
     void onKeyguardVisibilityChanged(boolean showing) {
         if (!showing && mMainStage.isActive()
                 && mTopStageAfterFoldDismiss != STAGE_TYPE_UNDEFINED) {
-            exitSplitScreen(mTopStageAfterFoldDismiss == STAGE_TYPE_MAIN ? mMainStage : mSideStage);
+            exitSplitScreen(mTopStageAfterFoldDismiss == STAGE_TYPE_MAIN ? mMainStage : mSideStage,
+                    SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED);
         }
     }
 
-    void exitSplitScreen() {
-        exitSplitScreen(null /* childrenToTop */);
+    void exitSplitScreen(int exitReason) {
+        exitSplitScreen(null /* childrenToTop */, exitReason);
     }
 
     void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
         mExitSplitScreenOnHide = exitSplitScreenOnHide;
     }
 
-    private void exitSplitScreen(StageTaskListener childrenToTop) {
+    private void exitSplitScreen(StageTaskListener childrenToTop, int exitReason) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         mSideStage.removeAllTasks(wct, childrenToTop == mSideStage);
         mMainStage.deactivate(wct, childrenToTop == mMainStage);
@@ -469,6 +482,11 @@
         // Reset divider position.
         mSplitLayout.resetDividerPosition();
         mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
+        if (childrenToTop != null) {
+            logExitToStage(exitReason, childrenToTop == mMainStage);
+        } else {
+            logExit(exitReason);
+        }
     }
 
     /**
@@ -491,22 +509,8 @@
         opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, stage.mRootTaskInfo.token);
     }
 
-    void updateActivityOptions(Bundle opts, @SplitPosition int position,
-            @Nullable WindowContainerTransaction wct) {
+    void updateActivityOptions(Bundle opts, @SplitPosition int position) {
         addActivityOptions(opts, position == mSideStagePosition ? mSideStage : mMainStage);
-
-        if (!mMainStage.isActive()) {
-            // Activate the main stage in anticipation of an app launch.
-            boolean needsApply = wct == null;
-            if (needsApply) {
-                wct = new WindowContainerTransaction();
-            }
-            mMainStage.activate(getMainStageBounds(), wct);
-            mSideStage.setBounds(getSideStageBounds(), wct);
-            if (needsApply) {
-                mTaskOrganizer.applyTransaction(wct);
-            }
-        }
     }
 
     void registerSplitScreenListener(SplitScreen.SplitScreenListener listener) {
@@ -535,9 +539,8 @@
         }
     }
 
-    private void onStageChildTaskStatusChanged(
-            StageListenerImpl stageListener, int taskId, boolean present, boolean visible) {
-
+    private void onStageChildTaskStatusChanged(StageListenerImpl stageListener, int taskId,
+            boolean present, boolean visible) {
         int stage;
         if (present) {
             stage = stageListener == mSideStageListener ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN;
@@ -545,6 +548,13 @@
             // No longer on any stage
             stage = STAGE_TYPE_UNDEFINED;
         }
+        if (stage == STAGE_TYPE_MAIN) {
+            mLogger.logMainStageAppChange(getMainStagePosition(), mMainStage.getTopChildTaskUid(),
+                    mSplitLayout.isLandscape());
+        } else {
+            mLogger.logSideStageAppChange(getSideStagePosition(), mSideStage.getTopChildTaskUid(),
+                    mSplitLayout.isLandscape());
+        }
 
         for (int i = mListeners.size() - 1; i >= 0; --i) {
             mListeners.get(i).onTaskStageChanged(taskId, stage, visible);
@@ -611,7 +621,7 @@
             // Don't dismiss staged split when both stages are not visible due to sleeping display,
             // like the cases keyguard showing or screen off.
             || (!mMainStage.mRootTaskInfo.isSleeping && !mSideStage.mRootTaskInfo.isSleeping)) {
-                exitSplitScreen();
+                exitSplitScreen(SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME);
             }
         } else if (mKeyguardOccluded) {
             // At least one of the stages is visible while keyguard occluded. Dismiss split because
@@ -619,7 +629,7 @@
             // task contains show-when-locked activity remains on top after split dismissed.
             final StageTaskListener toTop =
                     mainStageVisible ? mMainStage : (sideStageVisible ? mSideStage : null);
-            exitSplitScreen(toTop);
+            exitSplitScreen(toTop, SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED_SHOW_ON_TOP);
         }
 
         // When both stage's visibility changed to visible, main stage might receives visibility
@@ -686,7 +696,7 @@
 
         if (mDividerVisible) {
             t.show(dividerLeash)
-                    .setLayer(dividerLeash, Integer.MAX_VALUE)
+                    .setLayer(dividerLeash, SPLIT_DIVIDER_LAYER)
                     .setPosition(dividerLeash,
                             mSplitLayout.getDividerBounds().left,
                             mSplitLayout.getDividerBounds().top);
@@ -696,17 +706,16 @@
 
     }
 
-
     private void onStageHasChildrenChanged(StageListenerImpl stageListener) {
         final boolean hasChildren = stageListener.mHasChildren;
         final boolean isSideStage = stageListener == mSideStageListener;
         if (!hasChildren) {
             if (isSideStage && mMainStageListener.mVisible) {
                 // Exit to main stage if side stage no longer has children.
-                exitSplitScreen(mMainStage);
+                exitSplitScreen(mMainStage, SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED);
             } else if (!isSideStage && mSideStageListener.mVisible) {
                 // Exit to side stage if main stage no longer has children.
-                exitSplitScreen(mSideStage);
+                exitSplitScreen(mSideStage, SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED);
             }
         } else if (isSideStage) {
             final WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -715,6 +724,13 @@
             mSideStage.setBounds(getSideStageBounds(), wct);
             mTaskOrganizer.applyTransaction(wct);
         }
+        if (!mLogger.hasStartedSession() && mMainStageListener.mHasChildren
+                && mSideStageListener.mHasChildren) {
+            mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
+                    getMainStagePosition(), mMainStage.getTopChildTaskUid(),
+                    getSideStagePosition(), mSideStage.getTopChildTaskUid(),
+                    mSplitLayout.isLandscape());
+        }
     }
 
     @VisibleForTesting
@@ -733,26 +749,31 @@
             onSnappedToDismissTransition(mainStageToTop);
             return;
         }
-        exitSplitScreen(mainStageToTop ? mMainStage : mSideStage);
+        exitSplitScreen(mainStageToTop ? mMainStage : mSideStage,
+                SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER);
     }
 
     @Override
     public void onDoubleTappedDivider() {
         setSideStagePosition(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
                 ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT, null /* wct */);
+        mLogger.logSwap(getMainStagePosition(), mMainStage.getTopChildTaskUid(),
+                getSideStagePosition(), mSideStage.getTopChildTaskUid(),
+                mSplitLayout.isLandscape());
     }
 
     @Override
-    public void onBoundsChanging(SplitLayout layout) {
+    public void onLayoutChanging(SplitLayout layout) {
         mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
     }
 
     @Override
-    public void onBoundsChanged(SplitLayout layout) {
+    public void onLayoutChanged(SplitLayout layout) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         updateWindowBounds(layout, wct);
         mSyncQueue.queue(wct);
         mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t));
+        mLogger.logResize(mSplitLayout.getDividerPositionAsFraction());
     }
 
     /**
@@ -792,6 +813,18 @@
     }
 
     @Override
+    public void onLayoutShifted(int offsetX, int offsetY, SplitLayout layout) {
+        final StageTaskListener topLeftStage =
+                mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
+        final StageTaskListener bottomRightStage =
+                mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+        layout.applyLayoutShifted(wct, offsetX, offsetY, topLeftStage.mRootTaskInfo,
+                bottomRightStage.mRootTaskInfo);
+        mTaskOrganizer.applyTransaction(wct);
+    }
+
+    @Override
     public void onDisplayAreaAppeared(DisplayAreaInfo displayAreaInfo) {
         mDisplayAreaInfo = displayAreaInfo;
         if (mSplitLayout == null) {
@@ -813,7 +846,7 @@
         if (mSplitLayout != null
                 && mSplitLayout.updateConfiguration(mDisplayAreaInfo.configuration)
                 && mMainStage.isActive()) {
-            onBoundsChanged(mSplitLayout);
+            onLayoutChanged(mSplitLayout);
             mSyncQueue.runInSync(t -> applyDividerVisibility(t));
         }
     }
@@ -1100,7 +1133,7 @@
         // Be default, make it visible. The remote animator can adjust alpha if it plans to animate.
         if (show) {
             t.setAlpha(leash, 1.f);
-            t.setLayer(leash, Integer.MAX_VALUE);
+            t.setLayer(leash, SPLIT_DIVIDER_LAYER);
             t.setPosition(leash, bounds.left, bounds.top);
             t.show(leash);
         }
@@ -1113,7 +1146,7 @@
                 null /* contentInsets */, Integer.MAX_VALUE /* prefixOrderIndex */,
                 new android.graphics.Point(0, 0) /* position */, bounds, bounds,
                 new WindowConfiguration(), true, null /* startLeash */, null /* startBounds */,
-                null /* taskInfo */, TYPE_DOCK_DIVIDER);
+                null /* taskInfo */, false /* allowEnterPip */, TYPE_DOCK_DIVIDER);
     }
 
     @Override
@@ -1140,6 +1173,36 @@
         mMainStageListener.mHasChildren = mSideStageListener.mHasChildren = visible;
     }
 
+    /**
+     * Sets drag info to be logged when splitscreen is next entered.
+     */
+    public void logOnDroppedToSplit(@SplitPosition int position, InstanceId dragSessionId) {
+        mLogger.enterRequestedByDrag(position, dragSessionId);
+    }
+
+    /**
+     * Logs the exit of splitscreen.
+     */
+    private void logExit(int exitReason) {
+        mLogger.logExit(exitReason,
+                SPLIT_POSITION_UNDEFINED, 0 /* mainStageUid */,
+                SPLIT_POSITION_UNDEFINED, 0 /* sideStageUid */,
+                mSplitLayout.isLandscape());
+    }
+
+    /**
+     * Logs the exit of splitscreen to a specific stage. This must be called before the exit is
+     * executed.
+     */
+    private void logExitToStage(int exitReason, boolean toMainStage) {
+        mLogger.logExit(exitReason,
+                toMainStage ? getMainStagePosition() : SPLIT_POSITION_UNDEFINED,
+                toMainStage ? mMainStage.getTopChildTaskUid() : 0 /* mainStageUid */,
+                !toMainStage ? getSideStagePosition() : SPLIT_POSITION_UNDEFINED,
+                !toMainStage ? mSideStage.getTopChildTaskUid() : 0 /* sideStageUid */,
+                mSplitLayout.isLandscape());
+    }
+
     class StageListenerImpl implements StageTaskListener.StageListenerCallbacks {
         boolean mHasRootTask = false;
         boolean mVisible = false;
@@ -1179,7 +1242,8 @@
         @Override
         public void onNoLongerSupportMultiWindow() {
             if (mMainStage.isActive()) {
-                StageCoordinator.this.exitSplitScreen();
+                StageCoordinator.this.exitSplitScreen(
+                        SPLITSCREEN_UICHANGED__EXIT_REASON__APP_DOES_NOT_SUPPORT_MULTIWINDOW);
             }
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index c47353a..3512a0c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -97,6 +97,20 @@
         return mChildrenTaskInfo.contains(taskId);
     }
 
+    /**
+     * Returns the top activity uid for the top child task.
+     */
+    int getTopChildTaskUid() {
+        for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
+            final ActivityManager.RunningTaskInfo info = mChildrenTaskInfo.valueAt(i);
+            if (info.topActivityInfo == null) {
+                continue;
+            }
+            return info.topActivityInfo.applicationInfo.uid;
+        }
+        return 0;
+    }
+
     /** @return {@code true} if this listener contains the currently focused task. */
     boolean isFocused() {
         if (mRootTaskInfo.isFocused) return true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index dff5577..2286598 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -25,6 +25,7 @@
 import android.annotation.ColorInt;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityThread;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -315,7 +316,7 @@
         private Drawable mOverlayDrawable;
         private int mSuggestType;
         private int mThemeColor;
-        private Drawable mFinalIconDrawable;
+        private Drawable[] mFinalIconDrawables;
         private int mFinalIconSize = mIconSize;
 
         StartingWindowViewBuilder(@NonNull Context context, @NonNull ActivityInfo aInfo) {
@@ -347,18 +348,19 @@
                 animationDuration = 0;
                 mFinalIconSize = 0;
             } else if (mTmpAttrs.mSplashScreenIcon != null) {
-                // replaced icon, don't process
+                // Using the windowSplashScreenAnimatedIcon attribute
                 iconDrawable = mTmpAttrs.mSplashScreenIcon;
                 animationDuration = mTmpAttrs.mAnimationDuration;
 
                 // There is no background below the icon, so scale the icon up
-                if (mTmpAttrs.mIconBgColor == Color.TRANSPARENT) {
+                if (mTmpAttrs.mIconBgColor == Color.TRANSPARENT
+                        || mTmpAttrs.mIconBgColor == mThemeColor) {
                     mFinalIconSize *= NO_BACKGROUND_SCALE;
                 }
                 createIconDrawable(iconDrawable, false);
             } else {
                 final float iconScale = (float) mIconSize / (float) mDefaultIconSize;
-                final int densityDpi = mContext.getResources().getDisplayMetrics().densityDpi;
+                final int densityDpi = mContext.getResources().getConfiguration().densityDpi;
                 final int scaledIconDpi =
                         (int) (0.5f + iconScale * densityDpi * NO_BACKGROUND_SCALE);
                 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "getIcon");
@@ -383,7 +385,7 @@
                 animationDuration = 0;
             }
 
-            return fillViewWithIcon(mFinalIconSize, mFinalIconDrawable, animationDuration);
+            return fillViewWithIcon(mFinalIconSize, mFinalIconDrawables, animationDuration);
         }
 
         private class ShapeIconFactory extends BaseIconFactory {
@@ -394,12 +396,11 @@
 
         private void createIconDrawable(Drawable iconDrawable, boolean legacy) {
             if (legacy) {
-                mFinalIconDrawable = SplashscreenIconDrawableFactory.makeLegacyIconDrawable(
+                mFinalIconDrawables = SplashscreenIconDrawableFactory.makeLegacyIconDrawable(
                         iconDrawable, mDefaultIconSize, mFinalIconSize, mSplashscreenWorkerHandler);
             } else {
-                mFinalIconDrawable = SplashscreenIconDrawableFactory.makeIconDrawable(
-                        mTmpAttrs.mIconBgColor != Color.TRANSPARENT
-                                ? mTmpAttrs.mIconBgColor : mThemeColor,
+                mFinalIconDrawables = SplashscreenIconDrawableFactory.makeIconDrawable(
+                        mTmpAttrs.mIconBgColor, mThemeColor,
                         iconDrawable, mDefaultIconSize, mFinalIconSize, mSplashscreenWorkerHandler);
             }
         }
@@ -461,18 +462,24 @@
             return true;
         }
 
-        private SplashScreenView fillViewWithIcon(int iconSize, Drawable iconDrawable,
+        private SplashScreenView fillViewWithIcon(int iconSize, @Nullable Drawable[] iconDrawable,
                 int animationDuration) {
-            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "fillViewWithIcon");
-            final SplashScreenView.Builder builder = new SplashScreenView.Builder(mContext);
-            builder.setBackgroundColor(mThemeColor);
-            builder.setOverlayDrawable(mOverlayDrawable);
+            Drawable foreground = null;
+            Drawable background = null;
             if (iconDrawable != null) {
-                builder.setIconSize(iconSize)
-                        .setIconBackground(mTmpAttrs.mIconBgColor)
-                        .setCenterViewDrawable(iconDrawable)
-                        .setAnimationDurationMillis(animationDuration);
+                foreground = iconDrawable.length > 0 ? iconDrawable[0] : null;
+                background = iconDrawable.length > 1 ? iconDrawable[1] : null;
             }
+
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "fillViewWithIcon");
+            final SplashScreenView.Builder builder = new SplashScreenView.Builder(mContext)
+                    .setBackgroundColor(mThemeColor)
+                    .setOverlayDrawable(mOverlayDrawable)
+                    .setIconSize(iconSize)
+                    .setIconBackground(background)
+                    .setCenterViewDrawable(foreground)
+                    .setAnimationDurationMillis(animationDuration);
+
             if (mSuggestType == STARTING_WINDOW_TYPE_SPLASH_SCREEN
                     && mTmpAttrs.mBrandingImage != null) {
                 builder.setBrandingDrawable(mTmpAttrs.mBrandingImage, mBrandingImageWidth,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
index ba9123d..951b97e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
@@ -22,9 +22,11 @@
 import android.animation.ValueAnimator;
 import android.annotation.ColorInt;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.ColorFilter;
 import android.graphics.Matrix;
 import android.graphics.Paint;
@@ -33,7 +35,6 @@
 import android.graphics.Rect;
 import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.Animatable;
-import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.Trace;
@@ -44,32 +45,55 @@
 
 /**
  * Creating a lightweight Drawable object used for splash screen.
+ *
  * @hide
  */
 public class SplashscreenIconDrawableFactory {
 
-    static Drawable makeIconDrawable(@ColorInt int backgroundColor,
+    /**
+     * @return An array containing the foreground drawable at index 0 and if needed a background
+     * drawable at index 1.
+     */
+    static Drawable[] makeIconDrawable(@ColorInt int backgroundColor, @ColorInt int themeColor,
             @NonNull Drawable foregroundDrawable, int srcIconSize, int iconSize,
             Handler splashscreenWorkerHandler) {
+        Drawable foreground;
+        Drawable background = null;
+        boolean drawBackground =
+                backgroundColor != Color.TRANSPARENT && backgroundColor != themeColor;
+
         if (foregroundDrawable instanceof Animatable) {
-            return new AnimatableIconAnimateListener(backgroundColor, foregroundDrawable);
+            foreground = new AnimatableIconAnimateListener(foregroundDrawable);
         } else if (foregroundDrawable instanceof AdaptiveIconDrawable) {
-            return new ImmobileIconDrawable(foregroundDrawable,
+            // If the icon is Adaptive, we already use the icon background.
+            drawBackground = false;
+            foreground = new ImmobileIconDrawable(foregroundDrawable,
                     srcIconSize, iconSize, splashscreenWorkerHandler);
         } else {
-            // single layer icon
-            return new ImmobileIconDrawable(new AdaptiveIconDrawable(
-                    new ColorDrawable(backgroundColor), foregroundDrawable),
+            // Adaptive icon don't handle transparency so we draw the background of the adaptive
+            // icon with the same color as the window background color instead of using two layers
+            foreground = new ImmobileIconDrawable(
+                    new AdaptiveForegroundDrawable(foregroundDrawable),
                     srcIconSize, iconSize, splashscreenWorkerHandler);
         }
+
+        if (drawBackground) {
+            background = new MaskBackgroundDrawable(backgroundColor);
+        }
+
+        return new Drawable[]{foreground, background};
     }
 
-    static Drawable makeLegacyIconDrawable(@NonNull Drawable iconDrawable, int srcIconSize,
+    static Drawable[] makeLegacyIconDrawable(@NonNull Drawable iconDrawable, int srcIconSize,
             int iconSize, Handler splashscreenWorkerHandler) {
-        return new ImmobileIconDrawable(iconDrawable, srcIconSize, iconSize,
-                splashscreenWorkerHandler);
+        return new Drawable[]{new ImmobileIconDrawable(iconDrawable, srcIconSize, iconSize,
+                splashscreenWorkerHandler)};
     }
 
+    /**
+     * Drawable pre-drawing the scaled icon in a separate thread to increase the speed of the
+     * final drawing.
+     */
     private static class ImmobileIconDrawable extends Drawable {
         private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG
                 | Paint.FILTER_BITMAP_FLAG);
@@ -121,8 +145,10 @@
         }
     }
 
-    // Base class the draw the circle background
-    private abstract static class MaskBackgroundDrawable extends Drawable {
+    /**
+     * Base class the draw a background clipped by the system mask.
+     */
+    public static class MaskBackgroundDrawable extends Drawable {
         private static final float MASK_SIZE = AdaptiveIconDrawable.MASK_SIZE;
         private static final float EXTRA_INSET_PERCENTAGE = 1 / 4f;
         static final float DEFAULT_VIEW_PORT_SCALE = 1f / (1 + 2 * EXTRA_INSET_PERCENTAGE);
@@ -132,17 +158,24 @@
         private static Path sMask;
         private final Path mMaskScaleOnly;
         private final Matrix mMaskMatrix;
-        private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG
-                | Paint.FILTER_BITMAP_FLAG);
 
-        MaskBackgroundDrawable(@ColorInt int backgroundColor) {
+        @Nullable
+        private final Paint mBackgroundPaint;
+
+        public MaskBackgroundDrawable(@ColorInt int backgroundColor) {
             final Resources r = Resources.getSystem();
             sMask = PathParser.createPathFromPathData(r.getString(R.string.config_icon_mask));
             Path mask = new Path(sMask);
             mMaskScaleOnly = new Path(mask);
             mMaskMatrix = new Matrix();
-            mPaint.setColor(backgroundColor);
-            mPaint.setStyle(Paint.Style.FILL);
+            if (backgroundColor != Color.TRANSPARENT) {
+                mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG
+                        | Paint.FILTER_BITMAP_FLAG);
+                mBackgroundPaint.setColor(backgroundColor);
+                mBackgroundPaint.setStyle(Paint.Style.FILL);
+            } else {
+                mBackgroundPaint = null;
+            }
         }
 
         @Override
@@ -162,40 +195,80 @@
         @Override
         public void draw(Canvas canvas) {
             canvas.clipPath(mMaskScaleOnly);
-            if (mMaskScaleOnly != null) {
-                canvas.drawPath(mMaskScaleOnly, mPaint);
+            if (mBackgroundPaint != null) {
+                canvas.drawPath(mMaskScaleOnly, mBackgroundPaint);
             }
         }
 
         @Override
         public void setAlpha(int alpha) {
-            mPaint.setAlpha(alpha);
+            if (mBackgroundPaint != null) {
+                mBackgroundPaint.setAlpha(alpha);
+            }
         }
 
         @Override
         public int getOpacity() {
             return PixelFormat.RGBA_8888;
         }
+
+        @Override
+        public void setColorFilter(ColorFilter colorFilter) {
+        }
+    }
+
+    private static class AdaptiveForegroundDrawable extends MaskBackgroundDrawable {
+
+        @NonNull
+        protected final Drawable mForegroundDrawable;
+        private final Rect mTmpOutRect = new Rect();
+
+        AdaptiveForegroundDrawable(@NonNull Drawable foregroundDrawable) {
+            super(Color.TRANSPARENT);
+            mForegroundDrawable = foregroundDrawable;
+        }
+
+        @Override
+        protected void updateLayerBounds(Rect bounds) {
+            super.updateLayerBounds(bounds);
+            int cX = bounds.width() / 2;
+            int cY = bounds.height() / 2;
+
+            int insetWidth = (int) (bounds.width() / (DEFAULT_VIEW_PORT_SCALE * 2));
+            int insetHeight = (int) (bounds.height() / (DEFAULT_VIEW_PORT_SCALE * 2));
+            final Rect outRect = mTmpOutRect;
+            outRect.set(cX - insetWidth, cY - insetHeight, cX + insetWidth, cY + insetHeight);
+            if (mForegroundDrawable != null) {
+                mForegroundDrawable.setBounds(outRect);
+            }
+            invalidateSelf();
+        }
+
+        @Override
+        public void draw(Canvas canvas) {
+            super.draw(canvas);
+            mForegroundDrawable.draw(canvas);
+        }
+
+        @Override
+        public void setColorFilter(ColorFilter colorFilter) {
+            mForegroundDrawable.setColorFilter(colorFilter);
+        }
     }
 
     /**
      * A lightweight AdaptiveIconDrawable which support foreground to be Animatable, and keep this
      * drawable masked by config_icon_mask.
      */
-    private static class AnimatableIconAnimateListener extends MaskBackgroundDrawable
+    private static class AnimatableIconAnimateListener extends AdaptiveForegroundDrawable
             implements SplashScreenView.IconAnimateListener {
-        private final Drawable mForegroundDrawable;
         private Animatable mAnimatableIcon;
         private Animator mIconAnimator;
         private boolean mAnimationTriggered;
-        private final Rect mTmpOutRect = new Rect();
 
-        AnimatableIconAnimateListener(@ColorInt int backgroundColor, Drawable foregroundDrawable) {
-            super(backgroundColor);
-            mForegroundDrawable = foregroundDrawable;
-            if (mForegroundDrawable != null) {
-                mForegroundDrawable.setCallback(mCallback);
-            }
+        AnimatableIconAnimateListener(@NonNull Drawable foregroundDrawable) {
+            super(foregroundDrawable);
+            mForegroundDrawable.setCallback(mCallback);
         }
 
         @Override
@@ -231,22 +304,6 @@
             return true;
         }
 
-        @Override
-        protected void updateLayerBounds(Rect bounds) {
-            super.updateLayerBounds(bounds);
-            int cX = bounds.width() / 2;
-            int cY = bounds.height() / 2;
-
-            int insetWidth = (int) (bounds.width() / (DEFAULT_VIEW_PORT_SCALE * 2));
-            int insetHeight = (int) (bounds.height() / (DEFAULT_VIEW_PORT_SCALE * 2));
-            final Rect outRect = mTmpOutRect;
-            outRect.set(cX - insetWidth, cY - insetHeight, cX + insetWidth, cY + insetHeight);
-            if (mForegroundDrawable != null) {
-                mForegroundDrawable.setBounds(outRect);
-            }
-            invalidateSelf();
-        }
-
         private final Callback mCallback = new Callback() {
             @Override
             public void invalidateDrawable(@NonNull Drawable who) {
@@ -276,19 +333,8 @@
 
         @Override
         public void draw(Canvas canvas) {
+            ensureAnimationStarted();
             super.draw(canvas);
-            if (mForegroundDrawable != null) {
-                ensureAnimationStarted();
-                mForegroundDrawable.draw(canvas);
-            }
-        }
-
-        @Override
-        public void setColorFilter(ColorFilter colorFilter) {
-            if (mForegroundDrawable != null) {
-                mForegroundDrawable.setColorFilter(colorFilter);
-            }
         }
     }
-
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index fc7c86d..52a3ac5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -52,6 +52,7 @@
 import android.view.SurfaceControlViewHost;
 import android.view.View;
 import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
 import android.widget.FrameLayout;
 import android.window.SplashScreenView;
 import android.window.SplashScreenView.SplashScreenViewParcelable;
@@ -115,6 +116,7 @@
     @VisibleForTesting
     final SplashscreenContentDrawer mSplashscreenContentDrawer;
     private Choreographer mChoreographer;
+    private final WindowManagerGlobal mWindowManagerGlobal;
 
     /**
      * @param splashScreenExecutor The thread used to control add and remove starting window.
@@ -126,6 +128,8 @@
         mSplashScreenExecutor = splashScreenExecutor;
         mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext, pool);
         mSplashScreenExecutor.execute(() -> mChoreographer = Choreographer.getInstance());
+        mWindowManagerGlobal = WindowManagerGlobal.getInstance();
+        mDisplayManager.getDisplay(DEFAULT_DISPLAY);
     }
 
     private final SparseArray<StartingWindowRecord> mStartingWindowRecords = new SparseArray<>();
@@ -137,21 +141,8 @@
     private final SparseArray<SurfaceControlViewHost> mAnimatedSplashScreenSurfaceHosts =
             new SparseArray<>(1);
 
-    /** Obtain proper context for showing splash screen on the provided display. */
-    private Context getDisplayContext(Context context, int displayId) {
-        if (displayId == DEFAULT_DISPLAY) {
-            // The default context fits.
-            return context;
-        }
-
-        final Display targetDisplay = mDisplayManager.getDisplay(displayId);
-        if (targetDisplay == null) {
-            // Failed to obtain the non-default display where splash screen should be shown,
-            // lets not show at all.
-            return null;
-        }
-
-        return context.createDisplayContext(targetDisplay);
+    private Display getDisplay(int displayId) {
+        return mDisplayManager.getDisplay(displayId);
     }
 
     private int getSplashScreenTheme(int splashScreenThemeResId, ActivityInfo activityInfo) {
@@ -186,13 +177,11 @@
                     + " suggestType=" + suggestType);
         }
 
-        // Obtain proper context to launch on the right display.
-        final Context displayContext = getDisplayContext(context, displayId);
-        if (displayContext == null) {
+        final Display display = getDisplay(displayId);
+        if (display == null) {
             // Can't show splash screen on requested display, so skip showing at all.
             return;
         }
-        context = displayContext;
         if (theme != context.getThemeResId()) {
             try {
                 context = context.createPackageContextAsUser(activityInfo.packageName,
@@ -330,10 +319,8 @@
         };
         mSplashscreenContentDrawer.createContentView(context, suggestType, activityInfo, taskId,
                 viewSupplier::setView);
-
         try {
-            final WindowManager wm = context.getSystemService(WindowManager.class);
-            if (addWindow(taskId, appToken, rootLayout, wm, params, suggestType)) {
+            if (addWindow(taskId, appToken, rootLayout, display, params, suggestType)) {
                 // We use the splash screen worker thread to create SplashScreenView while adding
                 // the window, as otherwise Choreographer#doFrame might be delayed on this thread.
                 // And since Choreographer#doFrame won't happen immediately after adding the window,
@@ -508,12 +495,14 @@
         viewHost.getView().post(viewHost::release);
     }
 
-    protected boolean addWindow(int taskId, IBinder appToken, View view, WindowManager wm,
+    protected boolean addWindow(int taskId, IBinder appToken, View view, Display display,
             WindowManager.LayoutParams params, @StartingWindowType int suggestType) {
         boolean shouldSaveView = true;
+        final Context context = view.getContext();
         try {
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addRootView");
-            wm.addView(view, params);
+            mWindowManagerGlobal.addView(view, params, display,
+                    null /* parentWindow */, context.getUserId());
         } catch (WindowManager.BadTokenException e) {
             // ignore
             Slog.w(TAG, appToken + " already running, starting window not displayed. "
@@ -521,9 +510,9 @@
             shouldSaveView = false;
         } finally {
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
-            if (view != null && view.getParent() == null) {
+            if (view.getParent() == null) {
                 Slog.w(TAG, "view not successfully added to wm, removing view");
-                wm.removeViewImmediate(view);
+                mWindowManagerGlobal.removeView(view, true /* immediate */);
                 shouldSaveView = false;
             }
         }
@@ -587,10 +576,7 @@
         if (hideView) {
             decorView.setVisibility(View.GONE);
         }
-        final WindowManager wm = decorView.getContext().getSystemService(WindowManager.class);
-        if (wm != null) {
-            wm.removeView(decorView);
-        }
+        mWindowManagerGlobal.removeView(decorView, false /* immediate */);
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 01134a7..4ba6aca 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -23,16 +23,21 @@
 import static android.app.ActivityOptions.ANIM_SCALE_UP;
 import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
 import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_CLOSE;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_RELAUNCH;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS;
 import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
 import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION;
+import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
 import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
 import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
 import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
@@ -68,9 +73,12 @@
 import android.window.WindowContainerTransaction;
 
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.AttributeCache;
 import com.android.internal.policy.TransitionAnimation;
 import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
@@ -97,6 +105,7 @@
             SystemProperties.getBoolean(DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY, true);
 
     private final TransactionPool mTransactionPool;
+    private final DisplayController mDisplayController;
     private final Context mContext;
     private final ShellExecutor mMainExecutor;
     private final ShellExecutor mAnimExecutor;
@@ -114,8 +123,10 @@
 
     private ScreenRotationAnimation mRotationAnimation;
 
-    DefaultTransitionHandler(@NonNull TransactionPool transactionPool, Context context,
+    DefaultTransitionHandler(@NonNull DisplayController displayController,
+            @NonNull TransactionPool transactionPool, Context context,
             @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
+        mDisplayController = displayController;
         mTransactionPool = transactionPool;
         mContext = context;
         mMainExecutor = mainExecutor;
@@ -126,6 +137,110 @@
         AttributeCache.init(context);
     }
 
+    @VisibleForTesting
+    static boolean isRotationSeamless(@NonNull TransitionInfo info,
+            DisplayController displayController) {
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+                "Display is rotating, check if it should be seamless.");
+        boolean checkedDisplayLayout = false;
+        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+            final TransitionInfo.Change change = info.getChanges().get(i);
+
+            // Only look at changing things. showing/hiding don't need to rotate.
+            if (change.getMode() != TRANSIT_CHANGE) continue;
+
+            // This container isn't rotating, so we can ignore it.
+            if (change.getEndRotation() == change.getStartRotation()) continue;
+
+            if ((change.getFlags() & FLAG_IS_DISPLAY) != 0) {
+                // In the presence of System Alert windows we can not seamlessly rotate.
+                if ((change.getFlags() & FLAG_DISPLAY_HAS_ALERT_WINDOWS) != 0) {
+                    ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+                            "  display has system alert windows, so not seamless.");
+                    return false;
+                }
+            } else if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
+                if (change.getRotationAnimation() != ROTATION_ANIMATION_SEAMLESS) {
+                    ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+                            "  wallpaper is participating but isn't seamless.");
+                    return false;
+                }
+            } else if (change.getTaskInfo() != null) {
+                // We only enable seamless rotation if all the visible task windows requested it.
+                if (change.getRotationAnimation() != ROTATION_ANIMATION_SEAMLESS) {
+                    ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+                            "  task %s isn't requesting seamless, so not seamless.",
+                            change.getTaskInfo().taskId);
+                    return false;
+                }
+
+                // This is the only way to get display-id currently, so we will check display
+                // capabilities here
+                if (!checkedDisplayLayout) {
+                    // only need to check display once.
+                    checkedDisplayLayout = true;
+                    final DisplayLayout displayLayout = displayController.getDisplayLayout(
+                            change.getTaskInfo().displayId);
+                    // For the upside down rotation we don't rotate seamlessly as the navigation
+                    // bar moves position. Note most apps (using orientation:sensor or user as
+                    // opposed to fullSensor) will not enter the reverse portrait orientation, so
+                    // actually the orientation won't change at all.
+                    int upsideDownRotation = displayLayout.getUpsideDownRotation();
+                    if (change.getStartRotation() == upsideDownRotation
+                            || change.getEndRotation() == upsideDownRotation) {
+                        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+                                "  rotation involves upside-down portrait, so not seamless.");
+                        return false;
+                    }
+
+                    // If the navigation bar can't change sides, then it will jump when we change
+                    // orientations and we don't rotate seamlessly - unless that is allowed, eg.
+                    // with gesture navigation where the navbar is low-profile enough that this
+                    // isn't very noticeable.
+                    if (!displayLayout.allowSeamlessRotationDespiteNavBarMoving()
+                            && (!(displayLayout.navigationBarCanMove()
+                                    && (change.getStartAbsBounds().width()
+                                            != change.getStartAbsBounds().height())))) {
+                        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+                                "  nav bar changes sides, so not seamless.");
+                        return false;
+                    }
+                }
+            }
+        }
+
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  Rotation IS seamless.");
+        return true;
+    }
+
+    /**
+     * Gets the rotation animation for the topmost task. Assumes that seamless is checked
+     * elsewhere, so it will default SEAMLESS to ROTATE.
+     */
+    private int getRotationAnimation(@NonNull TransitionInfo info) {
+        // Traverse in top-to-bottom order so that the first task is top-most
+        for (int i = 0; i < info.getChanges().size(); ++i) {
+            final TransitionInfo.Change change = info.getChanges().get(i);
+
+            // Only look at changing things. showing/hiding don't need to rotate.
+            if (change.getMode() != TRANSIT_CHANGE) continue;
+
+            // This container isn't rotating, so we can ignore it.
+            if (change.getEndRotation() == change.getStartRotation()) continue;
+
+            if (change.getTaskInfo() != null) {
+                final int anim = change.getRotationAnimation();
+                if (anim == ROTATION_ANIMATION_UNSPECIFIED
+                        // Fallback animation for seamless should also be default.
+                        || anim == ROTATION_ANIMATION_SEAMLESS) {
+                    return ROTATION_ANIMATION_ROTATE;
+                }
+                return anim;
+            }
+        }
+        return ROTATION_ANIMATION_ROTATE;
+    }
+
     @Override
     public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction startTransaction,
@@ -133,10 +248,9 @@
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
                 "start default transition animation, info = %s", info);
-
-        // Fallback for screen wake. This just immediately finishes since there is no
-        // animation for screen-wake.
-        if (info.getType() == WindowManager.TRANSIT_WAKE) {
+        // If keyguard goes away, we should loadKeyguardExitAnimation. Otherwise this just
+        // immediately finishes since there is no animation for screen-wake.
+        if (info.getType() == WindowManager.TRANSIT_WAKE && !info.isKeyguardGoingAway()) {
             startTransaction.apply();
             finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
             return true;
@@ -168,11 +282,15 @@
             if (info.getType() == TRANSIT_CHANGE && change.getMode() == TRANSIT_CHANGE
                     && (change.getEndRotation() != change.getStartRotation())
                     && (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
-                mRotationAnimation = new ScreenRotationAnimation(mContext, mSurfaceSession,
-                        mTransactionPool, startTransaction, change, info.getRootLeash());
-                mRotationAnimation.startAnimation(animations, onAnimFinish,
-                        mTransitionAnimationScaleSetting, mMainExecutor, mAnimExecutor);
-                continue;
+                boolean isSeamless = isRotationSeamless(info, mDisplayController);
+                final int anim = getRotationAnimation(info);
+                if (!(isSeamless || anim == ROTATION_ANIMATION_JUMPCUT)) {
+                    mRotationAnimation = new ScreenRotationAnimation(mContext, mSurfaceSession,
+                            mTransactionPool, startTransaction, change, info.getRootLeash());
+                    mRotationAnimation.startAnimation(animations, onAnimFinish,
+                            mTransitionAnimationScaleSetting, mMainExecutor, mAnimExecutor);
+                    continue;
+                }
             }
 
             if (change.getMode() == TRANSIT_CHANGE) {
@@ -234,7 +352,7 @@
         final int overrideType = options != null ? options.getType() : ANIM_NONE;
         final boolean canCustomContainer = isTask ? !sDisableCustomTaskAnimationProperty : true;
 
-        if (type == TRANSIT_KEYGUARD_GOING_AWAY) {
+        if (info.isKeyguardGoingAway()) {
             a = mTransitionAnimation.loadKeyguardExitAnimation(flags,
                     (changeFlags & FLAG_SHOW_WALLPAPER) != 0);
         } else if (type == TRANSIT_KEYGUARD_UNOCCLUDE) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
index a13b03d..ada2ed2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
@@ -19,6 +19,7 @@
 import static android.hardware.HardwareBuffer.RGBA_8888;
 import static android.hardware.HardwareBuffer.USAGE_PROTECTED_CONTENT;
 import static android.util.RotationUtils.deltaRotation;
+import static android.view.WindowManagerPolicyConstants.SCREEN_FREEZE_LAYER_BASE;
 
 import static com.android.wm.shell.transition.DefaultTransitionHandler.startSurfaceAnimation;
 import static com.android.wm.shell.transition.Transitions.TAG;
@@ -81,19 +82,6 @@
  * </ul>
  */
 class ScreenRotationAnimation {
-
-    /** How much to multiply the policy's type layer, to reserve room
-     * for multiple windows of the same type and Z-ordering adjustment
-     * with TYPE_LAYER_OFFSET. */
-    static final int TYPE_LAYER_MULTIPLIER = 10000;
-    static final int WINDOW_FREEZE_LAYER = TYPE_LAYER_MULTIPLIER * 200;
-
-    /*
-     * Layers for screen rotation animation. We put these layers above
-     * WINDOW_FREEZE_LAYER so that screen freeze will cover all windows.
-     */
-    private static final int SCREEN_FREEZE_LAYER_BASE = WINDOW_FREEZE_LAYER + TYPE_LAYER_MULTIPLIER;
-
     static final int MAX_ANIMATION_DURATION = 10 * 1000;
 
     private final Context mContext;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 01ef2a6..8d21ce2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -19,6 +19,7 @@
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -54,6 +55,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.RemoteCallable;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.TransactionPool;
@@ -113,15 +115,16 @@
     private final ArrayList<ActiveTransition> mActiveTransitions = new ArrayList<>();
 
     public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool,
-            @NonNull Context context, @NonNull ShellExecutor mainExecutor,
-            @NonNull ShellExecutor animExecutor) {
+            @NonNull DisplayController displayController, @NonNull Context context,
+            @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
         mOrganizer = organizer;
         mContext = context;
         mMainExecutor = mainExecutor;
         mAnimExecutor = animExecutor;
         mPlayerImpl = new TransitionPlayerImpl();
         // The very last handler (0 in the list) should be the default one.
-        mHandlers.add(new DefaultTransitionHandler(pool, context, mainExecutor, animExecutor));
+        mHandlers.add(new DefaultTransitionHandler(displayController, pool, context, mainExecutor,
+                animExecutor));
         // Next lowest priority is remote transitions.
         mRemoteTransitionHandler = new RemoteTransitionHandler(mainExecutor);
         mHandlers.add(mRemoteTransitionHandler);
@@ -228,7 +231,7 @@
     public static boolean isOpeningType(@WindowManager.TransitionType int type) {
         return type == TRANSIT_OPEN
                 || type == TRANSIT_TO_FRONT
-                || type == WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+                || type == TRANSIT_KEYGUARD_GOING_AWAY;
     }
 
     /** @return true if the transition was triggered by closing something vs opening something */
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index 9dd25fe..3ca5b9c 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -25,11 +25,17 @@
 
 android_test {
     name: "WMShellFlickerTests",
-    srcs: ["src/**/*.java", "src/**/*.kt"],
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
     manifest: "AndroidManifest.xml",
     test_config: "AndroidTest.xml",
     platform_apis: true,
     certificate: "platform",
+    optimize: {
+        enabled: false,
+    },
     test_suites: ["device-tests"],
     libs: ["android.test.runner"],
     static_libs: [
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index c5b5b91..b36468b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -16,97 +16,100 @@
 
 package com.android.wm.shell.flicker
 
+import android.content.ComponentName
 import android.graphics.Region
 import android.view.Surface
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.traces.layers.getVisibleBounds
 
-fun FlickerTestParameter.appPairsDividerIsVisible() {
+fun FlickerTestParameter.appPairsDividerIsVisibleAtEnd() {
     assertLayersEnd {
-        this.isVisible(APP_PAIR_SPLIT_DIVIDER)
+        this.isVisible(APP_PAIR_SPLIT_DIVIDER_COMPONENT)
     }
 }
 
-fun FlickerTestParameter.appPairsDividerIsInvisible() {
+fun FlickerTestParameter.appPairsDividerIsInvisibleAtEnd() {
     assertLayersEnd {
-        this.notContains(APP_PAIR_SPLIT_DIVIDER)
+        this.notContains(APP_PAIR_SPLIT_DIVIDER_COMPONENT)
     }
 }
 
 fun FlickerTestParameter.appPairsDividerBecomesVisible() {
     assertLayers {
-        this.isInvisible(DOCKED_STACK_DIVIDER)
+        this.isInvisible(DOCKED_STACK_DIVIDER_COMPONENT)
             .then()
-            .isVisible(DOCKED_STACK_DIVIDER)
+            .isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
     }
 }
 
-fun FlickerTestParameter.dockedStackDividerIsVisible() {
+fun FlickerTestParameter.dockedStackDividerIsVisibleAtEnd() {
     assertLayersEnd {
-        this.isVisible(DOCKED_STACK_DIVIDER)
+        this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
     }
 }
 
 fun FlickerTestParameter.dockedStackDividerBecomesVisible() {
     assertLayers {
-        this.isInvisible(DOCKED_STACK_DIVIDER)
+        this.isInvisible(DOCKED_STACK_DIVIDER_COMPONENT)
             .then()
-            .isVisible(DOCKED_STACK_DIVIDER)
+            .isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
     }
 }
 
 fun FlickerTestParameter.dockedStackDividerBecomesInvisible() {
     assertLayers {
-        this.isVisible(DOCKED_STACK_DIVIDER)
+        this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
             .then()
-            .isInvisible(DOCKED_STACK_DIVIDER)
+            .isInvisible(DOCKED_STACK_DIVIDER_COMPONENT)
     }
 }
 
-fun FlickerTestParameter.dockedStackDividerIsInvisible() {
+fun FlickerTestParameter.dockedStackDividerNotExistsAtEnd() {
     assertLayersEnd {
-        this.notContains(DOCKED_STACK_DIVIDER)
+        this.notContains(DOCKED_STACK_DIVIDER_COMPONENT)
     }
 }
 
-fun FlickerTestParameter.appPairsPrimaryBoundsIsVisible(rotation: Int, primaryLayerName: String) {
+fun FlickerTestParameter.appPairsPrimaryBoundsIsVisibleAtEnd(
+    rotation: Int,
+    primaryComponent: ComponentName
+) {
     assertLayersEnd {
-        val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
-        visibleRegion(primaryLayerName)
+        val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
+        visibleRegion(primaryComponent)
             .coversExactly(getPrimaryRegion(dividerRegion, rotation))
     }
 }
 
-fun FlickerTestParameter.dockedStackPrimaryBoundsIsVisible(
+fun FlickerTestParameter.dockedStackPrimaryBoundsIsVisibleAtEnd(
     rotation: Int,
-    primaryLayerName: String
+    primaryComponent: ComponentName
 ) {
     assertLayersEnd {
-        val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
-        visibleRegion(primaryLayerName)
+        val dividerRegion = layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region
+        visibleRegion(primaryComponent)
             .coversExactly(getPrimaryRegion(dividerRegion, rotation))
     }
 }
 
-fun FlickerTestParameter.appPairsSecondaryBoundsIsVisible(
+fun FlickerTestParameter.appPairsSecondaryBoundsIsVisibleAtEnd(
     rotation: Int,
-    secondaryLayerName: String
+    secondaryComponent: ComponentName
 ) {
     assertLayersEnd {
-        val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
-        visibleRegion(secondaryLayerName)
+        val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
+        visibleRegion(secondaryComponent)
             .coversExactly(getSecondaryRegion(dividerRegion, rotation))
     }
 }
 
-fun FlickerTestParameter.dockedStackSecondaryBoundsIsVisible(
+fun FlickerTestParameter.dockedStackSecondaryBoundsIsVisibleAtEnd(
     rotation: Int,
-    secondaryLayerName: String
+    secondaryComponent: ComponentName
 ) {
     assertLayersEnd {
-        val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
-        visibleRegion(secondaryLayerName)
+        val dividerRegion = layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region
+        visibleRegion(secondaryComponent)
             .coversExactly(getSecondaryRegion(dividerRegion, rotation))
     }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
index 03b93c7..ff1a6e6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt
@@ -14,9 +14,11 @@
  * limitations under the License.
  */
 
+@file:JvmName("CommonConstants")
 package com.android.wm.shell.flicker
 
-const val IME_WINDOW_NAME = "InputMethod"
+import android.content.ComponentName
+
 const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui"
-const val APP_PAIR_SPLIT_DIVIDER = "AppPairSplitDivider"
-const val DOCKED_STACK_DIVIDER = "DockedStackDivider"
\ No newline at end of file
+val APP_PAIR_SPLIT_DIVIDER_COMPONENT = ComponentName("", "AppPairSplitDivider#")
+val DOCKED_STACK_DIVIDER_COMPONENT = ComponentName("", "DockedStackDivider#")
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
index ef9f742..19374ed 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.apppairs
 
-import android.os.SystemClock
 import android.platform.test.annotations.Presubmit
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
@@ -25,7 +24,7 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group1
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.wm.shell.flicker.appPairsDividerIsInvisible
+import com.android.wm.shell.flicker.appPairsDividerIsInvisibleAtEnd
 import com.android.wm.shell.flicker.helpers.AppPairsHelper
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
@@ -61,7 +60,7 @@
                 // TODO pair apps through normal UX flow
                 executeShellCommand(
                     composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true))
-                SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+                nonResizeableApp?.run { wmHelper.waitForFullScreenApp(nonResizeableApp.component) }
             }
         }
 
@@ -85,15 +84,13 @@
     @Test
     override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
 
-    @FlakyTest
+    @Presubmit
     @Test
-    override fun navBarLayerIsAlwaysVisible() {
-        super.navBarLayerIsAlwaysVisible()
-    }
+    override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
 
     @Presubmit
     @Test
-    fun appPairsDividerIsInvisible() = testSpec.appPairsDividerIsInvisible()
+    fun appPairsDividerIsInvisibleAtEnd() = testSpec.appPairsDividerIsInvisibleAtEnd()
 
     @Presubmit
     @Test
@@ -103,8 +100,8 @@
             "Non resizeable app not initialized"
         }
         testSpec.assertWmEnd {
-            isVisible(nonResizeableApp.defaultWindowName)
-            isInvisible(primaryApp.defaultWindowName)
+            isVisible(nonResizeableApp.component)
+            isInvisible(primaryApp.component)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
index db63c4c..46ee892 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.apppairs
 
-import android.os.SystemClock
 import android.platform.test.annotations.Presubmit
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
@@ -25,10 +24,10 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group1
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.traces.layers.getVisibleBounds
-import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER
-import com.android.wm.shell.flicker.appPairsDividerIsVisible
+import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -54,10 +53,14 @@
                 // TODO pair apps through normal UX flow
                 executeShellCommand(
                     composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
-                SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+                waitAppsShown(primaryApp, secondaryApp)
             }
         }
 
+    @Presubmit
+    @Test
+    override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+
     @FlakyTest
     @Test
     override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
@@ -68,14 +71,14 @@
 
     @Presubmit
     @Test
-    fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
+    fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd()
 
     @Presubmit
     @Test
     fun bothAppWindowsVisible() {
         testSpec.assertWmEnd {
-            isVisible(primaryApp.defaultWindowName)
-            isVisible(secondaryApp.defaultWindowName)
+            isVisible(primaryApp.component)
+            isVisible(secondaryApp.component)
         }
     }
 
@@ -83,10 +86,10 @@
     @Test
     fun appsEndingBounds() {
         testSpec.assertLayersEnd {
-            val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
-            visibleRegion(primaryApp.defaultWindowName)
+            val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
+            visibleRegion(primaryApp.component)
                 .coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion))
-            visibleRegion(secondaryApp.defaultWindowName)
+            visibleRegion(secondaryApp.component)
                 .coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion))
         }
     }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
index c8d3423..f7ced71 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.apppairs
 
-import android.os.SystemClock
 import android.platform.test.annotations.Presubmit
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
@@ -25,7 +24,7 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group1
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.wm.shell.flicker.appPairsDividerIsVisible
+import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.AppPairsHelper
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
@@ -61,7 +60,7 @@
                 // TODO pair apps through normal UX flow
                 executeShellCommand(
                         composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true))
-                SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+                nonResizeableApp?.run { wmHelper.waitForFullScreenApp(nonResizeableApp.component) }
             }
         }
 
@@ -77,6 +76,10 @@
         resetMultiWindowConfig(instrumentation)
     }
 
+    @Presubmit
+    @Test
+    override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+
     @FlakyTest
     @Test
     override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
@@ -87,7 +90,7 @@
 
     @Presubmit
     @Test
-    fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
+    fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd()
 
     @Presubmit
     @Test
@@ -97,8 +100,8 @@
             "Non resizeable app not initialized"
         }
         testSpec.assertWmEnd {
-            isVisible(nonResizeableApp.defaultWindowName)
-            isVisible(primaryApp.defaultWindowName)
+            isVisible(nonResizeableApp.component)
+            isVisible(primaryApp.component)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
index 83df836..3debdd3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
@@ -25,10 +25,10 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group1
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.traces.layers.getVisibleBounds
-import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER
-import com.android.wm.shell.flicker.appPairsDividerIsInvisible
+import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.appPairsDividerIsInvisibleAtEnd
 import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -51,9 +51,11 @@
         get() = {
             super.transition(this, it)
             setup {
-                executeShellCommand(
-                    composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
-                SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+                eachRun {
+                    executeShellCommand(
+                            composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
+                    waitAppsShown(primaryApp, secondaryApp)
+                }
             }
             transitions {
                 // TODO pair apps through normal UX flow
@@ -73,14 +75,14 @@
 
     @Presubmit
     @Test
-    fun appPairsDividerIsInvisible() = testSpec.appPairsDividerIsInvisible()
+    fun appPairsDividerIsInvisibleAtEnd() = testSpec.appPairsDividerIsInvisibleAtEnd()
 
     @Presubmit
     @Test
     fun bothAppWindowsInvisible() {
         testSpec.assertWmEnd {
-            isInvisible(primaryApp.defaultWindowName)
-            isInvisible(secondaryApp.defaultWindowName)
+            isInvisible(primaryApp.component)
+            isInvisible(secondaryApp.component)
         }
     }
 
@@ -88,10 +90,10 @@
     @Test
     fun appsStartingBounds() {
         testSpec.assertLayersStart {
-            val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
-            visibleRegion(primaryApp.defaultWindowName)
+            val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
+            visibleRegion(primaryApp.component)
                 .coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion))
-            visibleRegion(secondaryApp.defaultWindowName)
+            visibleRegion(secondaryApp.component)
                 .coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion))
         }
     }
@@ -100,16 +102,14 @@
     @Test
     fun appsEndingBounds() {
         testSpec.assertLayersEnd {
-            notContains(primaryApp.defaultWindowName)
-            notContains(secondaryApp.defaultWindowName)
+            notContains(primaryApp.component)
+            notContains(secondaryApp.component)
         }
     }
 
-    @FlakyTest
+    @Presubmit
     @Test
-    override fun navBarLayerIsAlwaysVisible() {
-        super.navBarLayerIsAlwaysVisible()
-    }
+    override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
index 1935bb9..cdf89a5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
@@ -30,14 +30,14 @@
 import com.android.server.wm.flicker.helpers.isRotated
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.repetitions
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.wm.shell.flicker.helpers.AppPairsHelper
 import com.android.wm.shell.flicker.helpers.BaseAppHelper
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.getDevEnableNonResizableMultiWindow
@@ -154,26 +154,26 @@
 
     @FlakyTest(bugId = 186510496)
     @Test
-    open fun navBarLayerIsAlwaysVisible() {
-        testSpec.navBarLayerIsAlwaysVisible()
+    open fun navBarLayerIsVisible() {
+        testSpec.navBarLayerIsVisible()
     }
 
     @Presubmit
     @Test
-    open fun statusBarLayerIsAlwaysVisible() {
-        testSpec.statusBarLayerIsAlwaysVisible()
+    open fun statusBarLayerIsVisible() {
+        testSpec.statusBarLayerIsVisible()
     }
 
     @Presubmit
     @Test
-    open fun navBarWindowIsAlwaysVisible() {
-        testSpec.navBarWindowIsAlwaysVisible()
+    open fun navBarWindowIsVisible() {
+        testSpec.navBarWindowIsVisible()
     }
 
     @Presubmit
     @Test
-    open fun statusBarWindowIsAlwaysVisible() {
-        testSpec.statusBarWindowIsAlwaysVisible()
+    open fun statusBarWindowIsVisible() {
+        testSpec.statusBarWindowIsVisible()
     }
 
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
index c875c00..3e782e6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.apppairs
 
-import android.os.SystemClock
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -28,10 +27,10 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.wm.shell.flicker.appPairsDividerIsVisible
-import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisible
-import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisible
-import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -57,41 +56,43 @@
             transitions {
                 executeShellCommand(composePairsCommand(
                     primaryTaskId, secondaryTaskId, true /* pair */))
-                SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+                waitAppsShown(primaryApp, secondaryApp)
                 setRotation(testSpec.config.endRotation)
             }
         }
 
-    @FlakyTest
+    @Presubmit
     @Test
-    override fun statusBarLayerIsAlwaysVisible() {
-        super.statusBarLayerIsAlwaysVisible()
-    }
+    override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+
+    @Presubmit
+    @Test
+    override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
 
     @Presubmit
     @Test
     fun bothAppWindowsVisible() {
         testSpec.assertWmEnd {
-            isVisible(primaryApp.defaultWindowName)
-                .isVisible(secondaryApp.defaultWindowName)
+            isVisible(primaryApp.component)
+                .isVisible(secondaryApp.component)
         }
     }
 
     @Presubmit
     @Test
-    fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
+    fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd()
 
-    @FlakyTest(bugId = 172776659)
+    @Presubmit
     @Test
-    fun appPairsPrimaryBoundsIsVisible() =
-        testSpec.appPairsPrimaryBoundsIsVisible(testSpec.config.endRotation,
-            primaryApp.defaultWindowName)
+    fun appPairsPrimaryBoundsIsVisibleAtEnd() =
+        testSpec.appPairsPrimaryBoundsIsVisibleAtEnd(testSpec.config.endRotation,
+            primaryApp.component)
 
-    @FlakyTest(bugId = 172776659)
+    @FlakyTest
     @Test
-    fun appPairsSecondaryBoundsIsVisible() =
-        testSpec.appPairsSecondaryBoundsIsVisible(testSpec.config.endRotation,
-            secondaryApp.defaultWindowName)
+    fun appPairsSecondaryBoundsIsVisibleAtEnd() =
+        testSpec.appPairsSecondaryBoundsIsVisibleAtEnd(testSpec.config.endRotation,
+            secondaryApp.component)
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
index c3360ca..ee28c7a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.apppairs
 
-import android.os.SystemClock
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -28,12 +27,10 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.appPairsDividerIsVisible
-import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisible
-import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisible
-import com.android.wm.shell.flicker.helpers.AppPairsHelper
+import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -60,48 +57,50 @@
                 this.setRotation(testSpec.config.endRotation)
                 executeShellCommand(
                     composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
-                SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
+                waitAppsShown(primaryApp, secondaryApp)
             }
         }
 
     @Presubmit
     @Test
-    fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
+    fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd()
 
     @Presubmit
     @Test
-    override fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    override fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
 
-    @FlakyTest
+    @Presubmit
     @Test
-    override fun statusBarLayerIsAlwaysVisible() {
-        super.statusBarLayerIsAlwaysVisible()
-    }
+    override fun statusBarWindowIsVisible() = super.statusBarWindowIsVisible()
+
+    @Presubmit
+    @Test
+    override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
 
     @Presubmit
     @Test
     fun bothAppWindowsVisible() {
         testSpec.assertWmEnd {
-            isVisible(primaryApp.defaultWindowName)
-            isVisible(secondaryApp.defaultWindowName)
+            isVisible(primaryApp.component)
+            isVisible(secondaryApp.component)
         }
     }
 
     @FlakyTest(bugId = 172776659)
     @Test
-    fun appPairsPrimaryBoundsIsVisible() =
-        testSpec.appPairsPrimaryBoundsIsVisible(testSpec.config.endRotation,
-            primaryApp.defaultWindowName)
+    fun appPairsPrimaryBoundsIsVisibleAtEnd() =
+        testSpec.appPairsPrimaryBoundsIsVisibleAtEnd(testSpec.config.endRotation,
+            primaryApp.component)
 
     @FlakyTest(bugId = 172776659)
     @Test
-    fun appPairsSecondaryBoundsIsVisible() =
-        testSpec.appPairsSecondaryBoundsIsVisible(testSpec.config.endRotation,
-            secondaryApp.defaultWindowName)
+    fun appPairsSecondaryBoundsIsVisibleAtEnd() =
+        testSpec.appPairsSecondaryBoundsIsVisibleAtEnd(testSpec.config.endRotation,
+            secondaryApp.component)
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
index 277aca8..b95193a1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt
@@ -40,8 +40,8 @@
                 test {
                     device.wakeUpAndGoToHomeScreen()
                     this.setRotation(Surface.ROTATION_0)
-                    primaryApp.launchViaIntent()
-                    secondaryApp.launchViaIntent()
+                    primaryApp.launchViaIntent(wmHelper)
+                    secondaryApp.launchViaIntent(wmHelper)
                     updateTasksId()
                 }
             }
@@ -64,8 +64,8 @@
 
     @FlakyTest
     @Test
-    override fun navBarLayerIsAlwaysVisible() {
-        super.navBarLayerIsAlwaysVisible()
+    override fun navBarLayerIsVisible() {
+        super.navBarLayerIsVisible()
     }
 
     @FlakyTest
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
index 5b8cfb8..5a438af 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
@@ -19,6 +19,7 @@
 import android.app.Instrumentation
 import android.content.ComponentName
 import android.graphics.Region
+import com.android.server.wm.flicker.Flicker
 import com.android.server.wm.flicker.helpers.WindowUtils
 
 class AppPairsHelper(
@@ -43,5 +44,17 @@
     companion object {
         const val TEST_REPETITIONS = 1
         const val TIMEOUT_MS = 3_000L
+
+        fun Flicker.waitAppsShown(app1: SplitScreenHelper?, app2: SplitScreenHelper?) {
+            wmHelper.waitFor("primaryAndSecondaryAppsVisible") { dump ->
+                val primaryAppVisible = app1?.let {
+                    dump.wmState.isWindowSurfaceShown(app1.defaultWindowName)
+                } ?: false
+                val secondaryAppVisible = app2?.let {
+                    dump.wmState.isWindowSurfaceShown(app2.defaultWindowName)
+                } ?: false
+                primaryAppVisible && secondaryAppVisible
+            }
+        }
     }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
index cac46fe..086e8b7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
@@ -61,7 +61,7 @@
         if (wmHelper == null) {
             device.waitForIdle()
         } else {
-            require(wmHelper.waitImeWindowShown()) { "IME did not appear" }
+            require(wmHelper.waitImeShown()) { "IME did not appear" }
         }
     }
 
@@ -78,7 +78,7 @@
             if (wmHelper == null) {
                 uiDevice.waitForIdle()
             } else {
-                require(wmHelper.waitImeWindowGone()) { "IME did did not close" }
+                require(wmHelper.waitImeGone()) { "IME did did not close" }
             }
         } else {
             // While pressing the back button should close the IME on TV as well, it may also lead
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
index f4dd7de..d4b4e5d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
@@ -62,7 +62,7 @@
         stringExtras: Map<String, String>
     ) {
         super.launchViaIntent(wmHelper, expectedWindowName, action, stringExtras)
-        wmHelper.waitFor { it.wmState.hasPipWindow() }
+        wmHelper.waitFor("hasPipWindow") { it.wmState.hasPipWindow() }
     }
 
     private fun focusOnObject(selector: BySelector): Boolean {
@@ -84,7 +84,7 @@
         clickObject(ENTER_PIP_BUTTON_ID)
 
         // Wait on WMHelper or simply wait for 3 seconds
-        wmHelper?.waitFor { it.wmState.hasPipWindow() } ?: SystemClock.sleep(3_000)
+        wmHelper?.waitFor("hasPipWindow") { it.wmState.hasPipWindow() } ?: SystemClock.sleep(3_000)
     }
 
     fun clickStartMediaSessionButton() {
@@ -137,7 +137,7 @@
         }
 
         // Wait for animation to complete.
-        wmHelper.waitFor { !it.wmState.hasPipWindow() }
+        wmHelper.waitFor("!hasPipWindow") { !it.wmState.hasPipWindow() }
         wmHelper.waitForHomeActivityVisible()
     }
 
@@ -167,7 +167,7 @@
         val windowRect = windowRegion.bounds
         uiDevice.click(windowRect.centerX(), windowRect.centerY())
         uiDevice.click(windowRect.centerX(), windowRect.centerY())
-        wmHelper.waitFor { !it.wmState.hasPipWindow() }
+        wmHelper.waitFor("!hasPipWindow") { !it.wmState.hasPipWindow() }
         wmHelper.waitForAppTransitionIdle()
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
index 901b7a3..2d996ca 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
@@ -18,6 +18,7 @@
 
 import android.app.Instrumentation
 import android.content.ComponentName
+import android.content.res.Resources
 import com.android.wm.shell.flicker.testapp.Components
 
 class SplitScreenHelper(
@@ -30,6 +31,11 @@
         const val TEST_REPETITIONS = 1
         const val TIMEOUT_MS = 3_000L
 
+        // TODO: remove all legacy split screen flicker tests when legacy split screen is fully
+        //  deprecated.
+        fun isUsingLegacySplit(): Boolean =
+                Resources.getSystem().getBoolean(com.android.internal.R.bool.config_useLegacySplit)
+
         fun getPrimary(instrumentation: Instrumentation): SplitScreenHelper =
             SplitScreenHelper(instrumentation,
                 Components.SplitScreenActivity.LABEL,
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 4f12f2b..508e939 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
@@ -16,22 +16,24 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
+import android.content.ComponentName
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 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.HOME_WINDOW_TITLE
 import com.android.server.wm.flicker.annotation.Group1
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -60,16 +62,16 @@
             }
         }
 
-    override val ignoredWindows: List<String>
-        get() = listOf(LAUNCHER_PACKAGE_NAME, LIVE_WALLPAPER_PACKAGE_NAME,
-            splitScreenApp.defaultWindowName, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-            WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME, *HOME_WINDOW_TITLE)
+    override val ignoredWindows: List<ComponentName>
+        get() = listOf(LAUNCHER_COMPONENT, LIVE_WALLPAPER_COMPONENT,
+            splitScreenApp.component, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+            WindowManagerStateHelper.SNAPSHOT_COMPONENT, LAUNCHER_COMPONENT)
 
     @Presubmit
     @Test
-    fun dockedStackPrimaryBoundsIsVisible() =
-        testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
-            splitScreenApp.defaultWindowName)
+    fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
+        testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+            splitScreenApp.component)
 
     @Presubmit
     @Test
@@ -77,27 +79,39 @@
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
 
     @Presubmit
     @Test
     fun appWindowIsVisible() {
         testSpec.assertWmEnd {
-            isVisible(splitScreenApp.defaultWindowName)
+            isVisible(splitScreenApp.component)
         }
     }
 
+    @FlakyTest
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+        super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+    @Presubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+        super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): Collection<FlickerTestParameter> {
             return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
                 repetitions = SplitScreenHelper.TEST_REPETITIONS,
-                supportedRotations = listOf(Surface.ROTATION_0) // bugId = 179116910
+                supportedRotations = listOf(Surface.ROTATION_0), // bugId = 179116910
+                supportedNavigationModes = listOf(
+                        WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)
             )
         }
     }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
index f91f634..12f3909 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
+import android.content.ComponentName
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -25,7 +26,7 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -61,24 +62,34 @@
             }
         }
 
-    override val ignoredWindows: List<String>
-        get() = listOf(LAUNCHER_PACKAGE_NAME,
-                WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-                WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME,
-                splitScreenApp.defaultWindowName)
+    override val ignoredWindows: List<ComponentName>
+        get() = listOf(LAUNCHER_COMPONENT,
+                WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+                WindowManagerStateHelper.SNAPSHOT_COMPONENT,
+                splitScreenApp.component)
 
     @Presubmit
     @Test
-    fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+    fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
 
     @Presubmit
     @Test
     fun appWindowIsVisible() {
         testSpec.assertWmEnd {
-            isVisible(splitScreenApp.defaultWindowName)
+            isVisible(splitScreenApp.component)
         }
     }
 
+    @Presubmit
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+            super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+    @Presubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
index 85ded8a..ac85c48 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
+import android.content.ComponentName
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -23,17 +24,16 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group1
-import com.android.server.wm.flicker.appWindowBecomesVisible
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
-import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -62,22 +62,22 @@
             }
         }
 
-    override val ignoredWindows: List<String>
-        get() = listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
-            secondaryApp.defaultWindowName, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-            WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+    override val ignoredWindows: List<ComponentName>
+        get() = listOf(LAUNCHER_COMPONENT, splitScreenApp.component,
+            secondaryApp.component, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+            WindowManagerStateHelper.SNAPSHOT_COMPONENT)
 
     @Presubmit
     @Test
-    fun dockedStackPrimaryBoundsIsVisible() =
-        testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
-            splitScreenApp.defaultWindowName)
+    fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
+        testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+            splitScreenApp.component)
 
     @Presubmit
     @Test
-    fun dockedStackSecondaryBoundsIsVisible() =
-        testSpec.dockedStackSecondaryBoundsIsVisible(testSpec.config.startRotation,
-            secondaryApp.defaultWindowName)
+    fun dockedStackSecondaryBoundsIsVisibleAtEnd() =
+        testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+            secondaryApp.component)
 
     @Presubmit
     @Test
@@ -85,15 +85,35 @@
 
     @Presubmit
     @Test
-    fun appWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp.defaultWindowName)
+    fun appWindowBecomesVisible() {
+        testSpec.assertWm {
+            // when the app is launched, first the activity becomes visible, then the
+            // SnapshotStartingWindow appears and then the app window becomes visible.
+            // Because we log WM once per frame, sometimes the activity and the window
+            // become visible in the same entry, sometimes not, thus it is not possible to
+            // assert the visibility of the activity here
+            this.isAppWindowInvisible(secondaryApp.component, ignoreActivity = true)
+                    .then()
+                    // during re-parenting, the window may disappear and reappear from the
+                    // trace, this occurs because we log only 1x per frame
+                    .notContains(secondaryApp.component, isOptional = true)
+                    .then()
+                    .isAppWindowVisible(secondaryApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
+
+    @Presubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
index e958bf3..964af23 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
+import android.content.ComponentName
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -26,7 +27,7 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.canSplitScreen
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -70,12 +71,12 @@
             }
         }
 
-    override val ignoredWindows: List<String>
-        get() = listOf(LAUNCHER_PACKAGE_NAME,
-            WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-            WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME,
-            nonResizeableApp.defaultWindowName,
-            splitScreenApp.defaultWindowName)
+    override val ignoredWindows: List<ComponentName>
+        get() = listOf(LAUNCHER_COMPONENT,
+            WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+            WindowManagerStateHelper.SNAPSHOT_COMPONENT,
+            nonResizeableApp.component,
+            splitScreenApp.component)
 
     @Before
     override fun setup() {
@@ -91,7 +92,12 @@
 
     @Presubmit
     @Test
-    fun dockedStackDividerIsInvisible() = testSpec.dockedStackDividerIsInvisible()
+    fun dockedStackDividerNotExistsAtEnd() = testSpec.dockedStackDividerNotExistsAtEnd()
+
+    @Presubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
index d3acc82..1b8afa6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
+import android.content.ComponentName
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -26,7 +27,7 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -67,12 +68,12 @@
             }
         }
 
-    override val ignoredWindows: List<String>
-        get() = listOf(LAUNCHER_PACKAGE_NAME,
-                WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-                WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME,
-                nonResizeableApp.defaultWindowName,
-                splitScreenApp.defaultWindowName)
+    override val ignoredWindows: List<ComponentName>
+        get() = listOf(LAUNCHER_COMPONENT,
+                WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+                WindowManagerStateHelper.SNAPSHOT_COMPONENT,
+                nonResizeableApp.component,
+                splitScreenApp.component)
 
     @Before
     override fun setup() {
@@ -88,16 +89,21 @@
 
     @Presubmit
     @Test
-    fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+    fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
 
     @Presubmit
     @Test
     fun appWindowIsVisible() {
         testSpec.assertWmEnd {
-            isVisible(nonResizeableApp.defaultWindowName)
+            isVisible(nonResizeableApp.component)
         }
     }
 
+    @Presubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
index bad4683..247965f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
@@ -16,7 +16,8 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
-import android.platform.test.annotations.Presubmit
+import android.content.ComponentName
+import android.platform.test.annotations.Postsubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
@@ -24,15 +25,13 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesInVisible
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.exitSplitScreenFromBottom
 import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.layerBecomesInvisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
+import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -67,31 +66,52 @@
                 }
             }
             transitions {
-                device.exitSplitScreenFromBottom()
+                device.exitSplitScreenFromBottom(wmHelper)
             }
         }
 
-    override val ignoredWindows: List<String>
-        get() = listOf(LAUNCHER_PACKAGE_NAME, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-            splitScreenApp.defaultWindowName, secondaryApp.defaultWindowName,
-            WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+    override val ignoredWindows: List<ComponentName>
+        get() = listOf(LAUNCHER_COMPONENT, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+            splitScreenApp.component, secondaryApp.component,
+            WindowManagerStateHelper.SNAPSHOT_COMPONENT)
 
-    @Presubmit
+    @Postsubmit
     @Test
-    fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(DOCKED_STACK_DIVIDER)
+    fun layerBecomesInvisible() {
+        testSpec.assertLayers {
+            this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
+                    .then()
+                    .isInvisible(DOCKED_STACK_DIVIDER_COMPONENT)
+        }
+    }
 
     @FlakyTest
     @Test
-    fun appWindowBecomesInVisible() =
-        testSpec.appWindowBecomesInVisible(secondaryApp.defaultWindowName)
+    fun appWindowBecomesInVisible() {
+        testSpec.assertWm {
+            this.isAppWindowVisible(secondaryApp.component)
+                    .then()
+                    .isAppWindowInvisible(secondaryApp.component)
+        }
+    }
 
-    @Presubmit
+    @Postsubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
-    @Presubmit
+    @Postsubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
+
+    @FlakyTest
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+            super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+    @FlakyTest
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
index 76dcd8b..af99fc4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
@@ -16,6 +16,8 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
+import android.content.ComponentName
+import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -24,15 +26,13 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesInVisible
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.flicker.layerBecomesInvisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -71,31 +71,52 @@
             }
         }
 
-    override val ignoredWindows: List<String>
-        get() = listOf(LAUNCHER_PACKAGE_NAME, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-            splitScreenApp.defaultWindowName, secondaryApp.defaultWindowName,
-            WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
-
-    @FlakyTest(bugId = 175687842)
-    @Test
-    fun dockedStackDividerIsInvisible() = testSpec.dockedStackDividerIsInvisible()
-
-    @FlakyTest
-    @Test
-    fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName)
-
-    @FlakyTest
-    @Test
-    fun appWindowBecomesInVisible() =
-        testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
+    override val ignoredWindows: List<ComponentName>
+        get() = listOf(LAUNCHER_COMPONENT, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+            splitScreenApp.component, secondaryApp.component,
+            WindowManagerStateHelper.SNAPSHOT_COMPONENT)
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun dockedStackDividerNotExistsAtEnd() = testSpec.dockedStackDividerNotExistsAtEnd()
+
+    @FlakyTest
+    @Test
+    fun layerBecomesInvisible() {
+        testSpec.assertLayers {
+            this.isVisible(splitScreenApp.component)
+                    .then()
+                    .isInvisible(splitScreenApp.component)
+        }
+    }
+
+    @FlakyTest
+    @Test
+    fun appWindowBecomesInVisible() {
+        testSpec.assertWm {
+            this.isAppWindowVisible(splitScreenApp.component)
+                    .then()
+                    .isAppWindowInvisible(splitScreenApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
+
+    @Presubmit
+    @Test
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
+
+    @Presubmit
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+            super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+    @Postsubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
index d0a64b3..95e4085 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
+import android.content.ComponentName
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -23,15 +24,11 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesInVisible
-import com.android.server.wm.flicker.appWindowBecomesVisible
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.layerBecomesInvisible
-import com.android.server.wm.flicker.layerBecomesVisible
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
-import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -72,11 +69,11 @@
             }
         }
 
-    override val ignoredWindows: List<String>
-        get() = listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, LETTERBOX_NAME,
-            nonResizeableApp.defaultWindowName, splitScreenApp.defaultWindowName,
-            WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-            WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+    override val ignoredWindows: List<ComponentName>
+        get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
+                nonResizeableApp.component, splitScreenApp.component,
+                WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+                WindowManagerStateHelper.SNAPSHOT_COMPONENT)
 
     @Before
     override fun setup() {
@@ -92,44 +89,110 @@
 
     @Presubmit
     @Test
-    fun resizableAppLayerBecomesInvisible() =
-            testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName)
+    fun resizableAppLayerBecomesInvisible() {
+        testSpec.assertLayers {
+            this.isVisible(splitScreenApp.component)
+                    .then()
+                    .isInvisible(splitScreenApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun nonResizableAppLayerBecomesVisible() =
-            testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
+    fun nonResizableAppLayerBecomesVisible() {
+        testSpec.assertLayers {
+            this.notContains(nonResizeableApp.component)
+                    .then()
+                    .isInvisible(nonResizeableApp.component)
+                    .then()
+                    .isVisible(nonResizeableApp.component)
+        }
+    }
+
+    /**
+     * Assets that [splitScreenApp] exists at the start of the trace and, once it becomes
+     * invisible, it remains invisible until the end of the trace.
+     */
+    @Presubmit
+    @Test
+    fun resizableAppWindowBecomesInvisible() {
+        testSpec.assertWm {
+            // when the activity gets PAUSED the window may still be marked as visible
+            // it will be updated in the next log entry. This occurs because we record 1x
+            // per frame, thus ignore activity check here
+            this.isAppWindowVisible(splitScreenApp.component, ignoreActivity = true)
+                    .then()
+                    // immediately after the window (after onResume and before perform relayout)
+                    // the activity is invisible. This may or not be logged, since we record 1x
+                    // per frame, thus ignore activity check here
+                    .isAppWindowInvisible(splitScreenApp.component, ignoreActivity = true)
+        }
+    }
+
+    /**
+     * Assets that [nonResizeableApp] doesn't exist at the start of the trace, then
+     * [nonResizeableApp] is created (visible or not) and, once [nonResizeableApp] becomes
+     * visible, it remains visible until the end of the trace.
+     */
+    @Presubmit
+    @Test
+    fun nonResizableAppWindowBecomesVisible() {
+        testSpec.assertWm {
+            this.notContains(nonResizeableApp.component)
+                    .then()
+                    // we log once per frame, upon logging, window may be visible or not depending
+                    // on what was processed until that moment. Both behaviors are correct
+                    .isAppWindowInvisible(nonResizeableApp.component,
+                            ignoreActivity = true, isOptional = true)
+                    .then()
+                    // immediately after the window (after onResume and before perform relayout)
+                    // the activity is invisible. This may or not be logged, since we record 1x
+                    // per frame, thus ignore activity check here
+                    .isAppWindowVisible(nonResizeableApp.component, ignoreActivity = true)
+        }
+    }
+
+    /**
+     * Asserts that both the app window and the activity are visible at the end of the trace
+     */
+    @Presubmit
+    @Test
+    fun nonResizableAppWindowBecomesVisibleAtEnd() {
+        testSpec.assertWmEnd {
+            this.isVisible(nonResizeableApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun resizableAppWindowBecomesInvisible() =
-            testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
-
-    @Presubmit
-    @Test
-    fun nonResizableAppWindowBecomesVisible() =
-            testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
-
-    @Presubmit
-    @Test
-    fun dockedStackDividerIsInvisibleAtEnd() = testSpec.dockedStackDividerIsInvisible()
+    fun dockedStackDividerNotExistsAtEnd() = testSpec.dockedStackDividerNotExistsAtEnd()
 
     @Presubmit
     @Test
     fun onlyNonResizableAppWindowIsVisibleAtEnd() {
         testSpec.assertWmEnd {
-            isInvisible(splitScreenApp.defaultWindowName)
-            isVisible(nonResizeableApp.defaultWindowName)
+            isInvisible(splitScreenApp.component)
+            isVisible(nonResizeableApp.component)
         }
     }
 
+    @Presubmit
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+            super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+    @Presubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): Collection<FlickerTestParameter> {
             return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
-                repetitions = SplitScreenHelper.TEST_REPETITIONS,
-                supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668
+                    repetitions = SplitScreenHelper.TEST_REPETITIONS,
+                    supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668
         }
     }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
index c26c05f..65346aa 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
+import android.content.ComponentName
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -23,13 +24,11 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesVisible
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.layerBecomesVisible
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -70,11 +69,11 @@
             }
         }
 
-    override val ignoredWindows: List<String>
-        get() = listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, LETTERBOX_NAME,
-            nonResizeableApp.defaultWindowName, splitScreenApp.defaultWindowName,
-            WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-            WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+    override val ignoredWindows: List<ComponentName>
+        get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
+            nonResizeableApp.component, splitScreenApp.component,
+            WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+            WindowManagerStateHelper.SNAPSHOT_COMPONENT)
 
     @Before
     override fun setup() {
@@ -90,27 +89,60 @@
 
     @Presubmit
     @Test
-    fun nonResizableAppLayerBecomesVisible() =
-            testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
+    fun nonResizableAppLayerBecomesVisible() {
+        testSpec.assertLayers {
+            this.isInvisible(nonResizeableApp.component)
+                    .then()
+                    .isVisible(nonResizeableApp.component)
+        }
+    }
+
+    /**
+     * Assets that [nonResizeableApp] doesn't exist at the start of the trace, then
+     * [nonResizeableApp] is created (visible or not) and, once [nonResizeableApp] becomes
+     * visible, it remains visible until the end of the trace.
+     */
+    @Presubmit
+    @Test
+    fun nonResizableAppWindowBecomesVisible() {
+        testSpec.assertWm {
+            this.notContains(nonResizeableApp.component)
+                    .then()
+                    // we log once per frame, upon logging, window may be visible or not depending
+                    // on what was processed until that moment. Both behaviors are correct
+                    .isAppWindowInvisible(nonResizeableApp.component,
+                            ignoreActivity = true, isOptional = true)
+                    .then()
+                    // immediately after the window (after onResume and before perform relayout)
+                    // the activity is invisible. This may or not be logged, since we record 1x
+                    // per frame, thus ignore activity check here
+                    .isAppWindowVisible(nonResizeableApp.component, ignoreActivity = true)
+        }
+    }
 
     @Presubmit
     @Test
-    fun nonResizableAppWindowBecomesVisible() =
-            testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
-
-    @Presubmit
-    @Test
-    fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisible()
+    fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
 
     @Presubmit
     @Test
     fun bothAppsWindowsAreVisibleAtEnd() {
         testSpec.assertWmEnd {
-            isVisible(splitScreenApp.defaultWindowName)
-            isVisible(nonResizeableApp.defaultWindowName)
+            isVisible(splitScreenApp.component)
+            isVisible(nonResizeableApp.component)
         }
     }
 
+    @Presubmit
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+            super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+    @Presubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
index fb17589..547341a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
@@ -16,6 +16,8 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
+import android.content.ComponentName
+import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -23,16 +25,12 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesInVisible
-import com.android.server.wm.flicker.appWindowBecomesVisible
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.flicker.layerBecomesInvisible
-import com.android.server.wm.flicker.layerBecomesVisible
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
-import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
+import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -73,11 +71,11 @@
             }
         }
 
-    override val ignoredWindows: List<String>
-        get() = listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, LETTERBOX_NAME, TOAST_NAME,
-                splitScreenApp.defaultWindowName, nonResizeableApp.defaultWindowName,
-                WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-                WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+    override val ignoredWindows: List<ComponentName>
+        get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
+                TOAST_COMPONENT, splitScreenApp.component, nonResizeableApp.component,
+                WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+                WindowManagerStateHelper.SNAPSHOT_COMPONENT)
 
     @Before
     override fun setup() {
@@ -93,37 +91,73 @@
 
     @Presubmit
     @Test
-    fun resizableAppLayerBecomesInvisible() =
-            testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName)
+    fun resizableAppLayerBecomesInvisible() {
+        testSpec.assertLayers {
+            this.isVisible(splitScreenApp.component)
+                    .then()
+                    .isInvisible(splitScreenApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun nonResizableAppLayerBecomesVisible() =
-            testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
+    fun nonResizableAppLayerBecomesVisible() {
+        testSpec.assertLayers {
+            this.isInvisible(nonResizeableApp.component)
+                    .then()
+                    .isVisible(nonResizeableApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun resizableAppWindowBecomesInvisible() =
-        testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
+    fun resizableAppWindowBecomesInvisible() {
+        testSpec.assertWm {
+            // when the activity gets PAUSED the window may still be marked as visible
+            // it will be updated in the next log entry. This occurs because we record 1x
+            // per frame, thus ignore activity check here
+            this.isAppWindowVisible(splitScreenApp.component, ignoreActivity = true)
+                    .then()
+                    // immediately after the window (after onResume and before perform relayout)
+                    // the activity is invisible. This may or not be logged, since we record 1x
+                    // per frame, thus ignore activity check here
+                    .isAppWindowInvisible(splitScreenApp.component, ignoreActivity = true)
+        }
+    }
+
+    @Postsubmit
+    @Test
+    fun nonResizableAppWindowBecomesVisible() {
+        testSpec.assertWm {
+            this.isAppWindowInvisible(nonResizeableApp.component)
+                    .then()
+                    .isAppWindowVisible(nonResizeableApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun nonResizableAppWindowBecomesVisible() =
-        testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
-
-    @Presubmit
-    @Test
-    fun dockedStackDividerIsInvisibleAtEnd() = testSpec.dockedStackDividerIsInvisible()
+    fun dockedStackDividerNotExistsAtEnd() = testSpec.dockedStackDividerNotExistsAtEnd()
 
     @Presubmit
     @Test
     fun onlyNonResizableAppWindowIsVisibleAtEnd() {
         testSpec.assertWmEnd {
-            isInvisible(splitScreenApp.defaultWindowName)
-            isVisible(nonResizeableApp.defaultWindowName)
+            isInvisible(splitScreenApp.component)
+            isVisible(nonResizeableApp.component)
         }
     }
 
+    @Presubmit
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+            super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+    @Presubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
index a9c28ef..3f86658 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
+import android.content.ComponentName
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -23,14 +24,12 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesVisible
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.flicker.layerBecomesVisible
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
+import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
 import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -71,11 +70,11 @@
             }
         }
 
-    override val ignoredWindows: List<String>
-        get() = listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, LETTERBOX_NAME, TOAST_NAME,
-                splitScreenApp.defaultWindowName, nonResizeableApp.defaultWindowName,
-                WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-                WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+    override val ignoredWindows: List<ComponentName>
+        get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT,
+                TOAST_COMPONENT, splitScreenApp.component, nonResizeableApp.component,
+                WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+                WindowManagerStateHelper.SNAPSHOT_COMPONENT)
 
     @Before
     override fun setup() {
@@ -91,27 +90,60 @@
 
     @Presubmit
     @Test
-    fun nonResizableAppLayerBecomesVisible() =
-            testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
+    fun nonResizableAppLayerBecomesVisible() {
+        testSpec.assertLayers {
+            this.isInvisible(nonResizeableApp.component)
+                    .then()
+                    .isVisible(nonResizeableApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun nonResizableAppWindowBecomesVisible() =
-        testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
+    fun nonResizableAppWindowBecomesVisible() {
+        testSpec.assertWm {
+            // when the app is launched, first the activity becomes visible, then the
+            // SnapshotStartingWindow appears and then the app window becomes visible.
+            // Because we log WM once per frame, sometimes the activity and the window
+            // become visible in the same entry, sometimes not, thus it is not possible to
+            // assert the visibility of the activity here
+            this.isAppWindowInvisible(nonResizeableApp.component, ignoreActivity = true)
+                    .then()
+                    // during re-parenting, the window may disappear and reappear from the
+                    // trace, this occurs because we log only 1x per frame
+                    .notContains(nonResizeableApp.component, isOptional = true)
+                    .then()
+                    // if the window reappears after re-parenting it will most likely not
+                    // be visible in the first log entry (because we log only 1x per frame)
+                    .isAppWindowInvisible(nonResizeableApp.component, isOptional = true)
+                    .then()
+                    .isAppWindowVisible(nonResizeableApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisible()
+    fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
 
     @Presubmit
     @Test
     fun bothAppsWindowsAreVisibleAtEnd() {
         testSpec.assertWmEnd {
-            isVisible(splitScreenApp.defaultWindowName)
-            isVisible(nonResizeableApp.defaultWindowName)
+            isVisible(splitScreenApp.component)
+            isVisible(nonResizeableApp.component)
         }
     }
 
+    @Presubmit
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+            super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+    @Presubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
index a4d2ab51..7b4b71b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
@@ -16,10 +16,10 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
+import android.content.ComponentName
+import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
-import android.support.test.launcherhelper.LauncherStrategyFactory
 import android.view.Surface
-import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
@@ -27,20 +27,18 @@
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.focusDoesNotChange
+import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.exitSplitScreen
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.layerBecomesInvisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import com.android.wm.shell.flicker.dockedStackDividerBecomesInvisible
 import com.android.wm.shell.flicker.helpers.SimpleAppHelper
@@ -62,8 +60,6 @@
 class LegacySplitScreenToLauncher(
     testSpec: FlickerTestParameter
 ) : LegacySplitScreenTransition(testSpec) {
-    private val launcherPackageName = LauncherStrategyFactory.getInstance(instrumentation)
-        .launcherStrategy.supportedLauncherPackage
     private val testApp = SimpleAppHelper(instrumentation)
 
     override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
@@ -90,25 +86,25 @@
             }
         }
 
-    override val ignoredWindows: List<String>
-        get() = listOf(launcherPackageName, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-            WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+    override val ignoredWindows: List<ComponentName>
+        get() = listOf(LAUNCHER_COMPONENT, WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+            WindowManagerStateHelper.SNAPSHOT_COMPONENT)
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
 
     @Presubmit
     @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
 
     @Presubmit
     @Test
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.endRotation)
+    fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.endRotation)
 
     @Presubmit
     @Test
@@ -122,19 +118,39 @@
 
     @Presubmit
     @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+    fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
 
-    @Presubmit
+    @Postsubmit
     @Test
     fun dockedStackDividerBecomesInvisible() = testSpec.dockedStackDividerBecomesInvisible()
 
+    @Postsubmit
+    @Test
+    fun layerBecomesInvisible() {
+        testSpec.assertLayers {
+            this.isVisible(testApp.component)
+                    .then()
+                    .isInvisible(testApp.component)
+        }
+    }
+
+    @Postsubmit
+    @Test
+    fun focusDoesNotChange() {
+        testSpec.assertEventLog {
+            this.focusDoesNotChange()
+        }
+    }
+
     @Presubmit
     @Test
-    fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(testApp.getPackage())
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+            super.visibleLayersShownMoreThanOneConsecutiveEntry()
 
-    @FlakyTest(bugId = 151179149)
+    @Presubmit
     @Test
-    fun focusDoesNotChange() = testSpec.focusDoesNotChange()
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
index 16dfd85..3117693 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
@@ -17,6 +17,7 @@
 package com.android.wm.shell.flicker.legacysplitscreen
 
 import android.app.Instrumentation
+import android.content.ComponentName
 import android.content.Context
 import android.support.test.launcherhelper.LauncherStrategyFactory
 import android.view.Surface
@@ -38,6 +39,7 @@
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
 import org.junit.After
 import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
 import org.junit.Before
 import org.junit.Test
 
@@ -48,12 +50,15 @@
     protected val splitScreenApp = SplitScreenHelper.getPrimary(instrumentation)
     protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation)
     protected val nonResizeableApp = SplitScreenHelper.getNonResizeable(instrumentation)
-    protected val LAUNCHER_PACKAGE_NAME = LauncherStrategyFactory.getInstance(instrumentation)
-        .launcherStrategy.supportedLauncherPackage
+    protected val LAUNCHER_COMPONENT = ComponentName("",
+            LauncherStrategyFactory.getInstance(instrumentation)
+                    .launcherStrategy.supportedLauncherPackage)
     private var prevDevEnableNonResizableMultiWindow = 0
 
     @Before
     open fun setup() {
+        // Only run legacy split tests when the system is using legacy split screen.
+        assumeTrue(SplitScreenHelper.isUsingLegacySplit())
         // Legacy split is having some issue with Shell transition, and will be deprecated soon.
         assumeFalse(isShellTransitionsEnabled())
         prevDevEnableNonResizableMultiWindow = getDevEnableNonResizableMultiWindow(context)
@@ -74,8 +79,9 @@
      *
      * b/182720234
      */
-    open val ignoredWindows: List<String> = listOf(WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-        WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+    open val ignoredWindows: List<ComponentName> = listOf(
+        WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+        WindowManagerStateHelper.SNAPSHOT_COMPONENT)
 
     protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = { configuration ->
@@ -142,9 +148,9 @@
     }
 
     companion object {
-        internal const val LIVE_WALLPAPER_PACKAGE_NAME =
-            "com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2"
-        internal const val LETTERBOX_NAME = "Letterbox"
-        internal const val TOAST_NAME = "Toast"
+        internal val LIVE_WALLPAPER_COMPONENT = ComponentName("",
+            "com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2")
+        internal val LETTERBOX_COMPONENT = ComponentName("", "Letterbox")
+        internal val TOAST_COMPONENT = ComponentName("", "Toast")
     }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
index 05eb5f4..ec0c73a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.legacysplitscreen
 
+import android.content.ComponentName
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -24,14 +25,11 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesVisible
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.launchSplitScreen
-import com.android.server.wm.flicker.layerBecomesVisible
-import com.android.server.wm.flicker.noUncoveredRegions
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import com.android.wm.shell.flicker.appPairsDividerBecomesVisible
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -62,22 +60,28 @@
             }
         }
 
-    override val ignoredWindows: List<String>
-        get() = listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
-            WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-            WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+    override val ignoredWindows: List<ComponentName>
+        get() = listOf(LAUNCHER_COMPONENT, splitScreenApp.component,
+            WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+            WindowManagerStateHelper.SNAPSHOT_COMPONENT)
 
     @FlakyTest
     @Test
-    fun appWindowBecomesVisible() = testSpec.appWindowBecomesVisible(splitScreenApp.getPackage())
-
-    @FlakyTest
-    @Test
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation)
+    fun appWindowBecomesVisible() {
+        testSpec.assertWm {
+            this.isAppWindowInvisible(splitScreenApp.component)
+                    .then()
+                    .isAppWindowVisible(splitScreenApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+    fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation)
+
+    @Presubmit
+    @Test
+    fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
 
     @Presubmit
     @Test
@@ -85,12 +89,27 @@
 
     @FlakyTest
     @Test
-    fun layerBecomesVisible() = testSpec.layerBecomesVisible(splitScreenApp.getPackage())
+    fun layerBecomesVisible() {
+        testSpec.assertLayers {
+            this.isInvisible(splitScreenApp.component)
+                    .then()
+                    .isVisible(splitScreenApp.component)
+        }
+    }
 
-    @FlakyTest(bugId = 151179149)
+    @Presubmit
     @Test
-    fun focusChanges() = testSpec.focusChanges(splitScreenApp.`package`,
-        "recents_animation_input_consumer", "NexusLauncherActivity")
+    fun focusChanges() {
+        testSpec.assertEventLog {
+            this.focusChanges(splitScreenApp.`package`,
+                    "recents_animation_input_consumer", "NexusLauncherActivity")
+        }
+    }
+
+    @Presubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
index 3e83b63..d7f71a8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
@@ -28,23 +28,23 @@
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.endRotation
+import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.ImeAppHelper
 import com.android.server.wm.flicker.helpers.WindowUtils
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.resizeSplitScreen
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.traces.layers.getVisibleBounds
-import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT
 import com.android.wm.shell.flicker.helpers.SimpleAppHelper
+import com.android.wm.shell.flicker.testapp.Components
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -101,16 +101,16 @@
         }
 
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
 
     @FlakyTest(bugId = 156223549)
     @Test
     fun topAppWindowIsAlwaysVisible() {
         testSpec.assertWm {
-            this.showsAppWindow(sSimpleActivity)
+            this.isAppWindowVisible(Components.SimpleActivity.COMPONENT)
         }
     }
 
@@ -118,18 +118,18 @@
     @Test
     fun bottomAppWindowIsAlwaysVisible() {
         testSpec.assertWm {
-            this.showsAppWindow(sImeActivity)
+            this.isAppWindowVisible(Components.ImeActivity.COMPONENT)
         }
     }
 
     @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
 
     @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+    fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
 
     @Test
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.endRotation)
+    fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.endRotation)
 
     @Test
     fun navBarLayerRotatesAndScales() =
@@ -142,21 +142,21 @@
     @Test
     fun topAppLayerIsAlwaysVisible() {
         testSpec.assertLayers {
-            this.isVisible(sSimpleActivity)
+            this.isVisible(Components.SimpleActivity.COMPONENT)
         }
     }
 
     @Test
     fun bottomAppLayerIsAlwaysVisible() {
         testSpec.assertLayers {
-            this.isVisible(sImeActivity)
+            this.isVisible(Components.ImeActivity.COMPONENT)
         }
     }
 
     @Test
     fun dividerLayerIsAlwaysVisible() {
         testSpec.assertLayers {
-            this.isVisible(DOCKED_STACK_DIVIDER)
+            this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
         }
     }
 
@@ -166,7 +166,7 @@
         testSpec.assertLayersStart {
             val displayBounds = WindowUtils.displayBounds
             val dividerBounds =
-                entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds
+                layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region.bounds
 
             val topAppBounds = Region(0, 0, dividerBounds.right,
                 dividerBounds.top + WindowUtils.dockedStackDividerInset)
@@ -174,8 +174,8 @@
                 dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
                 displayBounds.right,
                 displayBounds.bottom - WindowUtils.navigationBarHeight)
-            visibleRegion("SimpleActivity").coversExactly(topAppBounds)
-            visibleRegion("ImeActivity").coversExactly(bottomAppBounds)
+            visibleRegion(Components.SimpleActivity.COMPONENT).coversExactly(topAppBounds)
+            visibleRegion(Components.ImeActivity.COMPONENT).coversExactly(bottomAppBounds)
         }
     }
 
@@ -185,7 +185,7 @@
         testSpec.assertLayersStart {
             val displayBounds = WindowUtils.displayBounds
             val dividerBounds =
-                entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds
+                layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region.bounds
 
             val topAppBounds = Region(0, 0, dividerBounds.right,
                 dividerBounds.top + WindowUtils.dockedStackDividerInset)
@@ -194,8 +194,8 @@
                 displayBounds.right,
                 displayBounds.bottom - WindowUtils.navigationBarHeight)
 
-            visibleRegion(sSimpleActivity).coversExactly(topAppBounds)
-            visibleRegion(sImeActivity).coversExactly(bottomAppBounds)
+            visibleRegion(Components.SimpleActivity.COMPONENT).coversExactly(topAppBounds)
+            visibleRegion(Components.ImeActivity.COMPONENT).coversExactly(bottomAppBounds)
         }
     }
 
@@ -207,8 +207,6 @@
     }
 
     companion object {
-        private const val sSimpleActivity = "SimpleActivity"
-        private const val sImeActivity = "ImeActivity"
         private val startRatio = Rational(1, 3)
         private val stopRatio = Rational(2, 3)
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
index 58482ea..8a2b55b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt
@@ -24,18 +24,17 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesVisible
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -66,21 +65,21 @@
 
     @Presubmit
     @Test
-    fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+    fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
 
     @Presubmit
     @Test
-    fun dockedStackPrimaryBoundsIsVisible() =
-        testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
-            splitScreenApp.defaultWindowName)
+    fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
+        testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+            splitScreenApp.component)
 
-    @FlakyTest(bugId = 169271943)
+    @Presubmit
     @Test
     fun navBarLayerRotatesAndScales() =
         testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
             testSpec.config.endRotation)
 
-    @FlakyTest(bugId = 169271943)
+    @Presubmit
     @Test
     fun statusBarLayerRotatesScales() =
         testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation,
@@ -88,16 +87,26 @@
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
 
     @FlakyTest
     @Test
-    fun appWindowBecomesVisible() =
-        testSpec.appWindowBecomesVisible(splitScreenApp.defaultWindowName)
+    fun appWindowBecomesVisible() {
+        testSpec.assertWm {
+            this.isAppWindowInvisible(splitScreenApp.component)
+                    .then()
+                    .isAppWindowVisible(splitScreenApp.component)
+        }
+    }
+
+    @Presubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
index 06828d6..b325157 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt
@@ -24,18 +24,17 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesVisible
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -66,35 +65,45 @@
 
     @Presubmit
     @Test
-    fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+    fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
 
     @Presubmit
     @Test
-    fun dockedStackPrimaryBoundsIsVisible() = testSpec.dockedStackPrimaryBoundsIsVisible(
-        testSpec.config.startRotation, splitScreenApp.defaultWindowName)
+    fun dockedStackPrimaryBoundsIsVisibleAtEnd() = testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(
+        testSpec.config.startRotation, splitScreenApp.component)
 
-    @FlakyTest(bugId = 169271943)
+    @Presubmit
     @Test
     fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(
         testSpec.config.startRotation, testSpec.config.endRotation)
 
-    @FlakyTest(bugId = 169271943)
+    @Presubmit
     @Test
     fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(
         testSpec.config.startRotation, testSpec.config.endRotation)
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
 
     @FlakyTest
     @Test
-    fun appWindowBecomesVisible() =
-        testSpec.appWindowBecomesVisible(splitScreenApp.defaultWindowName)
+    fun appWindowBecomesVisible() {
+        testSpec.assertWm {
+            this.isAppWindowInvisible(splitScreenApp.component)
+                    .then()
+                    .isAppWindowVisible(splitScreenApp.component)
+        }
+    }
+
+    @Presubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
index f8e32bf..2be6936 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt
@@ -18,26 +18,24 @@
 
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
-import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 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.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesVisible
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.reopenAppFromOverview
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
-import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -69,42 +67,66 @@
 
     @Presubmit
     @Test
-    fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+    fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
 
     @Presubmit
     @Test
-    fun dockedStackPrimaryBoundsIsVisible() =
-        testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
-            splitScreenApp.defaultWindowName)
+    fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
+        testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+            splitScreenApp.component)
 
     @Presubmit
     @Test
-    fun dockedStackSecondaryBoundsIsVisible() =
-        testSpec.dockedStackSecondaryBoundsIsVisible(testSpec.config.startRotation,
-            secondaryApp.defaultWindowName)
+    fun dockedStackSecondaryBoundsIsVisibleAtEnd() =
+        testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+            secondaryApp.component)
 
-    @FlakyTest(bugId = 169271943)
+    @Presubmit
     @Test
     fun navBarLayerRotatesAndScales() =
         testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
             testSpec.config.endRotation)
 
-    @FlakyTest(bugId = 169271943)
+    @Presubmit
     @Test
     fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(
         testSpec.config.startRotation, testSpec.config.endRotation)
 
     @Presubmit
     @Test
-    fun appWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp.defaultWindowName)
+    fun appWindowBecomesVisible() {
+        testSpec.assertWm {
+            // when the app is launched, first the activity becomes visible, then the
+            // SnapshotStartingWindow appears and then the app window becomes visible.
+            // Because we log WM once per frame, sometimes the activity and the window
+            // become visible in the same entry, sometimes not, thus it is not possible to
+            // assert the visibility of the activity here
+            this.isAppWindowInvisible(secondaryApp.component, ignoreActivity = true)
+                    .then()
+                    // during re-parenting, the window may disappear and reappear from the
+                    // trace, this occurs because we log only 1x per frame
+                    .notContains(secondaryApp.component, isOptional = true)
+                    .then()
+                    // if the window reappears after re-parenting it will most likely not
+                    // be visible in the first log entry (because we log only 1x per frame)
+                    .isAppWindowInvisible(secondaryApp.component, isOptional = true)
+                    .then()
+                    .isAppWindowVisible(secondaryApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
+
+    @Presubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
index cb246ca..5782f14 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt
@@ -24,20 +24,19 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.appWindowBecomesVisible
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.reopenAppFromOverview
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.wm.shell.flicker.dockedStackDividerIsVisible
-import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
-import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -74,27 +73,27 @@
 
     @Presubmit
     @Test
-    fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+    fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd()
 
     @Presubmit
     @Test
-    fun dockedStackPrimaryBoundsIsVisible() =
-        testSpec.dockedStackPrimaryBoundsIsVisible(testSpec.config.startRotation,
-            splitScreenApp.defaultWindowName)
+    fun dockedStackPrimaryBoundsIsVisibleAtEnd() =
+        testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+            splitScreenApp.component)
 
     @Presubmit
     @Test
-    fun dockedStackSecondaryBoundsIsVisible() =
-        testSpec.dockedStackSecondaryBoundsIsVisible(testSpec.config.startRotation,
-            secondaryApp.defaultWindowName)
+    fun dockedStackSecondaryBoundsIsVisibleAtEnd() =
+        testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.config.startRotation,
+            secondaryApp.component)
 
-    @FlakyTest(bugId = 169271943)
+    @Presubmit
     @Test
     fun navBarLayerRotatesAndScales() =
         testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
             testSpec.config.endRotation)
 
-    @FlakyTest(bugId = 169271943)
+    @Presubmit
     @Test
     fun statusBarLayerRotatesScales() =
         testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation,
@@ -102,16 +101,31 @@
 
     @FlakyTest
     @Test
-    fun appWindowBecomesVisible() =
-        testSpec.appWindowBecomesVisible(secondaryApp.defaultWindowName)
+    fun appWindowBecomesVisible() {
+        testSpec.assertWm {
+            this.isAppWindowInvisible(secondaryApp.component)
+                    .then()
+                    .isAppWindowVisible(secondaryApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
+
+    @Presubmit
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+            super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+    @Presubmit
+    @Test
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+            super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt
index 2a66074..443204c2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt
@@ -1,5 +1,5 @@
 /*
- * 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.
@@ -16,4 +16,4 @@
 
 package com.android.wm.shell.flicker.pip
 
-internal const val PIP_WINDOW_TITLE = "PipMenuActivity"
+internal const val PIP_WINDOW_COMPONENT = "PipMenuActivity"
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
index 00e50e7..39e89fb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
@@ -62,18 +63,21 @@
     @Test
     fun pipAppRemainInsideVisibleBounds() {
         testSpec.assertWm {
-            coversAtMost(displayBounds, pipApp.defaultWindowName)
+            coversAtMost(displayBounds, pipApp.component)
         }
     }
 
-    @Presubmit
+    @Postsubmit
     @Test
     fun showBothAppWindowsThenHidePip() {
         testSpec.assertWm {
-            showsAppWindow(testApp.defaultWindowName)
-                .showsAppWindowOnTop(pipApp.defaultWindowName)
+            // when the activity is STOPPING, sometimes it becomes invisible in an entry before
+            // the window, sometimes in the same entry. This occurs because we log 1x per frame
+            // thus we ignore activity here
+            isAppWindowVisible(testApp.component, ignoreActivity = true)
+                .isAppWindowOnTop(pipApp.component)
                 .then()
-                .hidesAppWindow(testApp.defaultWindowName)
+                .isAppWindowInvisible(testApp.component)
         }
     }
 
@@ -81,10 +85,10 @@
     @Test
     fun showBothAppLayersThenHidePip() {
         testSpec.assertLayers {
-            isVisible(testApp.defaultWindowName)
-                .isVisible(pipApp.defaultWindowName)
+            isVisible(testApp.component)
+                .isVisible(pipApp.component)
                 .then()
-                .isInvisible(testApp.defaultWindowName)
+                .isInvisible(testApp.component)
         }
     }
 
@@ -92,8 +96,8 @@
     @Test
     fun testAppCoversFullScreenWithPipOnDisplay() {
         testSpec.assertLayersStart {
-            visibleRegion(testApp.defaultWindowName).coversExactly(displayBounds)
-            visibleRegion(pipApp.defaultWindowName).coversAtMost(displayBounds)
+            visibleRegion(testApp.component).coversExactly(displayBounds)
+            visibleRegion(pipApp.component).coversAtMost(displayBounds)
         }
     }
 
@@ -101,7 +105,7 @@
     @Test
     fun pipAppCoversFullScreen() {
         testSpec.assertLayersEnd {
-            visibleRegion(pipApp.defaultWindowName).coversExactly(displayBounds)
+            visibleRegion(pipApp.component).coversExactly(displayBounds)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index b6af260..0f0a4ab 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -44,30 +44,24 @@
     override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = buildTransition(eachRun = true, stringExtras = emptyMap()) {
             transitions {
-                pipApp.clickEnterPipButton()
+                pipApp.clickEnterPipButton(wmHelper)
                 pipApp.expandPipWindow(wmHelper)
             }
         }
 
-    @FlakyTest
-    @Test
-    override fun noUncoveredRegions() {
-        super.noUncoveredRegions()
-    }
-
     @Presubmit
     @Test
     fun pipAppWindowAlwaysVisible() {
         testSpec.assertWm {
-            this.showsAppWindow(pipApp.defaultWindowName)
+            this.isAppWindowVisible(pipApp.component)
         }
     }
 
-    @FlakyTest
+    @Presubmit
     @Test
-    fun pipLayerBecomesVisible() {
+    fun pipAppLayerAlwaysVisible() {
         testSpec.assertLayers {
-            this.isVisible(pipApp.windowName)
+            this.isVisible(pipApp.component)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index 3a1456e..67ad322 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -92,15 +92,13 @@
 
     @FlakyTest
     @Test
-    override fun noUncoveredRegions() {
-        super.noUncoveredRegions()
-    }
+    override fun entireScreenCovered() = super.entireScreenCovered()
 
     @Presubmit
     @Test
     fun pipAppWindowIsAlwaysOnTop() {
         testSpec.assertWm {
-            showsAppWindowOnTop(pipApp.defaultWindowName)
+            isAppWindowOnTop(pipApp.component)
         }
     }
 
@@ -108,7 +106,7 @@
     @Test
     fun pipAppHidesTestApp() {
         testSpec.assertWmStart {
-            isInvisible(testApp.defaultWindowName)
+            isInvisible(testApp.component)
         }
     }
 
@@ -116,7 +114,7 @@
     @Test
     fun testAppWindowIsVisible() {
         testSpec.assertWmEnd {
-            isVisible(testApp.defaultWindowName)
+            isVisible(testApp.component)
         }
     }
 
@@ -124,8 +122,8 @@
     @Test
     fun pipAppLayerHidesTestApp() {
         testSpec.assertLayersStart {
-            visibleRegion(pipApp.defaultWindowName).coversExactly(startingBounds)
-            isInvisible(testApp.defaultWindowName)
+            visibleRegion(pipApp.component).coversExactly(startingBounds)
+            isInvisible(testApp.component)
         }
     }
 
@@ -133,7 +131,7 @@
     @Test
     fun testAppLayerCoversFullScreen() {
         testSpec.assertLayersEnd {
-            visibleRegion(testApp.defaultWindowName).coversExactly(endingBounds)
+            visibleRegion(testApp.component).coversExactly(endingBounds)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt
deleted file mode 100644
index 0037059..0000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/Extensions.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.pip
-
-import android.content.ComponentName
-import com.android.server.wm.traces.common.windowmanager.WindowManagerState
-import com.android.server.wm.traces.parser.toWindowName
-
-/**
- * Checks that an activity [activity] is in PIP mode
- */
-fun WindowManagerState.isInPipMode(activity: ComponentName): Boolean {
-    val windowName = activity.toWindowName()
-    return isInPipMode(windowName)
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt
index eae7e97..28b1028 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt
@@ -21,8 +21,8 @@
 import androidx.test.filters.FlakyTest
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.LAUNCHER_COMPONENT
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.focusChanges
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.startRotation
 import org.junit.Test
@@ -47,9 +47,9 @@
     @Test
     open fun pipWindowBecomesInvisible() {
         testSpec.assertWm {
-            this.showsAppWindow(PIP_WINDOW_TITLE)
+            this.invoke("hasPipWindow") { it.isPinned(pipApp.component) }
                 .then()
-                .hidesAppWindow(PIP_WINDOW_TITLE)
+                .isAppWindowInvisible(pipApp.component)
         }
     }
 
@@ -57,15 +57,21 @@
     @Test
     open fun pipLayerBecomesInvisible() {
         testSpec.assertLayers {
-            this.isVisible(PIP_WINDOW_TITLE)
+            this.isVisible(pipApp.component)
+                .isVisible(LAUNCHER_COMPONENT)
                 .then()
-                .isInvisible(PIP_WINDOW_TITLE)
+                .isInvisible(pipApp.component)
+                .isVisible(LAUNCHER_COMPONENT)
         }
     }
 
     @FlakyTest(bugId = 151179149)
     @Test
-    open fun focusChanges() = testSpec.focusChanges(pipApp.launcherName, "NexusLauncherActivity")
+    open fun focusChanges() {
+        testSpec.assertEventLog {
+            this.focusChanges(pipApp.launcherName, "NexusLauncherActivity")
+        }
+    }
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt
index cf84a2c..1c5d77f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt
@@ -48,13 +48,9 @@
 
     @FlakyTest
     @Test
-    override fun pipLayerBecomesInvisible() {
-        super.pipLayerBecomesInvisible()
-    }
+    override fun pipLayerBecomesInvisible() = super.pipLayerBecomesInvisible()
 
     @FlakyTest
     @Test
-    override fun pipWindowBecomesInvisible() {
-        super.pipWindowBecomesInvisible()
-    }
+    override fun pipWindowBecomesInvisible() = super.pipWindowBecomesInvisible()
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt
index 524a1b4..356ec94 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt
@@ -56,19 +56,19 @@
 
     @Presubmit
     @Test
-    override fun navBarLayerIsAlwaysVisible() = super.navBarLayerIsAlwaysVisible()
+    override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
 
     @Presubmit
     @Test
-    override fun statusBarLayerIsAlwaysVisible() = super.statusBarLayerIsAlwaysVisible()
+    override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
 
     @Presubmit
     @Test
-    override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+    override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+    override fun statusBarWindowIsVisible() = super.statusBarWindowIsVisible()
 
     @FlakyTest
     @Test
@@ -85,7 +85,7 @@
 
     @Presubmit
     @Test
-    override fun noUncoveredRegions() = super.noUncoveredRegions()
+    override fun entireScreenCovered() = super.entireScreenCovered()
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index d88f94d..5719413 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -27,7 +27,7 @@
 import com.android.server.wm.flicker.helpers.WindowUtils
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.startRotation
-import com.android.wm.shell.flicker.IME_WINDOW_NAME
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import com.android.wm.shell.flicker.helpers.ImeAppHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -79,7 +79,7 @@
     fun pipInVisibleBounds() {
         testSpec.assertWm {
             val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
-            coversAtMost(displayBounds, pipApp.defaultWindowName)
+            coversAtMost(displayBounds, pipApp.component)
         }
     }
 
@@ -90,7 +90,7 @@
     @Test
     fun pipIsAboveAppWindow() {
         testSpec.assertWmTag(TAG_IME_VISIBLE) {
-            isAboveWindow(IME_WINDOW_NAME, pipApp.defaultWindowName)
+            isAboveWindow(WindowManagerStateHelper.IME_COMPONENT, pipApp.component)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
index 6833b96..0861652 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
@@ -27,11 +27,16 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.wm.shell.flicker.helpers.ImeAppHelper
-import com.android.wm.shell.flicker.helpers.FixedAppHelper
 import com.android.server.wm.flicker.repetitions
 import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
+import com.android.wm.shell.flicker.helpers.BaseAppHelper.Companion.isShellTransitionsEnabled
+import com.android.wm.shell.flicker.helpers.FixedAppHelper
+import com.android.wm.shell.flicker.helpers.ImeAppHelper
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
 import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
+import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
+import org.junit.Before
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -46,12 +51,19 @@
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 161435597)
 @Group3
 class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
     private val imeApp = ImeAppHelper(instrumentation)
     private val testApp = FixedAppHelper(instrumentation)
 
+    @Before
+    open fun setup() {
+        // Only run legacy split tests when the system is using legacy split screen.
+        assumeTrue(SplitScreenHelper.isUsingLegacySplit())
+        // Legacy split is having some issue with Shell transition, and will be deprecated soon.
+        assumeFalse(isShellTransitionsEnabled())
+    }
+
     override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = {
             withTestName { testSpec.name }
@@ -80,11 +92,11 @@
             }
         }
 
-    @Presubmit
+    @FlakyTest(bugId = 161435597)
     @Test
     fun pipWindowInsideDisplayBounds() {
         testSpec.assertWm {
-            coversAtMost(displayBounds, pipApp.defaultWindowName)
+            coversAtMost(displayBounds, pipApp.component)
         }
     }
 
@@ -92,25 +104,17 @@
     @Test
     fun bothAppWindowsVisible() {
         testSpec.assertWmEnd {
-            isVisible(testApp.defaultWindowName)
-            isVisible(imeApp.defaultWindowName)
-            noWindowsOverlap(testApp.defaultWindowName, imeApp.defaultWindowName)
+            isVisible(testApp.component)
+            isVisible(imeApp.component)
+            noWindowsOverlap(testApp.component, imeApp.component)
         }
     }
 
-    @Presubmit
-    @Test
-    override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
-
-    @Presubmit
+    @FlakyTest(bugId = 161435597)
     @Test
     fun pipLayerInsideDisplayBounds() {
         testSpec.assertLayers {
-            coversAtMost(displayBounds, pipApp.defaultWindowName)
+            coversAtMost(displayBounds, pipApp.component)
         }
     }
 
@@ -118,18 +122,14 @@
     @Test
     fun bothAppLayersVisible() {
         testSpec.assertLayersEnd {
-            visibleRegion(testApp.defaultWindowName).coversAtMost(displayBounds)
-            visibleRegion(imeApp.defaultWindowName).coversAtMost(displayBounds)
+            visibleRegion(testApp.component).coversAtMost(displayBounds)
+            visibleRegion(imeApp.component).coversAtMost(displayBounds)
         }
     }
 
-    @Presubmit
+    @FlakyTest(bugId = 161435597)
     @Test
-    override fun navBarLayerIsAlwaysVisible() = super.navBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    override fun statusBarLayerIsAlwaysVisible() = super.statusBarLayerIsAlwaysVisible()
+    override fun entireScreenCovered() = super.entireScreenCovered()
 
     companion object {
         const val TEST_REPETITIONS = 2
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index d531af2..45cb152 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -26,12 +26,14 @@
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.endRotation
+import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.WindowUtils
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.noUncoveredRegions
 import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.traces.common.Region
+import com.android.server.wm.traces.parser.minus
 import com.android.wm.shell.flicker.helpers.FixedAppHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -73,9 +75,9 @@
             }
         }
 
-    @FlakyTest(bugId = 185400889)
+    @Presubmit
     @Test
-    override fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
+    override fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation,
         testSpec.config.endRotation, allStates = false)
 
     @FlakyTest
@@ -90,21 +92,27 @@
         testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation,
             testSpec.config.endRotation)
 
-    @FlakyTest(bugId = 185400889)
+    @Presubmit
     @Test
     fun appLayerRotates_StartingBounds() {
         testSpec.assertLayersStart {
-            visibleRegion(fixedApp.defaultWindowName).coversExactly(startingBounds)
-            visibleRegion(pipApp.defaultWindowName).coversAtMost(startingBounds)
+            val pipRegion = visibleRegion(pipApp.component).region
+            val expectedWithoutPip = Region(startingBounds.bounds.left, startingBounds.bounds.top,
+                    startingBounds.bounds.right, startingBounds.bounds.bottom).minus(pipRegion)
+            visibleRegion(fixedApp.component).coversExactly(expectedWithoutPip)
+            visibleRegion(pipApp.component).coversAtMost(startingBounds)
         }
     }
 
-    @FlakyTest(bugId = 185400889)
+    @Presubmit
     @Test
     fun appLayerRotates_EndingBounds() {
         testSpec.assertLayersEnd {
-            visibleRegion(fixedApp.defaultWindowName).coversExactly(endingBounds)
-            visibleRegion(pipApp.defaultWindowName).coversAtMost(endingBounds)
+            val pipRegion = visibleRegion(pipApp.component).region
+            val expectedWithoutPip = Region(endingBounds.bounds.left, endingBounds.bounds.top,
+                    endingBounds.bounds.right, endingBounds.bounds.bottom).minus(pipRegion)
+            visibleRegion(fixedApp.component).coversExactly(expectedWithoutPip)
+            visibleRegion(pipApp.component).coversAtMost(endingBounds)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipShelfHeightTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipShelfHeightTest.kt
index 1294ac9..914bc8b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipShelfHeightTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipShelfHeightTest.kt
@@ -63,13 +63,13 @@
 
     @Presubmit
     @Test
-    fun pipAlwaysVisible() = testSpec.assertWm { this.showsAppWindow(pipApp.windowName) }
+    fun pipAlwaysVisible() = testSpec.assertWm { this.isAppWindowVisible(pipApp.component) }
 
     @Presubmit
     @Test
     fun pipLayerInsideDisplay() {
         testSpec.assertLayersStart {
-            visibleRegion(pipApp.defaultWindowName).coversAtMost(displayBounds)
+            visibleRegion(pipApp.component).coversAtMost(displayBounds)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
index 55e5c41..5abcf39 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
@@ -22,9 +22,9 @@
 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_COMPONENT
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.focusChanges
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.startRotation
 import org.junit.FixMethodOrder
@@ -64,9 +64,11 @@
     @Test
     fun appReplacesPipWindow() {
         testSpec.assertWm {
-            this.showsAppWindow(PIP_WINDOW_TITLE)
+            this.invoke("hasPipWindow") { it.isPinned(pipApp.component) }
+                .isAppWindowOnTop(pipApp.component)
                 .then()
-                .showsAppWindowOnTop(pipApp.launcherName)
+                .invoke("hasNotPipWindow") { it.isNotPinned(pipApp.component) }
+                .isAppWindowOnTop(pipApp.component)
         }
     }
 
@@ -74,9 +76,11 @@
     @Test
     fun appReplacesPipLayer() {
         testSpec.assertLayers {
-            this.isVisible(PIP_WINDOW_TITLE)
+            this.isVisible(pipApp.component)
+                .isVisible(LAUNCHER_COMPONENT)
                 .then()
-                .isVisible(pipApp.launcherName)
+                .isVisible(pipApp.component)
+                .isInvisible(LAUNCHER_COMPONENT)
         }
     }
 
@@ -84,22 +88,26 @@
     @Test
     fun testAppCoversFullScreen() {
         testSpec.assertLayersStart {
-            visibleRegion(pipApp.defaultWindowName).coversExactly(displayBounds)
+            visibleRegion(pipApp.component).coversExactly(displayBounds)
         }
     }
 
     @FlakyTest(bugId = 151179149)
     @Test
-    fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity",
-        pipApp.launcherName, "NexusLauncherActivity")
+    fun focusChanges() {
+        testSpec.assertEventLog {
+            this.focusChanges("NexusLauncherActivity",
+                    pipApp.launcherName, "NexusLauncherActivity")
+        }
+    }
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): List<FlickerTestParameter> {
             return FlickerTestParameterFactory.getInstance()
-                .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
-                    repetitions = 5)
+                    .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
+                            repetitions = 5)
         }
     }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
index b4c75a6..ca80d18 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
@@ -20,25 +20,24 @@
 import android.content.Intent
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
-import androidx.test.filters.FlakyTest
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.WindowUtils
 import com.android.server.wm.flicker.helpers.isRotated
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.repetitions
 import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.wm.shell.flicker.helpers.PipAppHelper
 import com.android.wm.shell.flicker.testapp.Components
 import org.junit.Test
@@ -162,19 +161,19 @@
 
     @Presubmit
     @Test
-    open fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    open fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    open fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
 
-    @FlakyTest
+    @Presubmit
     @Test
-    open fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    open fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
 
-    @FlakyTest
+    @Presubmit
     @Test
-    open fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+    open fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
 
     @Presubmit
     @Test
@@ -188,6 +187,6 @@
 
     @Presubmit
     @Test
-    open fun noUncoveredRegions() =
-        testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
+    open fun entireScreenCovered() =
+        testSpec.entireScreenCovered(testSpec.config.startRotation, Surface.ROTATION_0)
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index 1f58bb2..e7b6197 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.pip
 
-import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
@@ -83,54 +82,70 @@
 
     @FlakyTest
     @Test
+    override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+
+    @FlakyTest
+    @Test
+    override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
+
+    @FlakyTest
+    @Test
+    override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
+
+    @FlakyTest
+    @Test
+    override fun statusBarWindowIsVisible() = super.statusBarWindowIsVisible()
+
+    @FlakyTest
+    @Test
     override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
 
     @FlakyTest
     @Test
     override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
 
-    @Presubmit
+    @FlakyTest
     @Test
     fun pipWindowInsideDisplay() {
         testSpec.assertWmStart {
-            frameRegion(pipApp.defaultWindowName).coversAtMost(startingBounds)
-        }
-    }
-
-    @Presubmit
-    @Test
-    fun pipAppShowsOnTop() {
-        testSpec.assertWmEnd {
-            showsAppWindowOnTop(pipApp.defaultWindowName)
-        }
-    }
-
-    @Presubmit
-    @Test
-    fun pipLayerInsideDisplay() {
-        testSpec.assertLayersStart {
-            visibleRegion(pipApp.defaultWindowName).coversAtMost(startingBounds)
-        }
-    }
-
-    @Presubmit
-    @Test
-    fun pipAlwaysVisible() = testSpec.assertWm {
-        this.showsAppWindow(pipApp.windowName)
-    }
-
-    @Presubmit
-    @Test
-    fun pipAppLayerCoversFullScreen() {
-        testSpec.assertLayersEnd {
-            visibleRegion(pipApp.defaultWindowName).coversExactly(endingBounds)
+            frameRegion(pipApp.component).coversAtMost(startingBounds)
         }
     }
 
     @FlakyTest
     @Test
-    override fun noUncoveredRegions() {
-        super.noUncoveredRegions()
+    fun pipAppShowsOnTop() {
+        testSpec.assertWmEnd {
+            isAppWindowOnTop(pipApp.component)
+        }
+    }
+
+    @FlakyTest
+    @Test
+    fun pipLayerInsideDisplay() {
+        testSpec.assertLayersStart {
+            visibleRegion(pipApp.component).coversAtMost(startingBounds)
+        }
+    }
+
+    @FlakyTest
+    @Test
+    fun pipAlwaysVisible() = testSpec.assertWm {
+        this.isAppWindowVisible(pipApp.component)
+    }
+
+    @FlakyTest
+    @Test
+    fun pipAppLayerCoversFullScreen() {
+        testSpec.assertLayersEnd {
+            visibleRegion(pipApp.component).coversExactly(endingBounds)
+        }
+    }
+
+    @FlakyTest
+    @Test
+    override fun entireScreenCovered() {
+        super.entireScreenCovered()
     }
 
     companion object {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
index 0110ba3..061218a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
@@ -37,14 +37,17 @@
     private val systemUiResources =
             packageManager.getResourcesForApplication(SYSTEM_UI_PACKAGE_NAME)
     private val pipBoundsWhileInMenu: Rect = systemUiResources.run {
-        val bounds = getString(getIdentifier("pip_menu_bounds", "string", SYSTEM_UI_PACKAGE_NAME))
+        val bounds = getString(getIdentifier("pip_menu_bounds", "string",
+                SYSTEM_UI_PACKAGE_NAME))
         Rect.unflattenFromString(bounds) ?: error("Could not retrieve PiP menu bounds")
     }
     private val playButtonDescription = systemUiResources.run {
-        getString(getIdentifier("pip_play", "string", SYSTEM_UI_PACKAGE_NAME))
+        getString(getIdentifier("pip_play", "string",
+                SYSTEM_UI_PACKAGE_NAME))
     }
     private val pauseButtonDescription = systemUiResources.run {
-        getString(getIdentifier("pip_pause", "string", SYSTEM_UI_PACKAGE_NAME))
+        getString(getIdentifier("pip_pause", "string",
+                SYSTEM_UI_PACKAGE_NAME))
     }
 
     @Before
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt
index 1b73920..1c66340 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt
@@ -70,7 +70,8 @@
     // descendant and then retrieve the element from the menu and return to the caller of this
     // method.
     val elementSelector = By.desc(desc)
-    val menuContainingElementSelector = By.copy(TV_PIP_MENU_SELECTOR).hasDescendant(elementSelector)
+    val menuContainingElementSelector = By.copy(TV_PIP_MENU_SELECTOR)
+            .hasDescendant(elementSelector)
 
     return wait(Until.findObject(menuContainingElementSelector), WAIT_TIME_MS)
             ?.findObject(elementSelector)
@@ -94,7 +95,8 @@
 }
 
 fun UiDevice.clickTvPipMenuElementWithDescription(desc: String) {
-    focusOnAndClickTvPipMenuElement(By.desc(desc).pkg(SYSTEM_UI_PACKAGE_NAME)) ||
+    focusOnAndClickTvPipMenuElement(By.desc(desc)
+            .pkg(SYSTEM_UI_PACKAGE_NAME)) ||
             error("Could not focus on the Pip menu object with \"$desc\" description")
     // So apparently Accessibility framework on TV is not very reliable and sometimes the state of
     // the tree of accessibility nodes as seen by the accessibility clients kind of lags behind of
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index 3e3195f..b0312e6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -869,6 +869,35 @@
         assertNotNull(mBubbleData.getOverflowBubbleWithKey(mBubbleA2.getKey()));
     }
 
+    /**
+     * Verifies that after the stack is collapsed with the overflow selected, it will select
+     * the top bubble upon next expansion.
+     */
+    @Test
+    public void test_collapseWithOverflowSelected_nextExpansion() {
+        sendUpdatedEntryAtTime(mEntryA1, 1000);
+        sendUpdatedEntryAtTime(mEntryA2, 2000);
+        mBubbleData.setExpanded(true);
+
+        mBubbleData.setListener(mListener);
+
+        // Select the overflow
+        mBubbleData.setShowingOverflow(true);
+        mBubbleData.setSelectedBubble(mBubbleData.getOverflow());
+        verifyUpdateReceived();
+        assertSelectionChangedTo(mBubbleData.getOverflow());
+
+        // Collapse
+        mBubbleData.setExpanded(false);
+        verifyUpdateReceived();
+        assertSelectionNotChanged();
+
+        // Expand (here we should select the new bubble)
+        mBubbleData.setExpanded(true);
+        verifyUpdateReceived();
+        assertSelectionChangedTo(mBubbleA2);
+    }
+
     private void verifyUpdateReceived() {
         verify(mListener).applyUpdate(mUpdateCaptor.capture());
         reset(mListener);
@@ -902,7 +931,7 @@
         assertWithMessage("selectionChanged").that(update.selectionChanged).isFalse();
     }
 
-    private void assertSelectionChangedTo(Bubble bubble) {
+    private void assertSelectionChangedTo(BubbleViewProvider bubble) {
         BubbleData.Update update = mUpdateCaptor.getValue();
         assertWithMessage("selectionChanged").that(update.selectionChanged).isTrue();
         assertWithMessage("selectedBubble").that(update.selectedBubble).isEqualTo(bubble);
@@ -925,7 +954,6 @@
         assertThat(update.overflowBubbles).isEqualTo(bubbles);
     }
 
-
     private BubbleEntry createBubbleEntry(int userId, String notifKey, String packageName,
             NotificationListenerService.Ranking ranking) {
         return createBubbleEntry(userId, notifKey, packageName, ranking, 1000);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleFlyoutViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleFlyoutViewTest.java
index 6644eaf..5c1bcb9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleFlyoutViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleFlyoutViewTest.java
@@ -63,7 +63,7 @@
         mFlyoutMessage.senderName = "Josh";
         mFlyoutMessage.message = "Hello";
 
-        mFlyout = new BubbleFlyoutView(getContext());
+        mFlyout = new BubbleFlyoutView(getContext(), mPositioner);
 
         mFlyoutText = mFlyout.findViewById(R.id.bubble_flyout_text);
         mSenderName = mFlyout.findViewById(R.id.bubble_flyout_name);
@@ -75,9 +75,8 @@
     public void testShowFlyout_isVisible() {
         mFlyout.setupFlyoutStartingAsDot(
                 mFlyoutMessage,
-                new PointF(100, 100), 500, true, Color.WHITE, null, null, mDotCenter,
-                false,
-                mPositioner);
+                new PointF(100, 100), true, Color.WHITE, null, null, mDotCenter,
+                false);
         mFlyout.setVisibility(View.VISIBLE);
 
         assertEquals("Hello", mFlyoutText.getText());
@@ -89,9 +88,8 @@
     public void testFlyoutHide_runsCallback() {
         Runnable after = mock(Runnable.class);
         mFlyout.setupFlyoutStartingAsDot(mFlyoutMessage,
-                new PointF(100, 100), 500, true, Color.WHITE, null, after, mDotCenter,
-                false,
-                mPositioner);
+                new PointF(100, 100), true, Color.WHITE, null, after, mDotCenter,
+                false);
         mFlyout.hideFlyout();
 
         verify(after).run();
@@ -100,9 +98,8 @@
     @Test
     public void testSetCollapsePercent() {
         mFlyout.setupFlyoutStartingAsDot(mFlyoutMessage,
-                new PointF(100, 100), 500, true, Color.WHITE, null, null, mDotCenter,
-                false,
-                mPositioner);
+                new PointF(100, 100), true, Color.WHITE, null, null, mDotCenter,
+                false);
         mFlyout.setVisibility(View.VISIBLE);
 
         mFlyout.setCollapsePercent(1f);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
index 1eba3c2..9732a88 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationControllerTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 
 import android.annotation.SuppressLint;
 import android.content.res.Configuration;
@@ -41,7 +42,6 @@
 import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Spy;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -49,26 +49,26 @@
 
     private int mDisplayWidth = 500;
     private int mDisplayHeight = 1000;
-    private int mExpandedViewPadding = 10;
 
     private Runnable mOnBubbleAnimatedOutAction = mock(Runnable.class);
-    @Spy
     ExpandedAnimationController mExpandedController;
 
     private int mStackOffset;
     private PointF mExpansionPoint;
+    private BubblePositioner mPositioner;
 
     @SuppressLint("VisibleForTests")
     @Before
     public void setUp() throws Exception {
         super.setUp();
 
-        BubblePositioner positioner = new BubblePositioner(getContext(), mock(WindowManager.class));
-        positioner.updateInternal(Configuration.ORIENTATION_PORTRAIT,
+        mPositioner = new BubblePositioner(getContext(), mock(WindowManager.class));
+        mPositioner.updateInternal(Configuration.ORIENTATION_PORTRAIT,
                 Insets.of(0, 0, 0, 0),
                 new Rect(0, 0, mDisplayWidth, mDisplayHeight));
-        mExpandedController = new ExpandedAnimationController(positioner, mExpandedViewPadding,
+        mExpandedController = new ExpandedAnimationController(mPositioner,
                 mOnBubbleAnimatedOutAction);
+        spyOn(mExpandedController);
 
         addOneMoreThanBubbleLimitBubbles();
         mLayout.setActiveController(mExpandedController);
@@ -141,13 +141,16 @@
 
     /** Check that children are in the correct positions for being expanded. */
     private void testBubblesInCorrectExpandedPositions() {
+        boolean onLeft = mPositioner.isStackOnLeft(mExpansionPoint);
         // Check all the visible bubbles to see if they're in the right place.
         for (int i = 0; i < mLayout.getChildCount(); i++) {
-            float expectedPosition = mExpandedController.getBubbleXOrYForOrientation(i);
-            assertEquals(expectedPosition,
+            PointF expectedPosition = mPositioner.getExpandedBubbleXY(i,
+                    mLayout.getChildCount(),
+                    onLeft);
+            assertEquals(expectedPosition.x,
                     mLayout.getChildAt(i).getTranslationX(),
                     2f);
-            assertEquals(expectedPosition,
+            assertEquals(expectedPosition.y,
                     mLayout.getChildAt(i).getTranslationY(), 2f);
         }
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
index ef046d4..b888450 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
@@ -58,7 +58,7 @@
         mT = mock(SurfaceControl.Transaction.class);
         mMock = mock(IInputMethodManager.class);
         mExecutor = spy(Runnable::run);
-        mPerDisplay = new DisplayImeController(null, null, mExecutor, new TransactionPool() {
+        mPerDisplay = new DisplayImeController(null, null, null, mExecutor, new TransactionPool() {
             @Override
             public SurfaceControl.Transaction acquire() {
                 return mT;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java
new file mode 100644
index 0000000..b66c2b4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java
@@ -0,0 +1,193 @@
+/*
+ * 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.wm.shell.common;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.notNull;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.RemoteException;
+import android.util.SparseArray;
+import android.view.IDisplayWindowInsetsController;
+import android.view.IWindowManager;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.TestShellExecutor;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+@SmallTest
+public class DisplayInsetsControllerTest {
+
+    private static final int SECOND_DISPLAY = DEFAULT_DISPLAY + 10;
+
+    @Mock
+    private IWindowManager mWm;
+    @Mock
+    private DisplayController mDisplayController;
+    private DisplayInsetsController mController;
+    private SparseArray<IDisplayWindowInsetsController> mInsetsControllersByDisplayId;
+    private TestShellExecutor mExecutor;
+
+    private ArgumentCaptor<Integer> mDisplayIdCaptor;
+    private ArgumentCaptor<IDisplayWindowInsetsController> mInsetsControllerCaptor;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mExecutor = new TestShellExecutor();
+        mInsetsControllersByDisplayId = new SparseArray<>();
+        mDisplayIdCaptor =  ArgumentCaptor.forClass(Integer.class);
+        mInsetsControllerCaptor = ArgumentCaptor.forClass(IDisplayWindowInsetsController.class);
+        mController = new DisplayInsetsController(mWm, mDisplayController, mExecutor);
+        addDisplay(DEFAULT_DISPLAY);
+    }
+
+    @Test
+    public void testOnDisplayAdded_setsDisplayWindowInsetsControllerOnWMService()
+            throws RemoteException {
+        addDisplay(SECOND_DISPLAY);
+
+        verify(mWm).setDisplayWindowInsetsController(eq(SECOND_DISPLAY), notNull());
+    }
+
+    @Test
+    public void testOnDisplayRemoved_unsetsDisplayWindowInsetsControllerInWMService()
+            throws RemoteException {
+        addDisplay(SECOND_DISPLAY);
+        removeDisplay(SECOND_DISPLAY);
+
+        verify(mWm).setDisplayWindowInsetsController(SECOND_DISPLAY, null);
+    }
+
+    @Test
+    public void testPerDisplayListenerCallback() throws RemoteException {
+        TrackedListener defaultListener = new TrackedListener();
+        TrackedListener secondListener = new TrackedListener();
+        addDisplay(SECOND_DISPLAY);
+        mController.addInsetsChangedListener(DEFAULT_DISPLAY, defaultListener);
+        mController.addInsetsChangedListener(SECOND_DISPLAY, secondListener);
+
+        mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).topFocusedWindowChanged(null);
+        mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).insetsChanged(null);
+        mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).insetsControlChanged(null, null);
+        mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).showInsets(0, false);
+        mInsetsControllersByDisplayId.get(DEFAULT_DISPLAY).hideInsets(0, false);
+        mExecutor.flushAll();
+
+        assertTrue(defaultListener.topFocusedWindowChangedCount == 1);
+        assertTrue(defaultListener.insetsChangedCount == 1);
+        assertTrue(defaultListener.insetsControlChangedCount == 1);
+        assertTrue(defaultListener.showInsetsCount == 1);
+        assertTrue(defaultListener.hideInsetsCount == 1);
+
+        assertTrue(secondListener.topFocusedWindowChangedCount == 0);
+        assertTrue(secondListener.insetsChangedCount == 0);
+        assertTrue(secondListener.insetsControlChangedCount == 0);
+        assertTrue(secondListener.showInsetsCount == 0);
+        assertTrue(secondListener.hideInsetsCount == 0);
+
+        mInsetsControllersByDisplayId.get(SECOND_DISPLAY).topFocusedWindowChanged(null);
+        mInsetsControllersByDisplayId.get(SECOND_DISPLAY).insetsChanged(null);
+        mInsetsControllersByDisplayId.get(SECOND_DISPLAY).insetsControlChanged(null, null);
+        mInsetsControllersByDisplayId.get(SECOND_DISPLAY).showInsets(0, false);
+        mInsetsControllersByDisplayId.get(SECOND_DISPLAY).hideInsets(0, false);
+        mExecutor.flushAll();
+
+        assertTrue(defaultListener.topFocusedWindowChangedCount == 1);
+        assertTrue(defaultListener.insetsChangedCount == 1);
+        assertTrue(defaultListener.insetsControlChangedCount == 1);
+        assertTrue(defaultListener.showInsetsCount == 1);
+        assertTrue(defaultListener.hideInsetsCount == 1);
+
+        assertTrue(secondListener.topFocusedWindowChangedCount == 1);
+        assertTrue(secondListener.insetsChangedCount == 1);
+        assertTrue(secondListener.insetsControlChangedCount == 1);
+        assertTrue(secondListener.showInsetsCount == 1);
+        assertTrue(secondListener.hideInsetsCount == 1);
+    }
+
+    private void addDisplay(int displayId) throws RemoteException {
+        mController.onDisplayAdded(displayId);
+        verify(mWm, times(mInsetsControllersByDisplayId.size() + 1))
+                .setDisplayWindowInsetsController(mDisplayIdCaptor.capture(),
+                        mInsetsControllerCaptor.capture());
+        List<Integer> displayIds = mDisplayIdCaptor.getAllValues();
+        List<IDisplayWindowInsetsController> insetsControllers =
+                mInsetsControllerCaptor.getAllValues();
+        for (int i = 0; i < displayIds.size(); i++) {
+            mInsetsControllersByDisplayId.put(displayIds.get(i), insetsControllers.get(i));
+        }
+    }
+
+    private void removeDisplay(int displayId) {
+        mController.onDisplayRemoved(displayId);
+        mInsetsControllersByDisplayId.remove(displayId);
+    }
+
+    private static class TrackedListener implements
+            DisplayInsetsController.OnInsetsChangedListener {
+        int topFocusedWindowChangedCount = 0;
+        int insetsChangedCount = 0;
+        int insetsControlChangedCount = 0;
+        int showInsetsCount = 0;
+        int hideInsetsCount = 0;
+
+        @Override
+        public void topFocusedWindowChanged(String packageName) {
+            topFocusedWindowChangedCount++;
+        }
+
+        @Override
+        public void insetsChanged(InsetsState insetsState) {
+            insetsChangedCount++;
+        }
+
+        @Override
+        public void insetsControlChanged(InsetsState insetsState,
+                InsetsSourceControl[] activeControls) {
+            insetsControlChangedCount++;
+        }
+
+        @Override
+        public void showInsets(int types, boolean fromIme) {
+            showInsetsCount++;
+        }
+
+        @Override
+        public void hideInsets(int types, boolean fromIme) {
+            hideInsetsCount++;
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index e138595..3557906 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -24,6 +24,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
 import android.content.res.Configuration;
@@ -42,6 +43,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -53,19 +56,20 @@
     @Mock SurfaceControl mRootLeash;
     @Mock DisplayImeController mDisplayImeController;
     @Mock ShellTaskOrganizer mTaskOrganizer;
+    @Captor ArgumentCaptor<Runnable> mRunnableCaptor;
     private SplitLayout mSplitLayout;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        mSplitLayout = new SplitLayout(
+        mSplitLayout = spy(new SplitLayout(
                 "TestSplitLayout",
                 mContext,
                 getConfiguration(),
                 mSplitLayoutHandler,
                 b -> b.setParent(mRootLeash),
                 mDisplayImeController,
-                mTaskOrganizer);
+                mTaskOrganizer));
     }
 
     @Test
@@ -92,13 +96,13 @@
     @Test
     public void testUpdateDivideBounds() {
         mSplitLayout.updateDivideBounds(anyInt());
-        verify(mSplitLayoutHandler).onBoundsChanging(any(SplitLayout.class));
+        verify(mSplitLayoutHandler).onLayoutChanging(any(SplitLayout.class));
     }
 
     @Test
     public void testSetDividePosition() {
         mSplitLayout.setDividePosition(anyInt());
-        verify(mSplitLayoutHandler).onBoundsChanged(any(SplitLayout.class));
+        verify(mSplitLayoutHandler).onLayoutChanged(any(SplitLayout.class));
     }
 
     @Test
@@ -109,18 +113,33 @@
 
     @Test
     @UiThreadTest
-    public void testSnapToDismissTarget() {
+    public void testSnapToDismissStart() {
         // verify it callbacks properly when the snap target indicates dismissing split.
         DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */,
                 DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START);
+
         mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget);
+        waitDividerFlingFinished();
         verify(mSplitLayoutHandler).onSnappedToDismiss(eq(false));
-        snapTarget = getSnapTarget(0 /* position */,
+    }
+
+    @Test
+    @UiThreadTest
+    public void testSnapToDismissEnd() {
+        // verify it callbacks properly when the snap target indicates dismissing split.
+        DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */,
                 DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END);
+
         mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget);
+        waitDividerFlingFinished();
         verify(mSplitLayoutHandler).onSnappedToDismiss(eq(true));
     }
 
+    private void waitDividerFlingFinished() {
+        verify(mSplitLayout).flingDividePosition(anyInt(), anyInt(), mRunnableCaptor.capture());
+        mRunnableCaptor.getValue().run();
+    }
+
     private static Configuration getConfiguration() {
         final Configuration configuration = new Configuration();
         configuration.unset();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
index 1a70f76..734b97b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
@@ -65,6 +65,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.logging.InstanceId;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.draganddrop.DragAndDropPolicy.Target;
 import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -96,6 +97,9 @@
     @Mock
     private SplitScreenController mSplitScreenStarter;
 
+    @Mock
+    private InstanceId mLoggerSessionId;
+
     private DisplayLayout mLandscapeDisplayLayout;
     private DisplayLayout mPortraitDisplayLayout;
     private Insets mInsets;
@@ -201,7 +205,7 @@
     @Test
     public void testDragAppOverFullscreenHome_expectOnlyFullscreenTarget() {
         setRunningTask(mHomeTask);
-        mPolicy.start(mLandscapeDisplayLayout, mActivityClipData);
+        mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId);
         ArrayList<Target> targets = assertExactTargetTypes(
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN);
 
@@ -213,7 +217,7 @@
     @Test
     public void testDragAppOverFullscreenApp_expectSplitScreenTargets() {
         setRunningTask(mFullscreenAppTask);
-        mPolicy.start(mLandscapeDisplayLayout, mActivityClipData);
+        mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId);
         ArrayList<Target> targets = assertExactTargetTypes(
                 mPolicy.getTargets(mInsets), TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
 
@@ -230,7 +234,7 @@
     @Test
     public void testDragAppOverFullscreenAppPhone_expectVerticalSplitScreenTargets() {
         setRunningTask(mFullscreenAppTask);
-        mPolicy.start(mPortraitDisplayLayout, mActivityClipData);
+        mPolicy.start(mPortraitDisplayLayout, mActivityClipData, mLoggerSessionId);
         ArrayList<Target> targets = assertExactTargetTypes(
                 mPolicy.getTargets(mInsets), TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
 
@@ -248,7 +252,7 @@
     public void testDragAppOverSplitApp_expectSplitTargets_DropLeft() {
         setInSplitScreen(true);
         setRunningTask(mSplitPrimaryAppTask);
-        mPolicy.start(mLandscapeDisplayLayout, mActivityClipData);
+        mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId);
         ArrayList<Target> targets = assertExactTargetTypes(
                 mPolicy.getTargets(mInsets), TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
 
@@ -261,7 +265,7 @@
     public void testDragAppOverSplitApp_expectSplitTargets_DropRight() {
         setInSplitScreen(true);
         setRunningTask(mSplitPrimaryAppTask);
-        mPolicy.start(mLandscapeDisplayLayout, mActivityClipData);
+        mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId);
         ArrayList<Target> targets = assertExactTargetTypes(
                 mPolicy.getTargets(mInsets), TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
 
@@ -274,7 +278,7 @@
     public void testDragAppOverSplitAppPhone_expectVerticalSplitTargets_DropTop() {
         setInSplitScreen(true);
         setRunningTask(mSplitPrimaryAppTask);
-        mPolicy.start(mPortraitDisplayLayout, mActivityClipData);
+        mPolicy.start(mPortraitDisplayLayout, mActivityClipData, mLoggerSessionId);
         ArrayList<Target> targets = assertExactTargetTypes(
                 mPolicy.getTargets(mInsets), TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
 
@@ -287,7 +291,7 @@
     public void testDragAppOverSplitAppPhone_expectVerticalSplitTargets_DropBottom() {
         setInSplitScreen(true);
         setRunningTask(mSplitPrimaryAppTask);
-        mPolicy.start(mPortraitDisplayLayout, mActivityClipData);
+        mPolicy.start(mPortraitDisplayLayout, mActivityClipData, mLoggerSessionId);
         ArrayList<Target> targets = assertExactTargetTypes(
                 mPolicy.getTargets(mInsets), TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
 
@@ -299,7 +303,7 @@
     @Test
     public void testTargetHitRects() {
         setRunningTask(mFullscreenAppTask);
-        mPolicy.start(mLandscapeDisplayLayout, mActivityClipData);
+        mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId);
         ArrayList<Target> targets = mPolicy.getTargets(mInsets);
         for (Target t : targets) {
             assertTrue(mPolicy.getTargetAtLocation(t.hitRegion.left, t.hitRegion.top) == t);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java
index 3f47c04..7b9553c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java
@@ -19,10 +19,16 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL;
 
+import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE;
+import static com.android.wm.shell.onehanded.OneHandedState.STATE_NONE;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.testing.AndroidTestingRunner;
@@ -54,17 +60,17 @@
     private OneHandedBackgroundPanelOrganizer mSpiedBackgroundPanelOrganizer;
     private WindowContainerToken mToken;
     private SurfaceControl mLeash;
-    private TestableLooper mTestableLooper;
 
     @Mock
     IWindowContainerToken mMockRealToken;
     @Mock
     DisplayController mMockDisplayController;
+    @Mock
+    OneHandedSettingsUtil mMockSettingsUtil;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mTestableLooper = TestableLooper.get(this);
         mToken = new WindowContainerToken(mMockRealToken);
         mLeash = new SurfaceControl();
         mDisplay = mContext.getDisplay();
@@ -74,32 +80,52 @@
                 FEATURE_ONE_HANDED_BACKGROUND_PANEL);
 
         mSpiedBackgroundPanelOrganizer = spy(
-                new OneHandedBackgroundPanelOrganizer(mContext, mDisplayLayout, Runnable::run));
+                new OneHandedBackgroundPanelOrganizer(mContext, mDisplayLayout, mMockSettingsUtil,
+                        Runnable::run));
+        mSpiedBackgroundPanelOrganizer.onDisplayChanged(mDisplayLayout);
     }
 
     @Test
     public void testOnDisplayAreaAppeared() {
         mSpiedBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
-        mTestableLooper.processAllMessages();
 
-        assertThat(mSpiedBackgroundPanelOrganizer.getBackgroundSurface()).isNotNull();
+        assertThat(mSpiedBackgroundPanelOrganizer.isRegistered()).isTrue();
+        verify(mSpiedBackgroundPanelOrganizer, never()).showBackgroundPanelLayer();
     }
 
     @Test
     public void testShowBackgroundLayer() {
-        mSpiedBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
-        mSpiedBackgroundPanelOrganizer.showBackgroundPanelLayer();
-        mTestableLooper.processAllMessages();
+        mSpiedBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, null);
+        mSpiedBackgroundPanelOrganizer.onStart();
 
-        assertThat(mSpiedBackgroundPanelOrganizer.mIsShowing).isTrue();
+        verify(mSpiedBackgroundPanelOrganizer).showBackgroundPanelLayer();
     }
 
     @Test
     public void testRemoveBackgroundLayer() {
         mSpiedBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
-        mSpiedBackgroundPanelOrganizer.removeBackgroundPanelLayer();
-        mTestableLooper.processAllMessages();
 
-        assertThat(mSpiedBackgroundPanelOrganizer.mIsShowing).isFalse();
+        assertThat(mSpiedBackgroundPanelOrganizer.isRegistered()).isNotNull();
+
+        reset(mSpiedBackgroundPanelOrganizer);
+        mSpiedBackgroundPanelOrganizer.removeBackgroundPanelLayer();
+
+        assertThat(mSpiedBackgroundPanelOrganizer.mBackgroundSurface).isNull();
+    }
+
+    @Test
+    public void testStateNone_onConfigurationChanged() {
+        mSpiedBackgroundPanelOrganizer.onStateChanged(STATE_NONE);
+        mSpiedBackgroundPanelOrganizer.onConfigurationChanged();
+
+        verify(mSpiedBackgroundPanelOrganizer, never()).showBackgroundPanelLayer();
+    }
+
+    @Test
+    public void testStateActivate_onConfigurationChanged() {
+        mSpiedBackgroundPanelOrganizer.onStateChanged(STATE_ACTIVE);
+        mSpiedBackgroundPanelOrganizer.onConfigurationChanged();
+
+        verify(mSpiedBackgroundPanelOrganizer).showBackgroundPanelLayer();
     }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index b224ae6..911fe07 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -110,7 +110,7 @@
         when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
         when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>());
         when(mMockDisplayAreaOrganizer.isReady()).thenReturn(true);
-        when(mMockBackgroundOrganizer.getBackgroundSurface()).thenReturn(mMockLeash);
+        when(mMockBackgroundOrganizer.isRegistered()).thenReturn(true);
         when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn(
                 mDefaultEnabled);
         when(mMockSettingsUitl.getSettingsOneHandedModeTimeout(any(), anyInt())).thenReturn(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
index e61f061..bea69c5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
@@ -102,7 +102,7 @@
 
         when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
         when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>());
-        when(mMockBackgroundOrganizer.getBackgroundSurface()).thenReturn(mMockLeash);
+        when(mMockBackgroundOrganizer.isRegistered()).thenReturn(true);
         when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn(
                 mDefaultEnabled);
         when(mMockSettingsUitl.getSettingsOneHandedModeTimeout(any(), anyInt())).thenReturn(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
index ae1d3b2..b1434ca 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
@@ -66,7 +66,7 @@
         mDisplayLayout = new DisplayLayout(mContext, mDisplay);
         mSpiedTransitionState = spy(new OneHandedState());
         mSpiedTutorialHandler = spy(
-                new OneHandedTutorialHandler(mContext, mMockWindowManager));
+                new OneHandedTutorialHandler(mContext, mMockSettingsUtil, mMockWindowManager));
         mTimeoutHandler = new OneHandedTimeoutHandler(mMockShellMainExecutor);
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index ab6f7699..b0a39d6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -65,9 +65,10 @@
         TestStageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
                 RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer,
                 MainStage mainStage, SideStage sideStage, DisplayImeController imeController,
-                SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool) {
+                SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool,
+                SplitscreenEventLogger logger) {
             super(context, displayId, syncQueue, rootTDAOrganizer, taskOrganizer, mainStage,
-                    sideStage, imeController, splitLayout, transitions, transactionPool);
+                    sideStage, imeController, splitLayout, transitions, transactionPool, logger);
 
             // Prepare default TaskDisplayArea for testing.
             mDisplayAreaInfo = new DisplayAreaInfo(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index b6da868..cb759dc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -82,6 +82,7 @@
     @Mock private TransactionPool mTransactionPool;
     @Mock private Transitions mTransitions;
     @Mock private SurfaceSession mSurfaceSession;
+    @Mock private SplitscreenEventLogger mLogger;
     private SplitLayout mSplitLayout;
     private MainStage mMainStage;
     private SideStage mSideStage;
@@ -107,7 +108,8 @@
         mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
         mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
                     mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
-                    mDisplayImeController, mSplitLayout, mTransitions, mTransactionPool);
+                    mDisplayImeController, mSplitLayout, mTransitions, mTransactionPool,
+                    mLogger);
         mSplitScreenTransitions = mStageCoordinator.getSplitTransitions();
         doAnswer((Answer<IBinder>) invocation -> mock(IBinder.class))
                 .when(mTransitions).startTransition(anyInt(), any(), any());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 06b0868..a4b76fb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -59,6 +59,7 @@
     @Mock private DisplayImeController mDisplayImeController;
     @Mock private Transitions mTransitions;
     @Mock private TransactionPool mTransactionPool;
+    @Mock private SplitscreenEventLogger mLogger;
     private StageCoordinator mStageCoordinator;
 
     @Before
@@ -66,7 +67,8 @@
         MockitoAnnotations.initMocks(this);
         mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
                 mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,
-                mDisplayImeController, null /* splitLayout */, mTransitions, mTransactionPool);
+                mDisplayImeController, null /* splitLayout */, mTransitions, mTransactionPool,
+                mLogger);
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
index d536adb..160b367 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
@@ -42,6 +42,7 @@
 import android.os.Looper;
 import android.os.UserHandle;
 import android.testing.TestableContext;
+import android.view.Display;
 import android.view.SurfaceControl;
 import android.view.View;
 import android.view.WindowManager;
@@ -92,8 +93,8 @@
         }
 
         @Override
-        protected boolean addWindow(int taskId, IBinder appToken,
-                View view, WindowManager wm, WindowManager.LayoutParams params, int suggestType) {
+        protected boolean addWindow(int taskId, IBinder appToken, View view, Display display,
+                WindowManager.LayoutParams params, int suggestType) {
             // listen for addView
             mAddWindowForTask = taskId;
             mViewThemeResId = view.getContext().getThemeResId();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index a2b1f64..54eacee 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -20,12 +20,20 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS;
+import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
+import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 
@@ -48,6 +56,9 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.view.IDisplayWindowListener;
+import android.view.IWindowManager;
+import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 import android.window.IRemoteTransition;
@@ -65,17 +76,23 @@
 import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.wm.shell.TestShellExecutor;
+import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.TransactionPool;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
 
 import java.util.ArrayList;
 
 /**
  * Tests for the shell transitions.
+ *
+ * Build/Install/Run:
+ *  atest WMShellUnitTests:ShellTransitionTests
  */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -97,8 +114,7 @@
 
     @Test
     public void testBasicTransitionFlow() {
-        Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
-                mMainExecutor, mAnimExecutor);
+        Transitions transitions = createTestTransitions();
         transitions.replaceDefaultHandlerForTest(mDefaultHandler);
 
         IBinder transitToken = new Binder();
@@ -117,8 +133,7 @@
 
     @Test
     public void testNonDefaultHandler() {
-        Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
-                mMainExecutor, mAnimExecutor);
+        Transitions transitions = createTestTransitions();
         transitions.replaceDefaultHandlerForTest(mDefaultHandler);
 
         final WindowContainerTransaction handlerWCT = new WindowContainerTransaction();
@@ -201,8 +216,7 @@
 
     @Test
     public void testRequestRemoteTransition() {
-        Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
-                mMainExecutor, mAnimExecutor);
+        Transitions transitions = createTestTransitions();
         transitions.replaceDefaultHandlerForTest(mDefaultHandler);
 
         final boolean[] remoteCalled = new boolean[]{false};
@@ -275,9 +289,76 @@
     }
 
     @Test
+    public void testTransitionFilterNotRequirement() {
+        // filter that requires one opening and NO translucent apps
+        TransitionFilter filter = new TransitionFilter();
+        filter.mRequirements = new TransitionFilter.Requirement[]{
+                new TransitionFilter.Requirement(), new TransitionFilter.Requirement()};
+        filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
+        filter.mRequirements[1].mFlags = FLAG_TRANSLUCENT;
+        filter.mRequirements[1].mNot = true;
+
+        final TransitionInfo openOnly = new TransitionInfoBuilder(TRANSIT_OPEN)
+                .addChange(TRANSIT_OPEN).build();
+        assertTrue(filter.matches(openOnly));
+
+        final TransitionInfo openAndTranslucent = new TransitionInfoBuilder(TRANSIT_OPEN)
+                .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
+        openAndTranslucent.getChanges().get(1).setFlags(FLAG_TRANSLUCENT);
+        assertFalse(filter.matches(openAndTranslucent));
+    }
+
+    @Test
+    public void testTransitionFilterChecksTypeSet() {
+        TransitionFilter filter = new TransitionFilter();
+        filter.mTypeSet = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
+
+        final TransitionInfo openOnly = new TransitionInfoBuilder(TRANSIT_OPEN)
+                .addChange(TRANSIT_OPEN).build();
+        assertTrue(filter.matches(openOnly));
+
+        final TransitionInfo toFrontOnly = new TransitionInfoBuilder(TRANSIT_TO_FRONT)
+                .addChange(TRANSIT_TO_FRONT).build();
+        assertTrue(filter.matches(toFrontOnly));
+
+        final TransitionInfo closeOnly = new TransitionInfoBuilder(TRANSIT_CLOSE)
+                .addChange(TRANSIT_CLOSE).build();
+        assertFalse(filter.matches(closeOnly));
+    }
+
+    @Test
+    public void testTransitionFilterChecksFlags() {
+        TransitionFilter filter = new TransitionFilter();
+        filter.mFlags = TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
+
+        final TransitionInfo withFlag = new TransitionInfoBuilder(TRANSIT_TO_BACK,
+                TRANSIT_FLAG_KEYGUARD_GOING_AWAY)
+                .addChange(TRANSIT_TO_BACK).build();
+        assertTrue(filter.matches(withFlag));
+
+        final TransitionInfo withoutFlag = new TransitionInfoBuilder(TRANSIT_OPEN)
+                .addChange(TRANSIT_OPEN).build();
+        assertFalse(filter.matches(withoutFlag));
+    }
+
+    @Test
+    public void testTransitionFilterChecksNotFlags() {
+        TransitionFilter filter = new TransitionFilter();
+        filter.mNotFlags = TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
+
+        final TransitionInfo withFlag = new TransitionInfoBuilder(TRANSIT_TO_BACK,
+                TRANSIT_FLAG_KEYGUARD_GOING_AWAY)
+                .addChange(TRANSIT_TO_BACK).build();
+        assertFalse(filter.matches(withFlag));
+
+        final TransitionInfo withoutFlag = new TransitionInfoBuilder(TRANSIT_OPEN)
+                .addChange(TRANSIT_OPEN).build();
+        assertTrue(filter.matches(withoutFlag));
+    }
+
+    @Test
     public void testRegisteredRemoteTransition() {
-        Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
-                mMainExecutor, mAnimExecutor);
+        Transitions transitions = createTestTransitions();
         transitions.replaceDefaultHandlerForTest(mDefaultHandler);
 
         final boolean[] remoteCalled = new boolean[]{false};
@@ -322,8 +403,7 @@
 
     @Test
     public void testOneShotRemoteHandler() {
-        Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
-                mMainExecutor, mAnimExecutor);
+        Transitions transitions = createTestTransitions();
         transitions.replaceDefaultHandlerForTest(mDefaultHandler);
 
         final boolean[] remoteCalled = new boolean[]{false};
@@ -369,8 +449,7 @@
 
     @Test
     public void testTransitionQueueing() {
-        Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
-                mMainExecutor, mAnimExecutor);
+        Transitions transitions = createTestTransitions();
         transitions.replaceDefaultHandlerForTest(mDefaultHandler);
 
         IBinder transitToken1 = new Binder();
@@ -410,8 +489,7 @@
 
     @Test
     public void testTransitionMerging() {
-        Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
-                mMainExecutor, mAnimExecutor);
+        Transitions transitions = createTestTransitions();
         mDefaultHandler.setSimulateMerge(true);
         transitions.replaceDefaultHandlerForTest(mDefaultHandler);
 
@@ -447,11 +525,73 @@
         assertEquals(0, mDefaultHandler.activeCount());
     }
 
+    @Test
+    public void testShouldRotateSeamlessly() throws Exception {
+        final RunningTaskInfo taskInfo =
+                createTaskInfo(1, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        final RunningTaskInfo taskInfoPip =
+                createTaskInfo(1, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+
+        final DisplayController displays = createTestDisplayController();
+        final @Surface.Rotation int upsideDown = displays
+                .getDisplayLayout(DEFAULT_DISPLAY).getUpsideDownRotation();
+
+        final TransitionInfo normalDispRotate = new TransitionInfoBuilder(TRANSIT_CHANGE)
+                .addChange(new ChangeBuilder(TRANSIT_CHANGE).setFlags(FLAG_IS_DISPLAY).setRotate()
+                        .build())
+                .addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo).setRotate().build())
+                .build();
+        assertFalse(DefaultTransitionHandler.isRotationSeamless(normalDispRotate, displays));
+
+        // Seamless if all tasks are seamless
+        final TransitionInfo rotateSeamless = new TransitionInfoBuilder(TRANSIT_CHANGE)
+                .addChange(new ChangeBuilder(TRANSIT_CHANGE).setFlags(FLAG_IS_DISPLAY).setRotate()
+                        .build())
+                .addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo)
+                        .setRotate(ROTATION_ANIMATION_SEAMLESS).build())
+                .build();
+        assertTrue(DefaultTransitionHandler.isRotationSeamless(rotateSeamless, displays));
+
+        // Not seamless if there is PiP (or any other non-seamless task)
+        final TransitionInfo pipDispRotate = new TransitionInfoBuilder(TRANSIT_CHANGE)
+                .addChange(new ChangeBuilder(TRANSIT_CHANGE).setFlags(FLAG_IS_DISPLAY).setRotate()
+                        .build())
+                .addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo)
+                        .setRotate(ROTATION_ANIMATION_SEAMLESS).build())
+                .addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfoPip)
+                        .setRotate().build())
+                .build();
+        assertFalse(DefaultTransitionHandler.isRotationSeamless(pipDispRotate, displays));
+
+        // Not seamless if one of rotations is upside-down
+        final TransitionInfo seamlessUpsideDown = new TransitionInfoBuilder(TRANSIT_CHANGE)
+                .addChange(new ChangeBuilder(TRANSIT_CHANGE).setFlags(FLAG_IS_DISPLAY)
+                        .setRotate(upsideDown, ROTATION_ANIMATION_UNSPECIFIED).build())
+                .addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo)
+                        .setRotate(upsideDown, ROTATION_ANIMATION_SEAMLESS).build())
+                .build();
+        assertFalse(DefaultTransitionHandler.isRotationSeamless(seamlessUpsideDown, displays));
+
+        // Not seamless if system alert windows
+        final TransitionInfo seamlessButAlert = new TransitionInfoBuilder(TRANSIT_CHANGE)
+                .addChange(new ChangeBuilder(TRANSIT_CHANGE).setFlags(
+                        FLAG_IS_DISPLAY | FLAG_DISPLAY_HAS_ALERT_WINDOWS).setRotate().build())
+                .addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo)
+                        .setRotate(ROTATION_ANIMATION_SEAMLESS).build())
+                .build();
+        assertFalse(DefaultTransitionHandler.isRotationSeamless(seamlessButAlert, displays));
+    }
+
     class TransitionInfoBuilder {
         final TransitionInfo mInfo;
 
         TransitionInfoBuilder(@WindowManager.TransitionType int type) {
-            mInfo = new TransitionInfo(type, 0 /* flags */);
+            this(type, 0 /* flags */);
+        }
+
+        TransitionInfoBuilder(@WindowManager.TransitionType int type,
+                @WindowManager.TransitionFlags int flags) {
+            mInfo = new TransitionInfo(type, flags);
             mInfo.setRootLeash(createMockSurface(true /* valid */), 0, 0);
         }
 
@@ -469,11 +609,53 @@
             return addChange(mode, null /* taskInfo */);
         }
 
+        TransitionInfoBuilder addChange(TransitionInfo.Change change) {
+            mInfo.addChange(change);
+            return this;
+        }
+
         TransitionInfo build() {
             return mInfo;
         }
     }
 
+    class ChangeBuilder {
+        final TransitionInfo.Change mChange;
+
+        ChangeBuilder(@WindowManager.TransitionType int mode) {
+            mChange = new TransitionInfo.Change(null /* token */, null /* leash */);
+            mChange.setMode(mode);
+        }
+
+        ChangeBuilder setFlags(@TransitionInfo.ChangeFlags int flags) {
+            mChange.setFlags(flags);
+            return this;
+        }
+
+        ChangeBuilder setTask(RunningTaskInfo taskInfo) {
+            mChange.setTaskInfo(taskInfo);
+            return this;
+        }
+
+        ChangeBuilder setRotate(int anim) {
+            return setRotate(Surface.ROTATION_90, anim);
+        }
+
+        ChangeBuilder setRotate() {
+            return setRotate(ROTATION_ANIMATION_UNSPECIFIED);
+        }
+
+        ChangeBuilder setRotate(@Surface.Rotation int target, int anim) {
+            mChange.setRotation(Surface.ROTATION_0, target);
+            mChange.setRotationAnimation(anim);
+            return this;
+        }
+
+        TransitionInfo.Change build() {
+            return mChange;
+        }
+    }
+
     class TestTransitionHandler implements Transitions.TransitionHandler {
         ArrayList<Transitions.TransitionFinishCallback> mFinishes = new ArrayList<>();
         final ArrayList<IBinder> mMerged = new ArrayList<>();
@@ -545,4 +727,46 @@
         return taskInfo;
     }
 
+    private DisplayController createTestDisplayController() {
+        IWindowManager mockWM = mock(IWindowManager.class);
+        final IDisplayWindowListener[] displayListener = new IDisplayWindowListener[1];
+        try {
+            doAnswer(new Answer() {
+                @Override
+                public Object answer(InvocationOnMock invocation) {
+                    displayListener[0] = invocation.getArgument(0);
+                    return null;
+                }
+            }).when(mockWM).registerDisplayWindowListener(any());
+        } catch (RemoteException e) {
+            // No remote stuff happening, so this can't be hit
+        }
+        DisplayController out = new DisplayController(mContext, mockWM, mMainExecutor);
+        out.initialize();
+        try {
+            displayListener[0].onDisplayAdded(DEFAULT_DISPLAY);
+            mMainExecutor.flushAll();
+        } catch (RemoteException e) {
+            // Again, no remote stuff
+        }
+        return out;
+    }
+
+    private Transitions createTestTransitions() {
+        return new Transitions(mOrganizer, mTransactionPool, createTestDisplayController(),
+                mContext, mMainExecutor, mAnimExecutor);
+    }
+//
+//    private class TestDisplayController extends DisplayController {
+//        private final DisplayLayout mTestDisplayLayout;
+//        TestDisplayController() {
+//            super(mContext, mock(IWindowManager.class), mMainExecutor);
+//            mTestDisplayLayout = new DisplayLayout();
+//            mTestDisplayLayout.
+//        }
+//
+//        @Override
+//        DisplayLayout
+//    }
+
 }
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index b8fa55a..109b535 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -84,7 +84,7 @@
 bool Properties::useHintManager = true;
 int Properties::targetCpuTimePercentage = 70;
 
-bool Properties::enableWebViewOverlays = false;
+bool Properties::enableWebViewOverlays = true;
 
 StretchEffectBehavior Properties::stretchEffectBehavior = StretchEffectBehavior::ShaderHWUI;
 
@@ -139,7 +139,7 @@
     targetCpuTimePercentage = base::GetIntProperty(PROPERTY_TARGET_CPU_TIME_PERCENTAGE, 70);
     if (targetCpuTimePercentage <= 0 || targetCpuTimePercentage > 100) targetCpuTimePercentage = 70;
 
-    enableWebViewOverlays = base::GetBoolProperty(PROPERTY_WEBVIEW_OVERLAYS_ENABLED, false);
+    enableWebViewOverlays = base::GetBoolProperty(PROPERTY_WEBVIEW_OVERLAYS_ENABLED, true);
 
     return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw);
 }
diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp
index df41011..5aad821 100644
--- a/libs/hwui/WebViewFunctorManager.cpp
+++ b/libs/hwui/WebViewFunctorManager.cpp
@@ -100,6 +100,9 @@
     destroyContext();
 
     ATRACE_NAME("WebViewFunctor::onDestroy");
+    if (mSurfaceControl) {
+        removeOverlays();
+    }
     mCallbacks.onDestroyed(mFunctor, mData);
 }
 
diff --git a/libs/hwui/apex/android_matrix.cpp b/libs/hwui/apex/android_matrix.cpp
index 693b22b..04ac3cf 100644
--- a/libs/hwui/apex/android_matrix.cpp
+++ b/libs/hwui/apex/android_matrix.cpp
@@ -35,3 +35,10 @@
     }
     return false;
 }
+
+jobject AMatrix_newInstance(JNIEnv* env, float values[9]) {
+    jobject matrixObj = android::android_graphics_Matrix_newInstance(env);
+    SkMatrix* m = android::android_graphics_Matrix_getSkMatrix(env, matrixObj);
+    m->set9(values);
+    return matrixObj;
+}
diff --git a/libs/hwui/apex/include/android/graphics/matrix.h b/libs/hwui/apex/include/android/graphics/matrix.h
index 987ad13..5705ba4 100644
--- a/libs/hwui/apex/include/android/graphics/matrix.h
+++ b/libs/hwui/apex/include/android/graphics/matrix.h
@@ -34,6 +34,16 @@
  */
 ANDROID_API bool AMatrix_getContents(JNIEnv* env, jobject matrixObj, float values[9]);
 
+/**
+ * Returns a new Matrix jobject that contains the values passed in as initial values.
+ * @param values The 9 values of the 3x3 matrix in the following order.
+ *               values[0] = scaleX  values[1] = skewX   values[2] = transX
+ *               values[3] = skewY   values[4] = scaleY  values[5] = transY
+ *               values[6] = persp0  values[7] = persp1  values[8] = persp2
+ * @return The matrix jobject
+ */
+ANDROID_API jobject AMatrix_newInstance(JNIEnv* env, float values[9]);
+
 __END_DECLS
 
 #endif // ANDROID_GRAPHICS_MATRIX_H
diff --git a/libs/hwui/jni/android_graphics_Matrix.cpp b/libs/hwui/jni/android_graphics_Matrix.cpp
index 7338ef2..cf6702e 100644
--- a/libs/hwui/jni/android_graphics_Matrix.cpp
+++ b/libs/hwui/jni/android_graphics_Matrix.cpp
@@ -378,13 +378,17 @@
     {"nEquals", "(JJ)Z", (void*) SkMatrixGlue::equals}
 };
 
+static jclass sClazz;
 static jfieldID sNativeInstanceField;
+static jmethodID sCtor;
 
 int register_android_graphics_Matrix(JNIEnv* env) {
     int result = RegisterMethodsOrDie(env, "android/graphics/Matrix", methods, NELEM(methods));
 
     jclass clazz = FindClassOrDie(env, "android/graphics/Matrix");
+    sClazz = MakeGlobalRefOrDie(env, clazz);
     sNativeInstanceField = GetFieldIDOrDie(env, clazz, "native_instance", "J");
+    sCtor = GetMethodIDOrDie(env, clazz, "<init>", "()V");
 
     return result;
 }
@@ -393,4 +397,7 @@
     return reinterpret_cast<SkMatrix*>(env->GetLongField(matrixObj, sNativeInstanceField));
 }
 
+jobject android_graphics_Matrix_newInstance(JNIEnv* env) {
+    return env->NewObject(sClazz, sCtor);
+}
 }
diff --git a/libs/hwui/jni/android_graphics_Matrix.h b/libs/hwui/jni/android_graphics_Matrix.h
index fe90d2e..79de48b 100644
--- a/libs/hwui/jni/android_graphics_Matrix.h
+++ b/libs/hwui/jni/android_graphics_Matrix.h
@@ -25,6 +25,9 @@
 /* Gets the underlying SkMatrix from a Matrix object. */
 SkMatrix* android_graphics_Matrix_getSkMatrix(JNIEnv* env, jobject matrixObj);
 
+/* Creates a new Matrix java object. */
+jobject android_graphics_Matrix_newInstance(JNIEnv* env);
+
 } // namespace android
 
 #endif // _ANDROID_GRAPHICS_MATRIX_H_
diff --git a/libs/hwui/libhwui.map.txt b/libs/hwui/libhwui.map.txt
index 73de0d1..77b8a44 100644
--- a/libs/hwui/libhwui.map.txt
+++ b/libs/hwui/libhwui.map.txt
@@ -28,6 +28,7 @@
     register_android_graphics_GraphicsStatsService;
     zygote_preload_graphics;
     AMatrix_getContents;
+    AMatrix_newInstance;
     APaint_createPaint;
     APaint_destroyPaint;
     APaint_setBlendMode;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 025be7b..2f3a509 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -482,6 +482,12 @@
 
     if (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw()) {
         mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
+        if (auto grContext = getGrContext()) {
+            // Submit to ensure that any texture uploads complete and Skia can
+            // free its staging buffers.
+            grContext->flushAndSubmit();
+        }
+
         // Notify the callbacks, even if there's nothing to draw so they aren't waiting
         // indefinitely
         waitOnFences();
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index acd8bce..d10e688 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -153,8 +153,7 @@
                     || update.state.surfaceHeight < desiredHeight) {
                 needApplyTransaction = true;
 
-                t.setSize(update.state.surfaceControl,
-                        desiredWidth, desiredHeight);
+                update.state.surfaceControl->updateDefaultBufferSize(desiredWidth, desiredHeight);
                 update.state.surfaceWidth = desiredWidth;
                 update.state.surfaceHeight = desiredHeight;
                 update.state.surfaceDrawn = false;
diff --git a/location/java/android/location/CorrelationVector.java b/location/java/android/location/CorrelationVector.java
index 718977f..5493e25 100644
--- a/location/java/android/location/CorrelationVector.java
+++ b/location/java/android/location/CorrelationVector.java
@@ -94,8 +94,6 @@
                 "FrequencyOffsetMetersPerSecond must be non-negative (greater than or equal to 0)");
         Preconditions.checkArgument(builder.mSamplingWidthMeters > 0.0,
                 "SamplingWidthMeters must be positive (greater than 0)");
-        Preconditions.checkArgument(builder.mSamplingStartMeters >= 0.0,
-                "SamplingStartMeters must be non-negative (greater than or equal to 0)");
         mMagnitude = builder.mMagnitude;
         mFrequencyOffsetMetersPerSecond = builder.mFrequencyOffsetMetersPerSecond;
         mSamplingWidthMeters = builder.mSamplingWidthMeters;
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index 1e8b952..209903c 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -1084,6 +1084,12 @@
         mExtras = (extras == null) ? null : new Bundle(extras);
     }
 
+    /**
+     * Location equality is provided primarily for test purposes. Comparing locations for equality
+     * in production may indicate incorrect assumptions, and should be avoided whenever possible.
+     *
+     * <p>{@inheritDoc}
+     */
     @Override
     public boolean equals(Object o) {
         if (this == o) {
@@ -1121,7 +1127,17 @@
                 && (!hasBearingAccuracy() || Float.compare(location.mBearingAccuracyDegrees,
                 mBearingAccuracyDegrees) == 0)
                 && Objects.equals(mProvider, location.mProvider)
-                && Objects.equals(mExtras, location.mExtras);
+                && areExtrasEqual(mExtras, location.mExtras);
+    }
+
+    private static boolean areExtrasEqual(@Nullable Bundle extras1, @Nullable Bundle extras2) {
+        if ((extras1 == null || extras1.isEmpty()) && (extras2 == null || extras2.isEmpty())) {
+            return true;
+        } else if (extras1 == null || extras2 == null) {
+            return false;
+        } else {
+            return extras1.kindofEquals(extras2);
+        }
     }
 
     @Override
diff --git a/location/java/android/location/LocationListener.java b/location/java/android/location/LocationListener.java
index 35a4091..031642b 100644
--- a/location/java/android/location/LocationListener.java
+++ b/location/java/android/location/LocationListener.java
@@ -38,9 +38,9 @@
 public interface LocationListener {
 
     /**
-     * Called when the location has changed. A wakelock is held on behalf on the listener for some
-     * brief amount of time as this callback executes. If this callback performs long running
-     * operations, it is the client's responsibility to obtain their own wakelock.
+     * Called when the location has changed. A wakelock may be held on behalf on the listener for
+     * some brief amount of time as this callback executes. If this callback performs long running
+     * operations, it is the client's responsibility to obtain their own wakelock if necessary.
      *
      * @param location the updated location
      */
@@ -48,7 +48,7 @@
 
     /**
      * Called when the location has changed and locations are being delivered in batches. The
-     * default implementation calls through to ({@link #onLocationChanged(Location)} with all
+     * default implementation calls through to {@link #onLocationChanged(Location)} with all
      * locations in the batch. The list of locations is always guaranteed to be non-empty, and is
      * always guaranteed to be ordered from earliest location to latest location (so that the
      * earliest location in the batch is at index 0 in the list, and the latest location in the
@@ -76,6 +76,11 @@
      * This callback will never be invoked on Android Q and above, and providers can be considered
      * as always in the {@link LocationProvider#AVAILABLE} state.
      *
+     * <p class="note">Note that this method only has a default implementation on Android R and
+     * above, and this method must still be overridden in order to run successfully on Android
+     * versions below R. LocationListenerCompat from the compat libraries may be used to avoid the
+     * need to override for older platforms.
+     *
      * @deprecated This callback will never be invoked on Android Q and above.
      */
     @Deprecated
@@ -84,6 +89,11 @@
     /**
      * Called when a provider this listener is registered with becomes enabled.
      *
+     * <p class="note">Note that this method only has a default implementation on Android R and
+     * above, and this method must still be overridden in order to run successfully on Android
+     * versions below R. LocationListenerCompat from the compat libraries may be used to avoid the
+     * need to override for older platforms.
+     *
      * @param provider the name of the location provider
      */
     default void onProviderEnabled(@NonNull String provider) {}
@@ -92,6 +102,11 @@
      * Called when the provider this listener is registered with becomes disabled. If a provider is
      * disabled when this listener is registered, this callback will be invoked immediately.
      *
+     * <p class="note">Note that this method only has a default implementation on Android R and
+     * above, and this method must still be overridden in order to run successfully on Android
+     * versions below R. LocationListenerCompat from the compat libraries may be used to avoid the
+     * need to override for older platforms.
+     *
      * @param provider the name of the location provider
      */
     default void onProviderDisabled(@NonNull String provider) {}
diff --git a/media/java/android/media/AudioManagerInternal.java b/media/java/android/media/AudioManagerInternal.java
index c827932..cb887f2 100644
--- a/media/java/android/media/AudioManagerInternal.java
+++ b/media/java/android/media/AudioManagerInternal.java
@@ -38,6 +38,17 @@
 
     public abstract void updateRingerModeAffectedStreamsInternal();
 
+    /**
+     * Notify the UID of the currently active {@link android.service.voice.HotwordDetectionService}.
+     *
+     * <p>The caller is expected to take care of any performance implications, e.g. by using a
+     * background thread to call this method.</p>
+     *
+     * @param uid UID of the currently active service or {@link android.os.Process#INVALID_UID} if
+     *            none.
+     */
+    public abstract void setHotwordDetectionServiceUid(int uid);
+
     public abstract void setAccessibilityServiceUids(IntArray uids);
 
     /**
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 8012f03..5d9f290 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.TestApi;
 import android.bluetooth.BluetoothCodecConfig;
@@ -1769,6 +1770,13 @@
     public static native int setAssistantUid(int uid);
 
     /**
+     * Communicate UID of the current {@link android.service.voice.HotwordDetectionService} to audio
+     * policy service.
+     * @hide
+     */
+    public static native int setHotwordDetectionServiceUid(int uid);
+
+    /**
      * @hide
      * Communicate UIDs of active accessibility services to audio policy service.
      */
@@ -1993,6 +2001,46 @@
      */
     public static native int setVibratorInfos(@NonNull List<Vibrator> vibrators);
 
+    /**
+     * @hide
+     * If a spatializer effect is present on the platform, this will return an
+     * ISpatializer interface to control this feature.
+     * If no spatializer is present, a null interface is returned.
+     * The INativeSpatializerCallback passed must not be null.
+     * Only one ISpatializer interface can exist at a given time. The native audio policy
+     * service will reject the request if an interface was already acquired and previous owner
+     * did not die or call ISpatializer.release().
+     * @param callback the callback to receive state updates if the ISpatializer
+     *        interface is acquired.
+     * @return the ISpatializer interface made available to control the
+     *        platform spatializer
+     */
+    @Nullable
+    public static ISpatializer getSpatializer(INativeSpatializerCallback callback) {
+        return ISpatializer.Stub.asInterface(nativeGetSpatializer(callback));
+    }
+    private static native IBinder nativeGetSpatializer(INativeSpatializerCallback callback);
+
+    /**
+     * @hide
+     * Queries if some kind of spatialization will be performed if the audio playback context
+     * described by the provided arguments is present.
+     * The context is made of:
+     * - The audio attributes describing the playback use case.
+     * - The audio configuration describing the audio format, channels, sampling rate ...
+     * - The devices describing the sink audio device selected for playback.
+     * All arguments are optional and only the specified arguments are used to match against
+     * supported criteria. For instance, supplying no argument will tell if spatialization is
+     * supported or not in general.
+     * @param attributes audio attributes describing the playback use case
+     * @param format audio configuration describing the audio format, channels, sampling rate...
+     * @param devices the sink audio device selected for playback
+     * @return true if spatialization is enabled for this context, false otherwise.
+     */
+    public static native boolean canBeSpatialized(AudioAttributes attributes,
+                                              AudioFormat format,
+                                              AudioDeviceAttributes[] devices);
+
     // Items shared with audio service
 
     /**
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index d746c85..37054b8 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -1573,6 +1573,9 @@
             if (isFdDuped) {
                 closeFileDescriptor(fileDescriptor);
             }
+            if (modernFd != null) {
+                modernFd.close();
+            }
         }
     }
 
@@ -2554,12 +2557,13 @@
 
     private void initForFilename(String filename) throws IOException {
         FileInputStream in = null;
+        ParcelFileDescriptor modernFd = null;
         mAssetInputStream = null;
         mFilename = filename;
         mIsInputStream = false;
         try {
             in = new FileInputStream(filename);
-            ParcelFileDescriptor modernFd = FileUtils.convertToModernFd(in.getFD());
+            modernFd = FileUtils.convertToModernFd(in.getFD());
             if (modernFd != null) {
                 closeQuietly(in);
                 in = new FileInputStream(modernFd.getFileDescriptor());
@@ -2570,6 +2574,9 @@
             loadAttributes(in);
         } finally {
             closeQuietly(in);
+            if (modernFd != null) {
+                modernFd.close();
+            }
         }
     }
 
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 2943eee..a15529e 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -36,6 +36,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.SystemProperties;
 import android.text.TextUtils;
+import android.util.Log;
 
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
@@ -52,6 +53,8 @@
  * frame and meta data from an input media file.
  */
 public class MediaMetadataRetriever implements AutoCloseable {
+    private static final String TAG = "MediaMetadataRetriever";
+
     // borrowed from ExoPlayer
     private static final String[] STANDARD_GENRES = new String[] {
             // These are the official ID3v1 genres.
@@ -301,11 +304,15 @@
      */
     public void setDataSource(FileDescriptor fd, long offset, long length)
             throws IllegalArgumentException  {
-        ParcelFileDescriptor modernFd = FileUtils.convertToModernFd(fd);
-        if (modernFd == null) {
-            _setDataSource(fd, offset, length);
-        } else {
-            _setDataSource(modernFd.getFileDescriptor(), offset, length);
+
+        try (ParcelFileDescriptor modernFd = FileUtils.convertToModernFd(fd)) {
+            if (modernFd == null) {
+                _setDataSource(fd, offset, length);
+            } else {
+                _setDataSource(modernFd.getFileDescriptor(), offset, length);
+            }
+        } catch (IOException e) {
+            Log.w(TAG, "Ignoring IO error while setting data source", e);
         }
     }
 
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 4f761ba..26eb2a9 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1271,11 +1271,14 @@
      */
     public void setDataSource(FileDescriptor fd, long offset, long length)
             throws IOException, IllegalArgumentException, IllegalStateException {
-        ParcelFileDescriptor modernFd = FileUtils.convertToModernFd(fd);
-        if (modernFd == null) {
-            _setDataSource(fd, offset, length);
-        } else {
-            _setDataSource(modernFd.getFileDescriptor(), offset, length);
+        try (ParcelFileDescriptor modernFd = FileUtils.convertToModernFd(fd)) {
+            if (modernFd == null) {
+                _setDataSource(fd, offset, length);
+            } else {
+                _setDataSource(modernFd.getFileDescriptor(), offset, length);
+            }
+        } catch (IOException e) {
+            Log.w(TAG, "Ignoring IO error while setting data source", e);
         }
     }
 
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 628f7ee..7f7fb60 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -292,8 +292,7 @@
         }
         synchronized (mRoutesLock) {
             for (MediaRoute2Info route : mRoutes.values()) {
-                if (sessionInfo.getSelectedRoutes().contains(route.getId())
-                        || sessionInfo.getTransferableRoutes().contains(route.getId())) {
+                if (sessionInfo.getTransferableRoutes().contains(route.getId())) {
                     routes.add(route);
                     continue;
                 }
diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java
index 86ed50b..72ee00f 100644
--- a/media/java/android/media/PlayerBase.java
+++ b/media/java/android/media/PlayerBase.java
@@ -102,6 +102,13 @@
         mState = AudioPlaybackConfiguration.PLAYER_STATE_IDLE;
     };
 
+    /** @hide */
+    public int getPlayerIId() {
+        synchronized (mLock) {
+            return mPlayerIId;
+        }
+    }
+
     /**
      * Call from derived class when instantiation / initialization is successful
      */
diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java
index 37e1415..72cddc9 100644
--- a/media/java/android/media/projection/MediaProjection.java
+++ b/media/java/android/media/projection/MediaProjection.java
@@ -16,14 +16,14 @@
 
 package android.media.projection;
 
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.VirtualDisplay;
 import android.hardware.display.VirtualDisplayConfig;
-import android.media.projection.IMediaProjection;
-import android.media.projection.IMediaProjectionCallback;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.util.ArrayMap;
@@ -106,7 +106,7 @@
         if (isSecure) {
             flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
         }
-        final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
+        final VirtualDisplayConfig.Builder builder = buildMirroredVirtualDisplay(name, width,
                 height, dpi);
         builder.setFlags(flags);
         if (surface != null) {
@@ -141,7 +141,7 @@
     public VirtualDisplay createVirtualDisplay(@NonNull String name,
             int width, int height, int dpi, int flags, @Nullable Surface surface,
             @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
-        final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
+        final VirtualDisplayConfig.Builder builder = buildMirroredVirtualDisplay(name, width,
                 height, dpi);
         builder.setFlags(flags);
         if (surface != null) {
@@ -151,6 +151,26 @@
     }
 
     /**
+     * Constructs a {@link VirtualDisplayConfig.Builder}, which will mirror the contents of a
+     * DisplayArea. The DisplayArea to mirror is from the DisplayArea the caller is launched on.
+     *
+     * @param name   The name of the virtual display, must be non-empty.
+     * @param width  The width of the virtual display in pixels. Must be greater than 0.
+     * @param height The height of the virtual display in pixels. Must be greater than 0.
+     * @param dpi    The density of the virtual display in dpi. Must be greater than 0.
+     * @return a config representing a VirtualDisplay
+     */
+    private VirtualDisplayConfig.Builder buildMirroredVirtualDisplay(@NonNull String name,
+            int width, int height, int dpi) {
+        Context windowContext = mContext.createWindowContext(mContext.getDisplayNoVerify(),
+                TYPE_APPLICATION, null /* options */);
+        final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
+                height, dpi);
+        builder.setWindowTokenClientToMirror(windowContext.getWindowContextToken());
+        return builder;
+    }
+
+    /**
      * Creates a {@link android.hardware.display.VirtualDisplay} to capture the
      * contents of the screen.
      *
diff --git a/mms/OWNERS b/mms/OWNERS
index befc320..7f05a2a 100644
--- a/mms/OWNERS
+++ b/mms/OWNERS
@@ -2,7 +2,6 @@
 
 tgunn@google.com
 breadley@google.com
-hallliu@google.com
 rgreenwalt@google.com
 amitmahajan@google.com
 fionaxu@google.com
@@ -10,7 +9,10 @@
 jminjie@google.com
 satk@google.com
 shuoq@google.com
-refuhoo@google.com
 nazaninb@google.com
 sarahchin@google.com
-dbright@google.com
\ No newline at end of file
+xiaotonj@google.com
+huiwang@google.com
+jayachandranc@google.com
+chinmayd@google.com
+amruthr@google.com
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index 95a2da9..51a0c99 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -111,8 +111,7 @@
         return nullptr;
     }
     if (preferredRateNanos <= 0) {
-        ALOGE("%s: PerformanceHint invalid preferred rate.", __FUNCTION__);
-        return nullptr;
+        preferredRateNanos = -1L;
     }
     return new APerformanceHintManager(std::move(manager), preferredRateNanos);
 }
diff --git a/packages/CarrierDefaultApp/OWNERS b/packages/CarrierDefaultApp/OWNERS
index 5668840..0d23f05 100644
--- a/packages/CarrierDefaultApp/OWNERS
+++ b/packages/CarrierDefaultApp/OWNERS
@@ -1,7 +1,6 @@
 set noparent
 tgunn@google.com
 breadley@google.com
-hallliu@google.com
 rgreenwalt@google.com
 amitmahajan@google.com
 fionaxu@google.com
@@ -9,9 +8,11 @@
 jminjie@google.com
 satk@google.com
 shuoq@google.com
-refuhoo@google.com
 nazaninb@google.com
 sarahchin@google.com
-dbright@google.com
 xiaotonj@google.com
+huiwang@google.com
+jayachandranc@google.com
+chinmayd@google.com
+amruthr@google.com
 
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index c1a0a9a..b4cafd8 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -93,9 +93,9 @@
             final DeviceFilterPair selectedDevice = getService().mDevicesFound.get(0);
             setTitle(Html.fromHtml(getString(
                     R.string.confirmation_title,
-                    getCallingAppName(),
-                    profileName,
-                    selectedDevice.getDisplayName()), 0));
+                    Html.escapeHtml(getCallingAppName()),
+                    Html.escapeHtml(selectedDevice.getDisplayName())), 0));
+
             mPairButton = findViewById(R.id.button_pair);
             mPairButton.setOnClickListener(v -> onDeviceConfirmed(getService().mSelectedDevice));
             getService().mSelectedDevice = selectedDevice;
@@ -108,8 +108,8 @@
             mPairButton = findViewById(R.id.button_pair);
             mPairButton.setVisibility(View.GONE);
             setTitle(Html.fromHtml(getString(R.string.chooser_title,
-                    profileName,
-                    getCallingAppName()), 0));
+                    Html.escapeHtml(profileName),
+                    Html.escapeHtml(getCallingAppName())), 0));
             mDeviceListView = findViewById(R.id.device_list);
             mDevicesAdapter = new DevicesAdapter();
             mDeviceListView.setAdapter(mDevicesAdapter);
diff --git a/packages/EasterEgg/AndroidManifest.xml b/packages/EasterEgg/AndroidManifest.xml
index 7f258eb..9d3ce34 100644
--- a/packages/EasterEgg/AndroidManifest.xml
+++ b/packages/EasterEgg/AndroidManifest.xml
@@ -134,6 +134,7 @@
         <receiver
             android:name=".widget.PaintChipsWidget"
             android:label="@string/s_egg_name"
+            android:exported="true"
             android:enabled="false">
             <intent-filter>
                 <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
@@ -145,4 +146,4 @@
         </receiver>
     </application>
 
-</manifest>
\ No newline at end of file
+</manifest>
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index 48cdf16..197b7b2 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -31,7 +31,7 @@
             android:directBootAware="true">
 
         <receiver android:name=".TemporaryFileManager"
-            android:exported="true">
+            android:exported="false">
             <intent-filter>
                 <action android:name="android.intent.action.BOOT_COMPLETED" />
             </intent-filter>
@@ -76,7 +76,7 @@
 
         <receiver android:name=".InstallEventReceiver"
                 android:permission="android.permission.INSTALL_PACKAGES"
-                android:exported="true">
+                android:exported="false">
             <intent-filter android:priority="1">
                 <action android:name="com.android.packageinstaller.ACTION_INSTALL_COMMIT" />
             </intent-filter>
@@ -106,14 +106,14 @@
 
         <receiver android:name=".UninstallEventReceiver"
             android:permission="android.permission.INSTALL_PACKAGES"
-            android:exported="true">
+            android:exported="false">
             <intent-filter android:priority="1">
                 <action android:name="com.android.packageinstaller.ACTION_UNINSTALL_COMMIT" />
             </intent-filter>
         </receiver>
 
         <receiver android:name=".PackageInstalledReceiver"
-                android:exported="true">
+                android:exported="false">
             <intent-filter android:priority="1">
                 <action android:name="android.intent.action.PACKAGE_ADDED" />
                 <data android:scheme="package" />
diff --git a/packages/PackageInstaller/TEST_MAPPING b/packages/PackageInstaller/TEST_MAPPING
index 5d7b9bb..cef9014 100644
--- a/packages/PackageInstaller/TEST_MAPPING
+++ b/packages/PackageInstaller/TEST_MAPPING
@@ -19,6 +19,9 @@
     },
     {
       "name": "CtsPackageUninstallTestCases"
+    },
+    {
+      "name": "PackageInstallerTests"
     }
   ]
-}
\ No newline at end of file
+}
diff --git a/packages/PackageInstaller/res/values-te/strings.xml b/packages/PackageInstaller/res/values-te/strings.xml
index 8951e0d..806734f 100644
--- a/packages/PackageInstaller/res/values-te/strings.xml
+++ b/packages/PackageInstaller/res/values-te/strings.xml
@@ -45,14 +45,14 @@
     <string name="out_of_space_dlg_title" msgid="4156690013884649502">"ఖాళీ లేదు"</string>
     <string name="out_of_space_dlg_text" msgid="8727714096031856231">"<xliff:g id="APP_NAME">%1$s</xliff:g>ని ఇన్‌స్టాల్ చేయడం సాధ్యపడలేదు. కొంత స్థలాన్ని ఖాళీ చేసి మళ్లీ ప్రయత్నించండి."</string>
     <string name="app_not_found_dlg_title" msgid="5107924008597470285">"యాప్ కనుగొనబడలేదు"</string>
-    <string name="app_not_found_dlg_text" msgid="5219983779377811611">"ఇన్‌స్టాల్ చేసిన యాప్‌ల జాబితాలో యాప్ కనుగొనబడలేదు."</string>
+    <string name="app_not_found_dlg_text" msgid="5219983779377811611">"ఇన్‌స్టాల్ చేసిన యాప్‌ల లిస్ట్‌లో యాప్ కనుగొనబడలేదు."</string>
     <string name="user_is_not_allowed_dlg_title" msgid="6915293433252210232">"అనుమతించబడలేదు"</string>
     <string name="user_is_not_allowed_dlg_text" msgid="3468447791330611681">"ప్రస్తుత వినియోగదారు ఈ అన్ఇన్‌స్టాలేషన్ చేసేందుకు అనుమతించబడరు."</string>
     <string name="generic_error_dlg_title" msgid="5863195085927067752">"లోపం"</string>
     <string name="generic_error_dlg_text" msgid="5287861443265795232">"యాప్‌ను అన్ఇన్‌స్టాల్ చేయడం సాధ్యపడలేదు."</string>
     <string name="uninstall_application_title" msgid="4045420072401428123">"యాప్‌ను అన్‌ఇన్‌స్టాల్ చేయి"</string>
     <string name="uninstall_update_title" msgid="824411791011583031">"అప్‌డేట్ అన్‌ఇన్‌స్టాల్ చేయి"</string>
-    <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> అనేది క్రింది యాప్‌లో ఒక భాగం:"</string>
+    <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> అనేది కింది యాప్‌లో ఒక భాగం:"</string>
     <string name="uninstall_application_text" msgid="3816830743706143980">"మీరు ఈ యాప్‌ను అన్‌ఇన్‌స్టాల్ చేయాలనుకుంటున్నారా?"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"మీరు ఈ యాప్‌ను "<b>"అందరు"</b>" వినియోగదారులకు అన్‌ఇన్‌స్టాల్ చేయాలనుకుంటున్నారా? అప్లికేషన్, దాని డేటా పరికరంలోని "<b>"అందరు"</b>" వినియోగదారుల నుండి తీసివేయబడుతుంది."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"మీరు వినియోగదారు <xliff:g id="USERNAME">%1$s</xliff:g> కోసం ఈ యాప్‌ను అన్‌ఇన్‌స్టాల్ చేయాలనుకుంటున్నారా?"</string>
@@ -83,10 +83,10 @@
     <string name="untrusted_external_source_warning" product="tablet" msgid="7067510047443133095">"మీ భద్రత దృష్ట్యా, ఈ సోర్సు నుండి తెలియని యాప్‌లను ఇన్‌స్టాల్ చేయడానికి మీ టాబ్లెట్ ప్రస్తుతం అనుమతించబడదు. మీరు దీన్ని సెట్టింగ్‌లలో మార్చవచ్చు."</string>
     <string name="untrusted_external_source_warning" product="tv" msgid="7057271609532508035">"మీ భద్రత దృష్ట్యా, ఈ సోర్సు నుండి తెలియని యాప్‌లను ఇన్‌స్టాల్ చేయడానికి మీ టీవీ ప్రస్తుతం అనుమతించబడదు. మీరు దీన్ని సెట్టింగ్‌లలో మార్చవచ్చు."</string>
     <string name="untrusted_external_source_warning" product="default" msgid="8444191224459138919">"మీ భద్రత దృష్ట్యా, ఈ సోర్సు నుండి తెలియని యాప్‌లను ఇన్‌స్టాల్ చేయడానికి మీ ఫోన్ ప్రస్తుతం అనుమతించబడదు. మీరు దీన్ని సెట్టింగ్‌లలో మార్చవచ్చు."</string>
-    <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"తెలియని యాప్‌లు మీ ఫోన్ పైన, వ్యక్తిగత డేటా పైన దాడి చేయడానికి ఎక్కువగా అవకాశం ఉంటుంది. ఈ యాప్ను ఇన్‌స్టాల్ చేయడం ద్వారా, దాని వినియోగంతో మీ ఫోన్‌కు ఏదైనా నష్టం జరిగితే లేదా మీ డేటాను కోల్పోతే అందుకు మీరే బాధ్యత వహిస్తారని అంగీకరిస్తున్నారు."</string>
+    <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"తెలియని యాప్‌లు మీ ఫోన్ పైన, వ్యక్తిగత డేటా పైన దాడి చేయడానికి ఎక్కువగా అవకాశం ఉంటుంది. ఈ యాప్‌ను ఇన్‌స్టాల్ చేయడం ద్వారా, దాని వినియోగంతో మీ ఫోన్‌కు ఏదైనా నష్టం జరిగితే లేదా మీ డేటాను కోల్పోతే అందుకు మీరే బాధ్యత వహిస్తారని అంగీకరిస్తున్నారు."</string>
     <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"మీ టాబ్లెట్ మరియు వ్యక్తిగత డేటాపై తెలియని యాప్‌లు దాడి చేయడానికి ఎక్కువ అవకాశం ఉంది. మీరు ఈ యాప్‌ను ఇన్‌స్టాల్ చేయడం ద్వారా, దీనిని ఉపయోగించడం వలన మీ టాబ్లెట్‌కు ఏదైనా హాని జరిగినా లేదా డేటా కోల్పోయినా బాధ్యత మీదేనని అంగీకరిస్తున్నారు."</string>
     <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"మీ టీవీ మరియు వ్యక్తిగత డేటాపై తెలియని యాప్‌లు దాడి చేయడానికి ఎక్కువ అవకాశం ఉంది. మీరు ఈ యాప్‌ను ఇన్‌స్టాల్ చేయడం ద్వారా, దీనిని ఉపయోగించడం వలన మీ టీవీకి ఏదైనా హాని జరిగినా లేదా డేటా కోల్పోయినా బాధ్యత మీదేనని అంగీకరిస్తున్నారు."</string>
-    <string name="anonymous_source_continue" msgid="4375745439457209366">"కొనసాగించు"</string>
+    <string name="anonymous_source_continue" msgid="4375745439457209366">"కొనసాగండి"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"సెట్టింగ్‌లు"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear యాప్‌లను ఇన్‌స్టాల్/అన్‌ఇన్‌స్టాల్ చేస్తోంది"</string>
     <string name="app_installed_notification_channel_description" msgid="2695385797601574123">"యాప్ ఇన్‌స్టాల్ చేయబడిందనే నోటిఫికేషన్"</string>
diff --git a/packages/PrintSpooler/res/values-ta/strings.xml b/packages/PrintSpooler/res/values-ta/strings.xml
index eaf05b1..7ffac67 100644
--- a/packages/PrintSpooler/res/values-ta/strings.xml
+++ b/packages/PrintSpooler/res/values-ta/strings.xml
@@ -63,7 +63,7 @@
     <string name="printer_info_desc" msgid="7181988788991581654">"இந்தப் பிரிண்டர் பற்றிய கூடுதல் தகவல்"</string>
     <string name="notification_channel_progress" msgid="872788690775721436">"இயக்கத்திலுள்ள அச்சுப் பணிகள்"</string>
     <string name="notification_channel_failure" msgid="9042250774797916414">"தோல்வியடைந்த அச்சுப் பணிகள்"</string>
-    <string name="could_not_create_file" msgid="3425025039427448443">"கோப்பை உருவாக்க முடியவில்லை"</string>
+    <string name="could_not_create_file" msgid="3425025039427448443">"ஃபைலை உருவாக்க முடியவில்லை"</string>
     <string name="print_services_disabled_toast" msgid="9089060734685174685">"சில அச்சுப் பொறிகள் முடக்கப்பட்டன"</string>
     <string name="print_searching_for_printers" msgid="6550424555079932867">"பிரிண்டர்களைத் தேடுகிறது"</string>
     <string name="print_no_print_services" msgid="8561247706423327966">"அச்சுப் பொறிகள் இல்லை"</string>
diff --git a/packages/SettingsLib/ActionBarShadow/Android.bp b/packages/SettingsLib/ActionBarShadow/Android.bp
index 800ab67..4a07d49 100644
--- a/packages/SettingsLib/ActionBarShadow/Android.bp
+++ b/packages/SettingsLib/ActionBarShadow/Android.bp
@@ -19,5 +19,5 @@
     ],
 
     sdk_version: "system_current",
-    min_sdk_version: "21",
+    min_sdk_version: "28",
 }
diff --git a/packages/SettingsLib/ActionBarShadow/lint-baseline.xml b/packages/SettingsLib/ActionBarShadow/lint-baseline.xml
deleted file mode 100644
index 4d5de5f..0000000
--- a/packages/SettingsLib/ActionBarShadow/lint-baseline.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 23 (current min is 21): `android.view.View#setOnScrollChangeListener`"
-        errorLine1="            mScrollView.setOnScrollChangeListener(mScrollChangeWatcher);"
-        errorLine2="                        ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/ActionBarShadow/src/com/android/settingslib/widget/ActionBarShadowController.java"
-            line="81"
-            column="25"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 23 (current min is 21): `android.view.View#setOnScrollChangeListener`"
-        errorLine1="        mScrollView.setOnScrollChangeListener(null);"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/ActionBarShadow/src/com/android/settingslib/widget/ActionBarShadowController.java"
-            line="88"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Class requires API level 23 (current min is 21): `android.view.View.OnScrollChangeListener`"
-        errorLine1="    final class ScrollChangeWatcher implements View.OnScrollChangeListener {"
-        errorLine2="                                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/ActionBarShadow/src/com/android/settingslib/widget/ActionBarShadowController.java"
-            line="95"
-            column="48"/>
-    </issue>
-
-</issues>
diff --git a/packages/SettingsLib/ActionButtonsPreference/lint-baseline.xml b/packages/SettingsLib/ActionButtonsPreference/lint-baseline.xml
deleted file mode 100644
index 95b7e3b..0000000
--- a/packages/SettingsLib/ActionButtonsPreference/lint-baseline.xml
+++ /dev/null
@@ -1,81 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
-
-    <issue
-        id="NewApi"
-        message="`android:Widget.DeviceDefault.Button.Borderless.Colored` requires API level 28 (current min is 21)"
-        errorLine1="    &lt;style name=&quot;SettingsActionButton&quot; parent=&quot;android:Widget.DeviceDefault.Button.Borderless.Colored&quot;>"
-        errorLine2="                                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml"
-            line="19"
-            column="40"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="`android:drawableTint` requires API level 23 (current min is 21)"
-        errorLine1="        &lt;item name=&quot;android:drawableTint&quot;>@*android:color/btn_colored_borderless_text_material&lt;/item>"
-        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml"
-            line="21"
-            column="15"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 21)"
-        errorLine1="        android:topLeftRadius=&quot;?android:attr/dialogCornerRadius&quot;"
-        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml"
-            line="23"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 21)"
-        errorLine1="        android:bottomLeftRadius=&quot;?android:attr/dialogCornerRadius&quot;"
-        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml"
-            line="25"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 21)"
-        errorLine1="        android:topRightRadius=&quot;?android:attr/dialogCornerRadius&quot;"
-        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml"
-            line="24"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 21)"
-        errorLine1="        android:bottomRightRadius=&quot;?android:attr/dialogCornerRadius&quot;"
-        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml"
-            line="26"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 21)"
-        errorLine1="        android:bottomRightRadius=&quot;?android:attr/dialogCornerRadius&quot;"
-        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml"
-            line="23"
-            column="9"/>
-    </issue>
-
-</issues>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml
index 16a85d6..8a25726 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml
@@ -17,6 +17,8 @@
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+       xmlns:tools="http://schemas.android.com/tools"
+       tools:targetApi="28"
        android:shape="rectangle">
     <solid android:color="?androidprv:attr/colorSurface" />
     <corners
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml
index 1b9f68f..7e626e5 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml
@@ -1,22 +1,24 @@
 <?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.
+    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.
   -->
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+       xmlns:tools="http://schemas.android.com/tools"
+       tools:targetApi="28"
        android:shape="rectangle">
     <solid android:color="?androidprv:attr/colorSurface" />
     <corners
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml
index a884ef1..9f4980b 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml
@@ -1,22 +1,24 @@
 <?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.
+    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.
   -->
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+       xmlns:tools="http://schemas.android.com/tools"
+       tools:targetApi="28"
        android:shape="rectangle">
     <solid android:color="?androidprv:attr/colorSurface" />
     <corners
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml b/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml
index 42c7d76..8a449cf 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/values/styles.xml
@@ -15,7 +15,9 @@
   limitations under the License.
   -->
 
-<resources>
+<resources
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:targetApi="28">
     <style name="SettingsLibActionButton" parent="android:Widget.DeviceDefault.Button.Borderless.Colored">
         <item name="android:drawablePadding">4dp</item>
         <item name="android:drawableTint">@*android:color/btn_colored_borderless_text_material</item>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-v31/styles.xml b/packages/SettingsLib/BannerMessagePreference/res/values-v31/styles.xml
index e74ac44..fede44f 100644
--- a/packages/SettingsLib/BannerMessagePreference/res/values-v31/styles.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-v31/styles.xml
@@ -33,18 +33,18 @@
     <style name="Banner.Title.SettingsLib"
         parent="@android:style/TextAppearance.Material.Subhead">
         <item name="android:textSize">20sp</item>
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item>
         <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
 
     <style name="Banner.Subtitle.SettingsLib"
-        parent="@*android:style/TextAppearance.DeviceDefault.Body1">
+        parent="@android:style/TextAppearance.DeviceDefault">
         <item name="android:textColor">?android:attr/textColorSecondary</item>
         <item name="android:textSize">14sp</item>
     </style>
 
     <style name="Banner.Summary.SettingsLib"
-        parent="@*android:style/TextAppearance.DeviceDefault.Body1">
+        parent="@android:style/TextAppearance.DeviceDefault">
         <item name="android:textColor">?android:attr/textColorSecondary</item>
         <item name="android:textSize">14sp</item>
     </style>
@@ -58,4 +58,4 @@
         parent="android:Widget.DeviceDefault.Button.Borderless.Colored">
         <item name="android:textColor">?android:attr/colorAccent</item>
     </style>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values/styles.xml b/packages/SettingsLib/BannerMessagePreference/res/values/styles.xml
index df47c64..4c6ed58 100644
--- a/packages/SettingsLib/BannerMessagePreference/res/values/styles.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values/styles.xml
@@ -17,14 +17,13 @@
 
 <resources>
     <style name="Banner.Text.Title"
-           parent="@android:style/TextAppearance.Material.Subhead">
+           parent="@android:style/TextAppearance.DeviceDefault.WindowTitle">
         <item name="android:textSize">16sp</item>
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
         <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
 
     <style name="Banner.Text.Summary"
-           parent="@*android:style/TextAppearance.DeviceDefault.Body1">
+           parent="@android:style/TextAppearance.DeviceDefault">
         <item name="android:textColor">?android:attr/textColorSecondary</item>
         <item name="android:textSize">14sp</item>
     </style>
diff --git a/packages/SettingsLib/BarChartPreference/Android.bp b/packages/SettingsLib/BarChartPreference/Android.bp
index ae26066..4f65373 100644
--- a/packages/SettingsLib/BarChartPreference/Android.bp
+++ b/packages/SettingsLib/BarChartPreference/Android.bp
@@ -14,7 +14,7 @@
     resource_dirs: ["res"],
 
     static_libs: [
-          "androidx.preference_preference",
+        "androidx.preference_preference",
     ],
 
     sdk_version: "system_current",
diff --git a/packages/SettingsLib/BarChartPreference/lint-baseline.xml b/packages/SettingsLib/BarChartPreference/lint-baseline.xml
deleted file mode 100644
index f1043bb..0000000
--- a/packages/SettingsLib/BarChartPreference/lint-baseline.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
-
-    <issue
-        id="NewApi"
-        message="`@android:style/Widget.DeviceDefault.Button.Borderless.Colored` requires API level 28 (current min is 21)"
-        errorLine1="           parent=&quot;@android:style/Widget.DeviceDefault.Button.Borderless.Colored&quot;>"
-        errorLine2="           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/BarChartPreference/res/values/styles.xml"
-            line="35"
-            column="12"/>
-    </issue>
-
-</issues>
diff --git a/packages/SettingsLib/BarChartPreference/res/values/styles.xml b/packages/SettingsLib/BarChartPreference/res/values/styles.xml
index 7a3fb7d..1c44207 100644
--- a/packages/SettingsLib/BarChartPreference/res/values/styles.xml
+++ b/packages/SettingsLib/BarChartPreference/res/values/styles.xml
@@ -15,7 +15,9 @@
   limitations under the License.
   -->
 
-<resources>
+<resources
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:targetApi="28">
 
     <style name="SettingsBarChart">
         <item name="android:layout_marginStart">10dp</item>
@@ -85,9 +87,9 @@
     </style>
 
     <style name="BarChart.Text"
-           parent="@android:style/TextAppearance.Material.Subhead">
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+           parent="@android:style/TextAppearance.DeviceDefault.WindowTitle">
         <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textSize">16sp</item>
     </style>
 
     <style name="BarChart.Text.HeaderTitle">
@@ -99,7 +101,7 @@
     </style>
 
     <style name="BarChart.Text.Summary"
-           parent="@*android:style/TextAppearance.DeviceDefault.Body1">
+           parent="@android:style/TextAppearance.DeviceDefault">
         <item name="android:textColor">?android:attr/textColorSecondary</item>
         <item name="android:textSize">12sp</item>
     </style>
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
index 2f911c4..238e65e 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/Android.bp
@@ -19,6 +19,7 @@
         "com.google.android.material_material",
         "SettingsLibSettingsTransition",
         "SettingsLibUtils",
+        "SettingsLibSettingsTheme",
     ],
     sdk_version: "system_current",
     min_sdk_version: "29",
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml
index 5950656..907863e 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml
@@ -16,7 +16,6 @@
   -->
 <androidx.coordinatorlayout.widget.CoordinatorLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/content_parent"
     android:layout_width="match_parent"
@@ -40,7 +39,7 @@
             android:clipToPadding="false"
             app:forceApplySystemWindowInsetTop="true"
             app:extraMultilineHeightEnabled="true"
-            app:contentScrim="?androidprv:attr/colorSurfaceHeader"
+            app:contentScrim="@color/settingslib_colorSurfaceHeader"
             app:maxLines="3"
             app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
             app:scrimAnimationDuration="50"
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night/themes.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v31/themes.xml
similarity index 83%
rename from packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night/themes.xml
rename to packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v31/themes.xml
index 878275a0..c20beaf 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night/themes.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night-v31/themes.xml
@@ -18,7 +18,7 @@
     <style name="Theme.CollapsingToolbar.Settings" parent="@style/Theme.MaterialComponents.DayNight">
         <item name="elevationOverlayEnabled">true</item>
         <item name="elevationOverlayColor">?attr/colorPrimary</item>
-        <item name="colorPrimary">@*android:color/primary_dark_device_default_settings</item>
-        <item name="colorAccent">@*android:color/accent_device_default_dark</item>
+        <item name="colorPrimary">@color/settingslib_primary_dark_device_default_settings</item>
+        <item name="colorAccent">@color/settingslib_accent_device_default_dark</item>
     </style>
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/dimens.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/dimens.xml
similarity index 100%
rename from packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/dimens.xml
rename to packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/dimens.xml
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/styles.xml
similarity index 77%
rename from packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml
rename to packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/styles.xml
index 63d397c..d0b6c4d 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/styles.xml
@@ -16,11 +16,13 @@
 -->
 <resources>
     <style name="CollapsingToolbarTitle.Collapsed" parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item>
         <item name="android:textSize">20dp</item>
+        <item name="android:textColor">@color/settingslib_text_color_primary_device_default</item>
     </style>
 
     <style name="CollapsingToolbarTitle.Expanded" parent="CollapsingToolbarTitle.Collapsed">
         <item name="android:textSize">36dp</item>
+        <item name="android:textColor">@color/settingslib_text_color_primary_device_default</item>
     </style>
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night/themes.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes.xml
similarity index 82%
copy from packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night/themes.xml
copy to packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes.xml
index 878275a0..9ecc297 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-night/themes.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values-v31/themes.xml
@@ -18,7 +18,7 @@
     <style name="Theme.CollapsingToolbar.Settings" parent="@style/Theme.MaterialComponents.DayNight">
         <item name="elevationOverlayEnabled">true</item>
         <item name="elevationOverlayColor">?attr/colorPrimary</item>
-        <item name="colorPrimary">@*android:color/primary_dark_device_default_settings</item>
-        <item name="colorAccent">@*android:color/accent_device_default_dark</item>
+        <item name="colorPrimary">@color/settingslib_primary_device_default_settings_light</item>
+        <item name="colorAccent">@color/settingslib_accent_device_default_light</item>
     </style>
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/themes.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/themes.xml
deleted file mode 100644
index 2e7a6a9..0000000
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/themes.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
--->
-<resources>
-    <style name="Theme.CollapsingToolbar.Settings" parent="@style/Theme.MaterialComponents.DayNight">
-        <item name="elevationOverlayEnabled">true</item>
-        <item name="elevationOverlayColor">?attr/colorPrimary</item>
-        <item name="colorPrimary">@*android:color/primary_device_default_settings_light</item>
-        <item name="colorAccent">@*android:color/accent_device_default_light</item>
-    </style>
-</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/FooterPreference/res/values/styles.xml b/packages/SettingsLib/FooterPreference/res/values/styles.xml
index 08dd359..5a3bada 100644
--- a/packages/SettingsLib/FooterPreference/res/values/styles.xml
+++ b/packages/SettingsLib/FooterPreference/res/values/styles.xml
@@ -17,9 +17,8 @@
 
 <resources>
     <style name="TextAppearance.Footer.Title.SettingsLib"
-           parent="@android:style/TextAppearance.DeviceDefault.Medium">
+           parent="@android:style/TextAppearance.DeviceDefault.WindowTitle">
         <item name="android:textSize">14sp</item>
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
         <item name="android:textColor">?android:attr/colorAccent</item>
     </style>
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/HelpUtils/lint-baseline.xml b/packages/SettingsLib/HelpUtils/lint-baseline.xml
deleted file mode 100644
index 940f027..0000000
--- a/packages/SettingsLib/HelpUtils/lint-baseline.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 28 (current min is 21): `android.content.pm.PackageInfo#getLongVersionCode`"
-        errorLine1="                sCachedVersionCode = Long.toString(info.getLongVersionCode());"
-        errorLine2="                                                        ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java"
-            line="239"
-            column="57"/>
-    </issue>
-
-</issues>
diff --git a/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java b/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java
index 541a246..70c8658 100644
--- a/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java
+++ b/packages/SettingsLib/HelpUtils/src/com/android/settingslib/HelpUtils.java
@@ -25,6 +25,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.net.Uri;
+import android.os.Build;
 import android.provider.Settings.Global;
 import android.text.TextUtils;
 import android.util.Log;
@@ -32,6 +33,7 @@
 import android.view.MenuItem;
 import android.view.MenuItem.OnMenuItemClickListener;
 
+import androidx.annotation.RequiresApi;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.settingslib.widget.R;
@@ -73,6 +75,15 @@
     private HelpUtils() {
     }
 
+    /**
+     * Prepares the help menu item by doing the following.
+     * - If the helpUrlString is empty or null, the help menu item is made invisible.
+     * - Otherwise, this makes the help menu item visible and sets the intent for the help menu
+     * item to view the URL.
+     *
+     * @return returns whether the help menu item has been made visible.
+     */
+    @RequiresApi(Build.VERSION_CODES.P)
     public static boolean prepareHelpMenuItem(Activity activity, Menu menu, String helpUri,
             String backupContext) {
         // menu contains help item, skip it
@@ -84,6 +95,15 @@
         return prepareHelpMenuItem(activity, helpItem, helpUri, backupContext);
     }
 
+    /**
+     * Prepares the help menu item by doing the following.
+     * - If the helpUrlString is empty or null, the help menu item is made invisible.
+     * - Otherwise, this makes the help menu item visible and sets the intent for the help menu
+     * item to view the URL.
+     *
+     * @return returns whether the help menu item has been made visible.
+     */
+    @RequiresApi(Build.VERSION_CODES.P)
     public static boolean prepareHelpMenuItem(Activity activity, Menu menu, int helpUriResource,
             String backupContext) {
         // menu contains help item, skip it
@@ -105,6 +125,7 @@
      * @return returns whether the help menu item has been made visible.
      */
     @VisibleForTesting
+    @RequiresApi(Build.VERSION_CODES.P)
     static boolean prepareHelpMenuItem(final Activity activity, MenuItem helpMenuItem,
             String helpUriString, String backupContext) {
         if (Global.getInt(activity.getContentResolver(), Global.DEVICE_PROVISIONED, 0) == 0) {
@@ -152,6 +173,10 @@
         }
     }
 
+    /**
+     * Get the help intent from helpUriString.
+     */
+    @RequiresApi(Build.VERSION_CODES.P)
     public static Intent getHelpIntent(Context context, String helpUriString,
             String backupContext) {
         if (Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0) == 0) {
@@ -223,7 +248,8 @@
      *
      * @return the uri with added query parameters
      */
-    private static Uri uriWithAddedParameters(Context context, Uri baseUri) {
+    @RequiresApi(Build.VERSION_CODES.P)
+    public static Uri uriWithAddedParameters(Context context, Uri baseUri) {
         Uri.Builder builder = baseUri.buildUpon();
 
         // Add in the preferred language
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
index 266fc78..1f80a3e 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
@@ -170,7 +170,14 @@
     }
 
     /**
-     * Sets image drawable to display image in {@link LottieAnimationView}
+     * Gets the lottie illustration resource id.
+     */
+    public int getLottieAnimationResId() {
+        return mImageResId;
+    }
+
+    /**
+     * Sets the image drawable to display image in {@link LottieAnimationView}.
      *
      * @param imageDrawable the drawable of an image
      */
@@ -183,7 +190,16 @@
     }
 
     /**
-     * Sets image uri to display image in {@link LottieAnimationView}
+     * Gets the image drawable from display image in {@link LottieAnimationView}.
+     *
+     * @return the drawable of an image
+     */
+    public Drawable getImageDrawable() {
+        return mImageDrawable;
+    }
+
+    /**
+     * Sets the image uri to display image in {@link LottieAnimationView}.
      *
      * @param imageUri the Uri of an image
      */
@@ -195,6 +211,15 @@
         }
     }
 
+    /**
+     * Gets the image uri from display image in {@link LottieAnimationView}.
+     *
+     * @return the Uri of an image
+     */
+    public Uri getImageUri() {
+        return mImageUri;
+    }
+
     private void resetImageResourceCache() {
         mImageDrawable = null;
         mImageUri = null;
diff --git a/packages/SettingsLib/LayoutPreference/res/values/styles.xml b/packages/SettingsLib/LayoutPreference/res/values/styles.xml
index 4a99e84..2ffe6d9 100644
--- a/packages/SettingsLib/LayoutPreference/res/values/styles.xml
+++ b/packages/SettingsLib/LayoutPreference/res/values/styles.xml
@@ -24,14 +24,13 @@
     </style>
 
     <style name="TextAppearance.EntityHeaderTitle"
-           parent="@android:style/TextAppearance.Material.Subhead">
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+           parent="@android:style/TextAppearance.DeviceDefault.WindowTitle">
         <item name="android:textColor">?android:attr/textColorPrimary</item>
         <item name="android:textSize">20sp</item>
     </style>
 
     <style name="TextAppearance.EntityHeaderSummary"
-           parent="@*android:style/TextAppearance.DeviceDefault.Body1">
+           parent="@android:style/TextAppearance.DeviceDefault">
         <item name="android:textAlignment">viewStart</item>
         <item name="android:textColor">?android:attr/textColorSecondary</item>
         <item name="android:singleLine">true</item>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_switch_bar_bg_disabled.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable-v31/settingslib_switch_bar_bg_disabled.xml
similarity index 100%
rename from packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_switch_bar_bg_disabled.xml
rename to packages/SettingsLib/MainSwitchPreference/res/drawable-v31/settingslib_switch_bar_bg_disabled.xml
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_switch_bar_bg_off.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable-v31/settingslib_switch_bar_bg_off.xml
similarity index 100%
rename from packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_switch_bar_bg_off.xml
rename to packages/SettingsLib/MainSwitchPreference/res/drawable-v31/settingslib_switch_bar_bg_off.xml
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_switch_bar_bg_on.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable-v31/settingslib_switch_bar_bg_on.xml
similarity index 100%
rename from packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_switch_bar_bg_on.xml
rename to packages/SettingsLib/MainSwitchPreference/res/drawable-v31/settingslib_switch_bar_bg_on.xml
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
index 6e5911c..30748e6 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
@@ -50,7 +50,7 @@
             android:tint="?android:attr/colorAccent"
             android:layout_gravity="center_vertical"
             android:layout_marginEnd="@dimen/settingslib_restricted_icon_margin_end"
-            android:src="@*android:drawable/ic_info"
+            android:src="@android:drawable/ic_info"
             android:visibility="gone" />
 
         <Switch
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
index 306145a..d0c2d0b 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout/settingslib_main_switch_bar.xml
@@ -28,7 +28,7 @@
         android:layout_gravity="center_vertical"
         android:maxLines="2"
         android:ellipsize="end"
-        android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title"
+        android:textAppearance="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title"
         android:textSize="16sp"
         android:textColor="?android:attr/textColorPrimaryInverse"
         android:layout_marginStart="@dimen/settingslib_switchbar_subsettings_margin_start"
@@ -42,7 +42,7 @@
         android:theme="@android:style/Theme.Material"
         android:layout_gravity="center_vertical"
         android:layout_marginEnd="@dimen/settingslib_restricted_icon_margin_end"
-        android:src="@*android:drawable/ic_info"
+        android:src="@android:drawable/ic_info"
         android:visibility="gone"/>
 
     <Switch
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values-v31/dimens.xml b/packages/SettingsLib/MainSwitchPreference/res/values-v31/dimens.xml
new file mode 100644
index 0000000..2272a37
--- /dev/null
+++ b/packages/SettingsLib/MainSwitchPreference/res/values-v31/dimens.xml
@@ -0,0 +1,37 @@
+<?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.
+  -->
+
+<resources>
+
+    <!-- Size of layout margin -->
+    <dimen name="settingslib_switchbar_margin">16dp</dimen>
+
+    <!-- Size of layout margin left -->
+    <dimen name="settingslib_switchbar_padding_left">24dp</dimen>
+
+    <!-- Size of layout margin right -->
+    <dimen name="settingslib_switchbar_padding_right">16dp</dimen>
+
+    <!-- Minimum width of switch -->
+    <dimen name="settingslib_min_switch_width">52dp</dimen>
+
+    <!-- Minimum width of switch bar -->
+    <dimen name="settingslib_min_switch_bar_height">72dp</dimen>
+
+    <!-- Radius of switch bar -->
+    <dimen name="settingslib_switch_bar_radius">28dp</dimen>
+</resources>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values-v31/styles.xml b/packages/SettingsLib/MainSwitchPreference/res/values-v31/styles.xml
new file mode 100644
index 0000000..a50fc7c
--- /dev/null
+++ b/packages/SettingsLib/MainSwitchPreference/res/values-v31/styles.xml
@@ -0,0 +1,25 @@
+<?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.
+  -->
+
+<resources>
+
+    <style name="MainSwitchText.Settingslib" parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
+        <item name="android:textSize">20sp</item>
+        <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item>
+        <item name="android:textColor">@android:color/black</item>
+    </style>
+</resources>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml b/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
index 16b8af6..6362882 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  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.
@@ -17,30 +17,12 @@
 
 <resources>
 
-    <!-- Size of layout margin -->
-    <dimen name="settingslib_switchbar_margin">16dp</dimen>
-
-    <!-- Size of layout margin left -->
-    <dimen name="settingslib_switchbar_padding_left">24dp</dimen>
-
-    <!-- Size of layout margin right -->
-    <dimen name="settingslib_switchbar_padding_right">16dp</dimen>
-
-    <!-- Minimum width of switch -->
-    <dimen name="settingslib_min_switch_width">52dp</dimen>
-
-    <!-- Minimum width of switch bar -->
-    <dimen name="settingslib_min_switch_bar_height">72dp</dimen>
-
     <!-- Restricted icon size in switch bar -->
-    <dimen name="settingslib_restricted_icon_size">@*android:dimen/config_restrictedIconSize</dimen>
+    <dimen name="settingslib_restricted_icon_size">@android:dimen/config_restrictedIconSize</dimen>
 
     <!-- Restricted icon in switch bar -->
     <dimen name="settingslib_restricted_icon_margin_end">16dp</dimen>
 
-    <!-- Radius of switch bar -->
-    <dimen name="settingslib_switch_bar_radius">28dp</dimen>
-
     <!-- Size of title margin -->
     <dimen name="settingslib_switch_title_margin">16dp</dimen>
 
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values/styles.xml b/packages/SettingsLib/MainSwitchPreference/res/values/styles.xml
index 3924e30..870812a 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/values/styles.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/values/styles.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-   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.
@@ -17,13 +17,6 @@
 
 <resources>
 
-    <style name="MainSwitchText.Settingslib" parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
-        <item name="android:textSize">20sp</item>
-        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
-        <item name="android:textColor">@android:color/black</item>
-    </style>
-
-
     <style name="SwitchBar.Switch.Settingslib" parent="@android:style/Widget.Material.CompoundButton.Switch">
         <item name="android:trackTint">@color/settingslib_switchbar_switch_track_tint</item>
         <item name="android:thumbTint">@color/settingslib_switchbar_switch_thumb_tint</item>
diff --git a/packages/SettingsLib/ProgressBar/lint-baseline.xml b/packages/SettingsLib/ProgressBar/lint-baseline.xml
deleted file mode 100644
index 03d0f3f..0000000
--- a/packages/SettingsLib/ProgressBar/lint-baseline.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
-
-    <issue
-        id="NewApi"
-        message="`?android:attr/colorSecondary` requires API level 25 (current min is 21)"
-        errorLine1="        android:background=&quot;?android:attr/colorSecondary&quot; />"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/ProgressBar/res/layout/progress_header.xml"
-            line="27"
-            column="9"/>
-    </issue>
-
-</issues>
diff --git a/packages/SettingsLib/ProgressBar/res/layout/progress_header.xml b/packages/SettingsLib/ProgressBar/res/layout/progress_header.xml
index 268858b..52ff42c 100644
--- a/packages/SettingsLib/ProgressBar/res/layout/progress_header.xml
+++ b/packages/SettingsLib/ProgressBar/res/layout/progress_header.xml
@@ -17,6 +17,8 @@
 
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:targetApi="25"
     android:layout_width="match_parent"
     android:layout_height="3dp">
     <View
diff --git a/packages/SettingsLib/RestrictedLockUtils/lint-baseline.xml b/packages/SettingsLib/RestrictedLockUtils/lint-baseline.xml
deleted file mode 100644
index 173c735..0000000
--- a/packages/SettingsLib/RestrictedLockUtils/lint-baseline.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 23 (current min is 21): `android.content.Context#getSystemService`"
-        errorLine1="        ComponentName adminComponent = userContext.getSystemService("
-        errorLine2="                                                   ~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java"
-            line="59"
-            column="52"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 23 (current min is 21): `android.content.Context#getSystemService`"
-        errorLine1="        UserManager um = context.getSystemService(UserManager.class);"
-        errorLine2="                                 ~~~~~~~~~~~~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java"
-            line="101"
-            column="34"/>
-    </issue>
-
-</issues>
diff --git a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
index a77e34b..80f02b4 100644
--- a/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/RestrictedLockUtils/src/com/android/settingslib/RestrictedLockUtils.java
@@ -21,11 +21,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.os.Build;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
 
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 
 import java.util.Objects;
 
@@ -34,10 +36,18 @@
  * support message dialog.
  */
 public class RestrictedLockUtils {
+    /**
+     * Get EnforcedAdmin from DevicePolicyManager
+     */
+    @RequiresApi(Build.VERSION_CODES.M)
     public static EnforcedAdmin getProfileOrDeviceOwner(Context context, UserHandle user) {
         return getProfileOrDeviceOwner(context, null, user);
     }
 
+    /**
+     * Get EnforcedAdmin from DevicePolicyManager
+     */
+    @RequiresApi(Build.VERSION_CODES.M)
     public static EnforcedAdmin getProfileOrDeviceOwner(
             Context context, String enforcedRestriction, UserHandle user) {
         if (user == null) {
@@ -73,6 +83,7 @@
     /**
      * Send the intent to trigger the {@code android.settings.ShowAdminSupportDetailsDialog}.
      */
+    @RequiresApi(Build.VERSION_CODES.M)
     public static void sendShowAdminSupportDetailsIntent(Context context, EnforcedAdmin admin) {
         final Intent intent = getShowAdminSupportDetailsIntent(context, admin);
         int targetUserId = UserHandle.myUserId();
@@ -97,6 +108,10 @@
         return intent;
     }
 
+    /**
+     * Check if current user is profile or not
+     */
+    @RequiresApi(Build.VERSION_CODES.M)
     public static boolean isCurrentUserOrProfile(Context context, int userId) {
         UserManager um = context.getSystemService(UserManager.class);
         return um.getUserProfiles().contains(UserHandle.of(userId));
diff --git a/packages/SettingsLib/SearchWidget/res/values-te/strings.xml b/packages/SettingsLib/SearchWidget/res/values-te/strings.xml
index c5ece74..dbad586 100644
--- a/packages/SettingsLib/SearchWidget/res/values-te/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-te/strings.xml
@@ -17,5 +17,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="search_menu" msgid="1914043873178389845">"సెట్టింగ్‌లను వెతకండి"</string>
+    <string name="search_menu" msgid="1914043873178389845">"సెట్టింగ్‌లను సెర్చ్ చేయండి"</string>
 </resources>
diff --git a/packages/SettingsLib/SettingsSpinner/lint-baseline.xml b/packages/SettingsLib/SettingsSpinner/lint-baseline.xml
deleted file mode 100644
index ae1ed38e..0000000
--- a/packages/SettingsLib/SettingsSpinner/lint-baseline.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
-
-    <issue
-        id="NewApi"
-        message="Call requires API level 23 (current min is 21): `new android.widget.Spinner`"
-        errorLine1="        super(context, attrs, defStyleAttr, defStyleRes, mode, null);"
-        errorLine2="        ~~~~~">
-        <location
-            file="frameworks/base/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinner.java"
-            line="122"
-            column="9"/>
-    </issue>
-
-</issues>
diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinner.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinner.java
index 0be80a9f..14286fa 100644
--- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinner.java
+++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/settingsspinner/SettingsSpinner.java
@@ -17,9 +17,12 @@
 package com.android.settingslib.widget.settingsspinner;
 
 import android.content.Context;
+import android.os.Build;
 import android.util.AttributeSet;
 import android.widget.Spinner;
 
+import androidx.annotation.RequiresApi;
+
 import com.android.settingslib.widget.R;
 
 /**
@@ -117,6 +120,7 @@
      * @see Spinner#MODE_DIALOG
      * @see Spinner#MODE_DROPDOWN
      */
+    @RequiresApi(Build.VERSION_CODES.M)
     public SettingsSpinner(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes,
             int mode) {
         super(context, attrs, defStyleAttr, defStyleRes, mode, null);
diff --git a/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_surface_light.xml b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_surface_light.xml
new file mode 100644
index 0000000..037b80a
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/color-v31/settingslib_surface_light.xml
@@ -0,0 +1,18 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@android:color/system_neutral1_500" android:lStar="98" />
+</selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
index 8c7c7ed..c206903 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
@@ -36,4 +36,11 @@
     <color name="settingslib_dialog_colorError">#f28b82</color> <!-- Red 300 -->
 
     <color name="settingslib_colorSurfaceVariant">@android:color/system_neutral1_700</color>
+
+    <color name="settingslib_colorSurfaceHeader">@android:color/system_neutral1_700</color>
+
+    <!-- copy from accent_primary_variant_dark_device_default-->
+    <color name="settingslib_accent_primary_variant">@android:color/system_accent1_300</color>
+
+    <color name="settingslib_text_color_primary_device_default">@android:color/system_neutral1_50</color>
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
index 77f1bcd..0401098 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
@@ -37,9 +37,32 @@
     <!-- Dialog accent color -->
     <color name="settingslib_dialog_accent">@android:color/system_accent1_600</color>
     <!-- Dialog background color -->
-    <color name="settingslib_dialog_background">@*android:color/surface_light</color>
+    <color name="settingslib_dialog_background">@color/settingslib_surface_light</color>
     <!-- Dialog error color. -->
     <color name="settingslib_dialog_colorError">#d93025</color> <!-- Red 600 -->
 
     <color name="settingslib_colorSurfaceVariant">@android:color/system_neutral2_100</color>
+
+    <color name="settingslib_colorSurfaceHeader">@android:color/system_neutral1_100</color>
+
+    <color name="settingslib_accent_device_default_dark">@android:color/system_accent1_100</color>
+
+    <color name="settingslib_accent_device_default_light">@android:color/system_accent1_600</color>
+
+    <color name="settingslib_primary_dark_device_default_settings">@android:color/system_neutral1_900</color>
+
+    <color name="settingslib_primary_device_default_settings_light">@android:color/system_neutral1_50</color>
+
+    <color name="settingslib_accent_primary_device_default">@android:color/system_accent1_100</color>
+
+    <!-- copy from accent_primary_variant_light_device_default-->
+    <color name="settingslib_accent_primary_variant">@android:color/system_accent1_600</color>
+
+    <color name="settingslib_accent_secondary_device_default">@android:color/system_accent2_100</color>
+
+    <color name="settingslib_background_device_default_dark">@android:color/system_neutral1_900</color>
+
+    <color name="settingslib_background_device_default_light">@android:color/system_neutral1_50</color>
+
+    <color name="settingslib_text_color_primary_device_default">@android:color/system_neutral1_900</color>
 </resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml
index ddcc83e..1c33f1a 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml
@@ -19,4 +19,5 @@
     <dimen name="app_preference_padding_start">20dp</dimen>
     <dimen name="app_icon_min_width">52dp</dimen>
     <dimen name="settingslib_preferred_minimum_touch_target">48dp</dimen>
+    <dimen name="settingslib_dialogCornerRadius">28dp</dimen>
 </resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/strings.xml
new file mode 100644
index 0000000..6d072a9
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/strings.xml
@@ -0,0 +1,37 @@
+<?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.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Name of a font family to use for headlines in SettingsLib. -->
+    <string name="settingslib_config_headlineFontFamily" translatable="false">
+        @*android:string/config_headlineFontFamily
+    </string>
+
+    <!-- Name of a font family to use for headlines-medium in SettingsLib. -->
+    <string name="settingslib_config_headlineFontFamilyMedium" translatable="false">
+        @*android:string/config_headlineFontFamilyMedium
+    </string>
+
+    <!-- Name of a font family to use for body in SettingsLib. -->
+    <string name="settingslib_config_bodyFontFamily" translatable="false">
+        @*android:string/config_bodyFontFamily
+    </string>
+
+    <!-- Name of a font family to use for body-medium in SettingsLib. -->
+    <string name="settingslib_config_bodyFontFamilyMedium" translatable="false">
+        @*android:string/config_bodyFontFamilyMedium
+    </string>
+</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
index 46f1e03..5800636 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
@@ -16,12 +16,16 @@
   -->
 <resources>
     <style name="TextAppearance.PreferenceTitle.SettingsLib"
-           parent="@*android:style/TextAppearance.DeviceDefault.ListItem">
+           parent="@android:style/TextAppearance.Material.Subhead">
+        <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item>
         <item name="android:textSize">20sp</item>
     </style>
 
     <style name="TextAppearance.CategoryTitle.SettingsLib"
-           parent="@*android:style/TextAppearance.DeviceDefault.Body2" />
+           parent="@android:style/TextAppearance.DeviceDefault.Medium">
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textSize">14sp</item>
+    </style>
 
     <style name="Switch.SettingsLib" parent="@android:style/Widget.Material.CompoundButton.Switch">
         <item name="android:switchMinWidth">52dp</item>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
index 8034710..6bf288b 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
@@ -50,6 +50,6 @@
         <item name="android:clipToPadding">true</item>
         <item name="android:clipChildren">true</item>
 
-        <item name="dialogCornerRadius">@*android:dimen/config_dialogCornerRadius</item>
+        <item name="dialogCornerRadius">@dimen/settingslib_dialogCornerRadius</item>
     </style>
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values/dimens.xml b/packages/SettingsLib/SettingsTheme/res/values/dimens.xml
index 25f9514..18af1f9 100644
--- a/packages/SettingsLib/SettingsTheme/res/values/dimens.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/dimens.xml
@@ -20,4 +20,5 @@
     <dimen name="app_preference_padding_start">?android:attr/listPreferredItemPaddingStart</dimen>
     <dimen name="app_icon_min_width">56dp</dimen>
     <dimen name="two_target_min_width">72dp</dimen>
+    <dimen name="settingslib_dialogCornerRadius">8dp</dimen>
 </resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values/themes.xml b/packages/SettingsLib/SettingsTheme/res/values/themes.xml
index 6f25177..2d881d1 100644
--- a/packages/SettingsLib/SettingsTheme/res/values/themes.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/themes.xml
@@ -35,7 +35,7 @@
         <!-- TODO(b/189308264): fix the crash in Android R if set the attributes:
              <item name="colorAccent">@*android:color/accent_device_default_light</item>
              <item name="android:colorBackground">@color/settingslib_dialog_background</item>
-             <item name="dialogCornerRadius">@*android:dimen/config_dialogCornerRadius</item>
+             <item name="dialogCornerRadius">@dimen/settingslib_dialogCornerRadius</item>
         -->
         <item name="android:windowSoftInputMode">adjustResize</item>
         <item name="android:clipToPadding">true</item>
diff --git a/packages/SettingsLib/TopIntroPreference/res/values/styles.xml b/packages/SettingsLib/TopIntroPreference/res/values/styles.xml
index 65869b5..b6ca41f 100644
--- a/packages/SettingsLib/TopIntroPreference/res/values/styles.xml
+++ b/packages/SettingsLib/TopIntroPreference/res/values/styles.xml
@@ -16,8 +16,7 @@
   -->
 <resources>
     <style name="TextAppearance.TopIntroText"
-           parent="@*android:style/TextAppearance.DeviceDefault">
-        <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
+           parent="@android:style/TextAppearance.DeviceDefault">
         <item name="android:textSize">14sp</item>
         <item name="android:textColor">?android:attr/textColorSecondary</item>
     </style>
diff --git a/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target_divider.xml b/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target_divider.xml
index f21e51c..4641421 100644
--- a/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target_divider.xml
+++ b/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target_divider.xml
@@ -22,6 +22,8 @@
     android:layout_height="match_parent"
     android:gravity="start|center_vertical"
     android:orientation="horizontal"
+    android:paddingStart="?android:attr/listPreferredItemPaddingEnd"
+    android:paddingLeft="?android:attr/listPreferredItemPaddingEnd"
     android:paddingTop="16dp"
     android:paddingBottom="16dp">
     <View
diff --git a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java
index 44f6f54..9dcb5bc 100644
--- a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java
+++ b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java
@@ -52,9 +52,9 @@
         }
 
         return (VERSION.CODENAME.equals("REL") && VERSION.SDK_INT >= 31)
-                || (VERSION.CODENAME.length() == 1
-                && VERSION.CODENAME.compareTo("S") >= 0
-                && VERSION.CODENAME.compareTo("Z") <= 0);
+                || (VERSION.CODENAME.length() >= 1
+                && VERSION.CODENAME.toUpperCase().charAt(0) >= 'S'
+                && VERSION.CODENAME.toUpperCase().charAt(0) <= 'Z');
     }
 
     private BuildCompatUtils() {}
diff --git a/packages/SettingsLib/lint-baseline.xml b/packages/SettingsLib/lint-baseline.xml
index f6d6ca6..d6ea73d 100644
--- a/packages/SettingsLib/lint-baseline.xml
+++ b/packages/SettingsLib/lint-baseline.xml
@@ -892,4 +892,26 @@
             column="59"/>
     </issue>
 
+    <issue
+        id="NewApi"
+        message="Call requires API level S (current min is 29): `android.os.UserManager#isUserForeground`"
+        errorLine1="                .getSystemService(UserManager.class).isUserForeground();"
+        errorLine2="                                                     ~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminController.java"
+            line="120"
+            column="54"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Call requires API level 31 (current min is 29): `android.os.UserManager#isUserForeground`"
+        errorLine1="                .getSystemService(UserManager.class).isUserForeground();"
+        errorLine2="                                                     ~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/packages/SettingsLib/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminController.java"
+            line="120"
+            column="54"/>
+    </issue>
+
 </issues>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 881b76b..e397035 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -511,7 +511,7 @@
     <string name="alarms_and_reminders_label" msgid="6918395649731424294">"المنبّهات والتذكيرات"</string>
     <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"السماح بضبط المنبّهات والتذكيرات"</string>
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"المنبّهات والتذكيرات"</string>
-    <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"عليك السماح لهذا التطبيق بضبط المنبّهات وتحديد مواعيد للإجراءات الحساسة زمنيًا. يسمح هذا الأذن بتشغيل التطبيق في الخلفية، ما قد يستهلك المزيد من شحن البطارية.\n\nإذا كان هذا الإذن غير مسموح به، لن تعمل الأحداث المستندة إلى وقت والمنبّهات الحالية التي يحدِّد هذا التطبيق موعدها."</string>
+    <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"يمكنك السماح لهذا التطبيق بضبط المنبّهات وجدولة الإجراءات لتنفيذها في الوقت المناسب. ويسمح هذا الإذن بتشغيل التطبيق في الخلفية، ما قد يستهلك المزيد من البطارية.\n\nفي حال عدم تفعيل هذا الإذن، لن تعمل المنبهات الحالية والأحداث المستندة إلى الوقت المضبوطة في هذا التطبيق."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"جدول زمني، جدولة، منبّه، تذكير، ساعة"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"تفعيل"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"تفعيل ميزة \"عدم الإزعاج\""</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 7755cb7..048039f 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -426,7 +426,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalía (rojo-verde)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalía (azul-amarillo)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Corrección de color"</string>
-    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="2333641630205214702">"Ajusta cómo se muestran los colores en tu dispositivo. Esto puede ser útil cuando quieres:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; Ver colores con más exactitud&lt;/li&gt; &lt;li&gt; Quitar colores para tener un enfoque más claro&lt;/li&gt; &lt;/ol&gt;"</string>
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="2333641630205214702">"Ajusta cómo se muestran los colores en tu dispositivo. Esto puede ser útil cuando quieres:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt;&amp;nbsp;Ver colores con más exactitud&lt;/li&gt; &lt;li&gt;&amp;nbsp;Quitar colores para tener un enfoque más claro&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Reemplazado por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Tiempo restante: aproximadamente <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index d835cb9..60469a1 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -507,7 +507,7 @@
     <string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarmid ja meeldetuletused"</string>
     <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Luba alarmide ja meeldetuletuste määramine"</string>
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmid ja meeldetuletused"</string>
-    <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Lubage sellel rakendusel määrata alarme ja ajastada kiire tähtajaga toiminguid. See võimaldab rakendusel töötada taustal, mistõttu võib akukasutus olla suurem.\n\nKui see luba on välja lülitatud, siis olemasolevad alarmid ja selle rakenduse ajastatud ajapõhised sündmused ei tööta."</string>
+    <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Lubage sellel rakendusel määrata alarme ja ajastada ajakriitilisi toiminguid. See võimaldab rakendusel töötada taustal, mistõttu võib akukasutus olla suurem.\n\nKui see luba on välja lülitatud, siis olemasolevad alarmid ja selle rakenduse ajastatud ajapõhised sündmused ei tööta."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ajakava, äratus, meeldetuletus, kell"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Lülita sisse"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Valiku Mitte segada sisselülitamine"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index b503fdb..693a6d2 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -155,7 +155,7 @@
     <string name="running_process_item_user_label" msgid="3988506293099805796">"Utilisateur : <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
     <string name="launch_defaults_some" msgid="3631650616557252926">"Certaines préférences par défaut définies"</string>
     <string name="launch_defaults_none" msgid="8049374306261262709">"Aucune préférence par défaut définie"</string>
-    <string name="tts_settings" msgid="8130616705989351312">"Synthèse vocale"</string>
+    <string name="tts_settings" msgid="8130616705989351312">"Paramètres de synthèse vocale"</string>
     <string name="tts_settings_title" msgid="7602210956640483039">"Synthèse vocale"</string>
     <string name="tts_default_rate_title" msgid="3964187817364304022">"Cadence"</string>
     <string name="tts_default_rate_summary" msgid="3781937042151716987">"Vitesse à laquelle le texte est énoncé"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index bf9b72e..630000f 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -527,7 +527,7 @@
     <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"कनेक्ट करने में समस्या हो रही है. डिवाइस को बंद करके चालू करें"</string>
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"वायर वाला ऑडियो डिवाइस"</string>
     <string name="help_label" msgid="3528360748637781274">"सहायता और सुझाव"</string>
-    <string name="storage_category" msgid="2287342585424631813">"डिवाइस की मेमोरी"</string>
+    <string name="storage_category" msgid="2287342585424631813">"डिवाइस का स्टोरेज"</string>
     <string name="shared_data_title" msgid="1017034836800864953">"शेयर किया गया डेटा"</string>
     <string name="shared_data_summary" msgid="5516326713822885652">"शेयर किए गए डेटा को देखें और उसमें बदलाव करें"</string>
     <string name="shared_data_no_blobs_text" msgid="3108114670341737434">"इस उपयोगकर्ता के साथ किसी तरह का डेटा शेयर नहीं किया गया है."</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 4b37650..c140308 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -507,7 +507,7 @@
     <string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarm dan pengingat"</string>
     <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Izinkan menyetel alarm dan pengingat"</string>
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarm &amp; pengingat"</string>
-    <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Izinkan aplikasi ini menyetel alarm dan menjadwalkan tindakan berjangka waktu. Hal ini memungkinkan aplikasi berjalan di latar belakang, sehingga mungkin menggunakan lebih banyak daya baterai.\n\nJika izin ini dinonaktifkan, alarm dan acara berjangka waktu yang dijadwalkan oleh aplikasi ini tidak akan berfungsi."</string>
+    <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Izinkan aplikasi ini menyetel alarm dan menjadwalkan tindakan yang sensitif waktu. Hal ini memungkinkan aplikasi berjalan di latar belakang, sehingga mungkin menggunakan lebih banyak daya baterai.\n\nJika izin ini dinonaktifkan, alarm dan acara berbasis waktu yang dijadwalkan oleh aplikasi ini tidak akan berfungsi."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"jadwal, alarm, pengingat, jam"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktifkan"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Aktifkan mode Jangan Ganggu"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index b3bdf89..65992a7 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -444,7 +444,7 @@
     <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Carica residua: meno di <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Carica residua: meno di <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Tempo residuo: più di <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
-    <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Tempo residuo: più di <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
+    <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Tempo rimanente: più di <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Il telefono potrebbe spegnersi a breve"</string>
     <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Il tablet potrebbe spegnersi a breve"</string>
     <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Il dispositivo potrebbe spegnersi a breve"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 71cc9a0..9f2d8a2 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -426,7 +426,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"פרוטנומליה (אדום-ירוק)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"טריטנומליה (כחול-צהוב)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"תיקון צבע"</string>
-    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="2333641630205214702">"‏ניתן לשנות את האופן שבו צבעים מוצגים במכשיר. שינוי כזה עשוי לעזור:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt; להבחין בצבעים בצורה יותר מדויקת&lt;/li&gt; &lt;li&gt; להסיר צבעים מסוימים כדי להתמקד&lt;/li&gt; &lt;/ol&gt;"</string>
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="2333641630205214702">"‏ניתן לשנות את האופן שבו צבעים מוצגים במכשיר. שינוי כזה עשוי לעזור:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt;&amp;nbsp;להבחין בצבעים בצורה יותר מדויקת&lt;/li&gt; &lt;li&gt;&amp;nbsp;להסיר צבעים מסוימים כדי להתמקד&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"נעקף על ידי <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"הזמן הנותר: בערך <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index b1aee50..7387985 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -309,7 +309,7 @@
     <string name="dev_settings_warning_message" msgid="37741686486073668">"Бул орнотуулар өндүрүүчүлөр үчүн гана берилген. Булар түзмөгүңүздүн колдонмолорун бузулушуна же туура эмес иштешине алып келиши мүмкүн."</string>
     <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"Орнотулуучу колдонмону текшерүү"</string>
     <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"ADB/ADT аркылуу орнотулган колдонмолордун коопсуздугу текшерилет."</string>
-    <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Аталышсыз Bluetooth түзмөктөрү (MAC даректери менен гана) көрсөтүлөт"</string>
+    <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Аталышсыз Bluetooth түзмөктөрү (MAC даректери менен гана) көрүнөт"</string>
     <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Алыскы түзмөктөр өтө катуу добуш чыгарып же көзөмөлдөнбөй жатса Bluetooth \"Үндүн абсолюттук деңгээли\" функциясын өчүрөт."</string>
     <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Bluetooth Gabeldorsche функциясынын топтомун иштетет."</string>
     <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Жакшыртылган туташуу функциясын иштетет."</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 2576993..474ded9 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -426,7 +426,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Протаномаль (улаан-ногоон)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Тританомаль (цэнхэр-шар)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Өнгө тохируулах"</string>
-    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="2333641630205214702">"Таны төхөөрөмж дээр өнгийг хэрхэн үзүүлэхийг тохируулна уу. Энэ нь танд дараахыг хийхийг хүссэн үед хэрэг болж магадгүй:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt;Өнгийг илүү оновчтой харах&lt;/li&gt; &lt;li&gt;Танд төвлөрөхөд туслахын тулд өнгийг хасах&lt;/li&gt; &lt;/ol&gt;"</string>
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="2333641630205214702">"Таны төхөөрөмж дээр өнгийг хэрхэн үзүүлэхийг тохируулна уу. Энэ нь танд дараахыг хийхийг хүссэн үед хэрэг болж магадгүй:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt;&amp;nbsp;Өнгийг илүү оновчтой харах&lt;/li&gt; &lt;li&gt;&amp;nbsp;Танд төвлөрөхөд туслахын тулд өнгийг хасах&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Давхарласан <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Ойролцоогоор <xliff:g id="TIME_REMAINING">%1$s</xliff:g> үлдсэн"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 864f177..631cb3b 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -307,7 +307,7 @@
     <string name="adb_keys_warning_message" msgid="2968555274488101220">"तुम्ही पूर्वी ऑथोराइझ केलेल्या सर्व संगणकांवरुन USB डीबग करण्यासाठी अ‍ॅक्सेस रीव्होक करायचा?"</string>
     <string name="dev_settings_warning_title" msgid="8251234890169074553">"विकास सेटिंग्जला अनुमती द्यायची?"</string>
     <string name="dev_settings_warning_message" msgid="37741686486073668">"या सेटिंग्जचा हेतू फक्त विकास वापरासाठी आहे. त्यामुळे तुमचे डिव्हाइस आणि त्यावरील ॲप्लिकेशन ब्रेक होऊ शकतात किंवा नेहमीपेक्षा वेगळे वर्तन करू शकतात."</string>
-    <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"USB वर अ‍ॅप्स पडताळून पाहा"</string>
+    <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"USB वर अ‍ॅप्स पडताळून पहा"</string>
     <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"हानिकारक वर्तनासाठी ADB/ADT द्वारे इंस्टॉल अ‍ॅप्स तपासा."</string>
     <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"नावांशिवाय ब्‍लूटूथ डिव्‍हाइस (फक्‍त MAC पत्ते) दाखवले जातील"</string>
     <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"रिमोट डिव्हाइसमध्ये सहन न होणारा मोठा आवाज किंवा नियंत्रणाचा अभाव यासारखी आवाजाची समस्या असल्यास ब्लूटूथ संपूर्ण आवाज वैशिष्ट्य बंद करते."</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index fea9601..2bdd617 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -443,8 +443,8 @@
     <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Bateria może się wyczerpać do <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Pozostało mniej niż <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Pozostało mniej niż <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
-    <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Pozostało mniej niż <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
-    <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Pozostało mniej niż <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
+    <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Pozostało ponad <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+    <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Pozostało ponad <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Wkrótce telefon może się wyłączyć"</string>
     <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Tablet może się wkrótce wyłączyć"</string>
     <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Urządzenie może się wkrótce wyłączyć"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 78b35aa..e39b420 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -507,7 +507,7 @@
     <string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarmet dhe alarmet rikujtuese"</string>
     <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Lejo caktimin e alarmeve dhe alarmeve rikujtuese"</string>
     <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmet dhe alarmet rikujtuese"</string>
-    <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Lejo që ky aplikacion të caktojë alarmet dhe të planifikojë veprime që kanë një afat të caktuar. Kjo lejon që aplikacioni të ekzekutohet në sfond, gjë që mund të përdorë më shumë bateri.\n\nNëse kjo leje është caktuar si joaktive, alarmet ekzistuese dhe ngjarjet me bazë kohore të planifikuara nga ky apliikacion nuk do të funksionojnë."</string>
+    <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Lejo që ky aplikacion të caktojë alarmet dhe të planifikojë veprime që kanë një afat të caktuar. Kjo mundëson që aplikacioni të ekzekutohet në sfond, gjë që mund të përdorë më shumë bateri.\n\nNëse kjo leje është caktuar si joaktive, alarmet ekzistuese dhe ngjarjet në bazë kohore të planifikuara nga ky aplikacion nuk do të funksionojnë."</string>
     <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"planifiko, alarm, alarm rikujtues, ora"</string>
     <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktivizo"</string>
     <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Aktivizo \"Mos shqetëso\""</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 6255d41..8398536 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -84,7 +84,7 @@
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"செயலில் உள்ளது"</string>
     <string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"மீடியா ஆடியோ"</string>
     <string name="bluetooth_profile_headset" msgid="5395952236133499331">"ஃபோன் அழைப்புகள்"</string>
-    <string name="bluetooth_profile_opp" msgid="6692618568149493430">"கோப்பு இடமாற்றம்"</string>
+    <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ஃபைல் இடமாற்றம்"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"உள்ளீட்டுச் சாதனம்"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"இணைய அணுகல்"</string>
     <string name="bluetooth_profile_pbap" msgid="7064307749579335765">"தொடர்புப் பகிர்தல்"</string>
@@ -98,10 +98,10 @@
     <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"செவித்துணை கருவிகளுடன் இணைக்கப்பட்டது"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"மீடியா ஆடியோவுடன் இணைக்கப்பட்டது"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"மொபைல் ஆடியோவுடன் இணைக்கப்பட்டது"</string>
-    <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"கோப்பைப் பரிமாற்றும் சேவையகத்துடன் இணைக்கப்பட்டது"</string>
+    <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"ஃபைலைப் பரிமாற்றும் சேவையகத்துடன் இணைக்கப்பட்டது"</string>
     <string name="bluetooth_map_profile_summary_connected" msgid="4141725591784669181">"வரைபடத்துடன் இணைக்கப்பட்டது"</string>
     <string name="bluetooth_sap_profile_summary_connected" msgid="1280297388033001037">"SAP உடன் இணைக்கப்பட்டது"</string>
-    <string name="bluetooth_opp_profile_summary_not_connected" msgid="3959741824627764954">"கோப்பு இடமாற்றும் சேவையகத்துடன் இணைக்கப்படவில்லை"</string>
+    <string name="bluetooth_opp_profile_summary_not_connected" msgid="3959741824627764954">"ஃபைல் இடமாற்றும் சேவையகத்துடன் இணைக்கப்படவில்லை"</string>
     <string name="bluetooth_hid_profile_summary_connected" msgid="3923653977051684833">"உள்ளீட்டுச் சாதனத்துடன் இணைக்கப்பட்டது"</string>
     <string name="bluetooth_pan_user_profile_summary_connected" msgid="380469653827505727">"சாதனத்துடன் இணைந்தது"</string>
     <string name="bluetooth_pan_nap_profile_summary_connected" msgid="3744773111299503493">"சாதனத்துடன் உள்ளூர் இண்டர்நெட்டைப் பகிர்தல்"</string>
@@ -110,7 +110,7 @@
     <string name="bluetooth_sap_profile_summary_use_for" msgid="6204902866176714046">"சிம் அணுகலுக்குப் பயன்படுத்தும்"</string>
     <string name="bluetooth_a2dp_profile_summary_use_for" msgid="7324694226276491807">"மீடியாவின் ஆடியோவிற்குப் பயன்படுத்து"</string>
     <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"மொபைல் ஆடியோவைப் பயன்படுத்து"</string>
-    <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"கோப்பு பரிமாற்றத்திற்காகப் பயன்படுத்து"</string>
+    <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ஃபைல் பரிமாற்றத்திற்காகப் பயன்படுத்து"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"உள்ளீட்டுக்குப் பயன்படுத்து"</string>
     <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"செவித்துணை கருவிகளுக்குப் பயன்படுத்தவும்"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"இணை"</string>
@@ -412,11 +412,11 @@
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView செயல்படுத்தல்"</string>
     <string name="select_webview_provider_dialog_title" msgid="2444261109877277714">"WebView செயல்படுத்தலை அமை"</string>
     <string name="select_webview_provider_toast_text" msgid="8512254949169359848">"இனி இந்தத் தேர்வைப் பயன்படுத்த முடியாது. மீண்டும் முயலவும்."</string>
-    <string name="convert_to_file_encryption" msgid="2828976934129751818">"கோப்பு முறைமையாக்கத்திற்கு மாற்று"</string>
+    <string name="convert_to_file_encryption" msgid="2828976934129751818">"ஃபைல் முறைமையாக்கத்திற்கு மாற்று"</string>
     <string name="convert_to_file_encryption_enabled" msgid="840757431284311754">"மாற்று…"</string>
-    <string name="convert_to_file_encryption_done" msgid="8965831011811180627">"ஏற்கனவே கோப்பு என்க்ரிப்ட் செய்யப்பட்டது"</string>
-    <string name="title_convert_fbe" msgid="5780013350366495149">"கோப்பு சார்ந்த முறைமையாக்கத்திற்கு மாற்றுதல்"</string>
-    <string name="convert_to_fbe_warning" msgid="34294381569282109">"தரவுப் பகிர்வை, கோப்பு சார்ந்த முறைமையாக்கத்திற்கு மாற்றவும்.\n !!எச்சரிக்கை!! இது எல்லா தரவையும் அழிக்கும்.\n இது ஆல்பா நிலை அம்சமாக இருப்பதால் சரியாகச் செயல்படாமல் போகக்கூடும்.\n தொடர, \'அழித்து, மாற்று…\' என்பதை அழுத்தவும்."</string>
+    <string name="convert_to_file_encryption_done" msgid="8965831011811180627">"ஏற்கனவே ஃபைல் என்க்ரிப்ட் செய்யப்பட்டது"</string>
+    <string name="title_convert_fbe" msgid="5780013350366495149">"ஃபைல் சார்ந்த முறைமையாக்கத்திற்கு மாற்றுதல்"</string>
+    <string name="convert_to_fbe_warning" msgid="34294381569282109">"தரவுப் பகிர்வை, ஃபைல் சார்ந்த முறைமையாக்கத்திற்கு மாற்றவும்.\n !!எச்சரிக்கை!! இது எல்லா தரவையும் அழிக்கும்.\n இது ஆல்பா நிலை அம்சமாக இருப்பதால் சரியாகச் செயல்படாமல் போகக்கூடும்.\n தொடர, \'அழித்து, மாற்று…\' என்பதை அழுத்தவும்."</string>
     <string name="button_convert_fbe" msgid="1159861795137727671">"அழித்து மாற்று…"</string>
     <string name="picture_color_mode" msgid="1013807330552931903">"படத்தின் வண்ணப் பயன்முறை"</string>
     <string name="picture_color_mode_desc" msgid="151780973768136200">"sRGBஐப் பயன்படுத்தும்"</string>
@@ -426,7 +426,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"நிறம் அடையாளங்காண முடியாமை (சிவப்பு-பச்சை)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"நிறம் அடையாளங்காண முடியாமை (நீலம்-மஞ்சள்)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"வண்ணத்திருத்தம்"</string>
-    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="2333641630205214702">"சாதனத்தில் வண்ணங்கள் காண்பிக்கப்படும் விதத்தைச் சரிசெய்யலாம். இதன் மூலம் நீங்கள் விரும்பும்போதெல்லாம்:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt;&amp;nbsp;வண்ணங்களை மிகத் தெளிவாகப் பார்க்கலாம்&lt;/li&gt; &lt;li&gt;கவனம் சிதறாமல் இருக்க வண்ணங்களை நீக்கலாம்&lt;/li&gt; &lt;/ol&gt;"</string>
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="2333641630205214702">"சாதனத்தில் வண்ணங்கள் காண்பிக்கப்படும் விதத்தைச் சரிசெய்யலாம். இதன் மூலம் நீங்கள் விரும்பும்போதெல்லாம்:&lt;br/&gt;&lt;br/&gt; &lt;ol&gt; &lt;li&gt;&amp;nbsp;வண்ணங்களை மிகத் தெளிவாகப் பார்க்கலாம்&lt;/li&gt; &lt;li&gt;&amp;nbsp;கவனம் சிதறாமல் இருக்க வண்ணங்களை நீக்கலாம்&lt;/li&gt; &lt;/ol&gt;"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> மூலம் மேலெழுதப்பட்டது"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"கிட்டத்தட்ட <xliff:g id="TIME_REMAINING">%1$s</xliff:g> மீதமுள்ளது"</string>
diff --git a/packages/SettingsLib/res/values-te/arrays.xml b/packages/SettingsLib/res/values-te/arrays.xml
index 99efbde..e0ba520 100644
--- a/packages/SettingsLib/res/values-te/arrays.xml
+++ b/packages/SettingsLib/res/values-te/arrays.xml
@@ -25,7 +25,7 @@
     <item msgid="3288373008277313483">"స్కాన్ చేస్తోంది…"</item>
     <item msgid="6050951078202663628">"కనెక్ట్ చేస్తోంది..."</item>
     <item msgid="8356618438494652335">"ప్రామాణీకరిస్తోంది…"</item>
-    <item msgid="2837871868181677206">"IP చిరునామాను పొందుతోంది…"</item>
+    <item msgid="2837871868181677206">"IP అడ్రస్‌ను పొందుతోంది…"</item>
     <item msgid="4613015005934755724">"కనెక్ట్ చేయబడింది"</item>
     <item msgid="3763530049995655072">"తాత్కాలికంగా రద్దు చేయబడింది"</item>
     <item msgid="7852381437933824454">"డిస్‌కనెక్ట్ చేస్తోంది..."</item>
@@ -39,7 +39,7 @@
     <item msgid="1818677602615822316">"స్కాన్ చేస్తోంది…"</item>
     <item msgid="8339720953594087771">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>కి కనెక్ట్ చేస్తోంది…"</item>
     <item msgid="3028983857109369308">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>తో ప్రామాణీకరిస్తోంది…"</item>
-    <item msgid="4287401332778341890">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> నుండి IP చిరునామాను పొందుతోంది…"</item>
+    <item msgid="4287401332778341890">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> నుండి IP అడ్రస్‌ను పొందుతోంది…"</item>
     <item msgid="1043944043827424501">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g>కి కనెక్ట్ చేయబడింది"</item>
     <item msgid="7445993821842009653">"తాత్కాలికంగా రద్దు చేయబడింది"</item>
     <item msgid="1175040558087735707">"<xliff:g id="NETWORK_NAME">%1$s</xliff:g> నుండి డిస్‌కనెక్ట్ చేస్తోంది…"</item>
@@ -138,15 +138,15 @@
     <item msgid="1333279807604675720">"స్టీరియో"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
-    <item msgid="1241278021345116816">"ఆడియో నాణ్యత (990kbps/909kbps) కోసం అనుకూలీకరించబడింది"</item>
-    <item msgid="3523665555859696539">"సమతుల్య ఆడియో మరియు కనెక్షన్ నాణ్యత (660kbps/606kbps)"</item>
-    <item msgid="886408010459747589">"కనెక్షన్ నాణ్యత (330kbps/303kbps) కోసం అనుకూలీకరించబడింది"</item>
+    <item msgid="1241278021345116816">"ఆడియో క్వాలిటీ (990kbps/909kbps) కోసం అనుకూలీకరించబడింది"</item>
+    <item msgid="3523665555859696539">"సమతుల్య ఆడియో మరియు కనెక్షన్ క్వాలిటీ (660kbps/606kbps)"</item>
+    <item msgid="886408010459747589">"కనెక్షన్ క్వాలిటీ (330kbps/303kbps) కోసం అనుకూలీకరించబడింది"</item>
     <item msgid="3808414041654351577">"ఉత్తమ కృషి (అనుకూల బిట్ రేట్)"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries">
-    <item msgid="804499336721569838">"ఆడియో నాణ్యత కోసం అనుకూలీకరించబడింది"</item>
-    <item msgid="7451422070435297462">"సమతుల్య ఆడియో మరియు కనెక్షన్ నాణ్యత"</item>
-    <item msgid="6173114545795428901">"కనెక్షన్ నాణ్యత కోసం అనుకూలీకరించబడింది"</item>
+    <item msgid="804499336721569838">"ఆడియో క్వాలిటీ కోసం అనుకూలీకరించబడింది"</item>
+    <item msgid="7451422070435297462">"సమతుల్య ఆడియో మరియు కనెక్షన్ క్వాలిటీ"</item>
+    <item msgid="6173114545795428901">"కనెక్షన్ క్వాలిటీ కోసం అనుకూలీకరించబడింది"</item>
     <item msgid="4349908264188040530">"ఉత్తమ కృషి (అనుకూల బిట్ రేట్)"</item>
   </string-array>
   <string-array name="bluetooth_audio_active_device_summaries">
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index a870026..1c03e22 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -26,19 +26,19 @@
     <string name="wifi_disconnected" msgid="7054450256284661757">"డిస్‌కనెక్ట్ అయ్యింది"</string>
     <string name="wifi_disabled_generic" msgid="2651916945380294607">"డిజేబుల్ చేయబడింది"</string>
     <string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP కాన్ఫిగరేషన్ వైఫల్యం"</string>
-    <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"తక్కువ నాణ్యతా నెట్‌వర్క్ కారణంగా కనెక్ట్ చేయబడలేదు"</string>
+    <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"తక్కువ క్వాలిటీ నెట్‌వర్క్ కారణంగా కనెక్ట్ చేయబడలేదు"</string>
     <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi కనెక్షన్ వైఫల్యం"</string>
     <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"ప్రామాణీకరణ సమస్య"</string>
     <string name="wifi_cant_connect" msgid="5718417542623056783">"కనెక్ట్ చేయడం సాధ్యపడదు"</string>
     <string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\'<xliff:g id="AP_NAME">%1$s</xliff:g>\'కు కనెక్ట్ చేయడం సాధ్యపడదు"</string>
     <string name="wifi_check_password_try_again" msgid="8817789642851605628">"పాస్‌వర్డ్‌ను చెక్ చేసి, మళ్లీ ప్రయత్నించండి"</string>
     <string name="wifi_not_in_range" msgid="1541760821805777772">"పరిధిలో లేదు"</string>
-    <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"స్వయంచాలకంగా కనెక్ట్ కాదు"</string>
+    <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ఆటోమేటిక్‌గా కనెక్ట్ కాదు"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"ఇంటర్నెట్ యాక్సెస్ లేదు"</string>
     <string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ద్వారా సేవ్ చేయబడింది"</string>
     <string name="connected_to_metered_access_point" msgid="9179693207918156341">"డేటా నియంత్రణ నెట్‌వర్క్‌కు కనెక్ట్ చేయబడింది"</string>
-    <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ద్వారా స్వయంచాలకంగా కనెక్ట్ చేయబడింది"</string>
-    <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"నెట్‌వర్క్ రేటింగ్ ప్రదాత ద్వారా స్వయంచాలకంగా కనెక్ట్ చేయబడింది"</string>
+    <string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ద్వారా ఆటోమేటిక్‌గా కనెక్ట్ చేయబడింది"</string>
+    <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"నెట్‌వర్క్ రేటింగ్ ప్రదాత ద్వారా ఆటోమేటిక్‌గా కనెక్ట్ చేయబడింది"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ద్వారా కనెక్ట్ చేయబడింది"</string>
     <string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> ద్వారా కనెక్ట్ చేయబడింది"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s ద్వారా అందుబాటులో ఉంది"</string>
@@ -83,14 +83,14 @@
     <string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> బ్యాటరీ, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> బ్యాటరీ"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"యాక్టివ్‌గా ఉంది"</string>
     <string name="bluetooth_profile_a2dp" msgid="4632426382762851724">"మీడియా ఆడియో"</string>
-    <string name="bluetooth_profile_headset" msgid="5395952236133499331">"ఫోన్ కాల్‌లు"</string>
+    <string name="bluetooth_profile_headset" msgid="5395952236133499331">"ఫోన్ కాల్స్‌"</string>
     <string name="bluetooth_profile_opp" msgid="6692618568149493430">"ఫైల్ బదిలీ"</string>
     <string name="bluetooth_profile_hid" msgid="2969922922664315866">"ఇన్‌పుట్ పరికరం"</string>
     <string name="bluetooth_profile_pan" msgid="1006235139308318188">"ఇంటర్నెట్ యాక్సెస్"</string>
     <string name="bluetooth_profile_pbap" msgid="7064307749579335765">"కాంటాక్ట్ షేరింగ్"</string>
-    <string name="bluetooth_profile_pbap_summary" msgid="2955819694801952056">"పరిచయ భాగస్వామ్యం కోసం ఉపయోగించు"</string>
+    <string name="bluetooth_profile_pbap_summary" msgid="2955819694801952056">"కాంటాక్ట్ షేరింగ్ కోసం ఉపయోగించండి"</string>
     <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ఇంటర్నెట్ కనెక్షన్ భాగస్వామ్యం"</string>
-    <string name="bluetooth_profile_map" msgid="8907204701162107271">"వచన సందేశాలు"</string>
+    <string name="bluetooth_profile_map" msgid="8907204701162107271">"వచన మెసేజ్‌లు"</string>
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM యాక్సెస్"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD ఆడియో: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD ఆడియో"</string>
@@ -116,7 +116,7 @@
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"జత చేయి"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"జత చేయి"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"రద్దు చేయి"</string>
-    <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"జత చేయడం వలన కనెక్ట్ చేయబడినప్పుడు మీ పరిచయాలకు మరియు కాల్ చరిత్రకు ప్రాప్యతను మంజూరు చేస్తుంది."</string>
+    <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"జత చేయడం వలన కనెక్ట్ చేయబడినప్పుడు మీ పరిచయాలకు మరియు కాల్ చరిత్రకు యాక్సెస్‌ను మంజూరు చేస్తుంది."</string>
     <string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>తో జత చేయడం సాధ్యపడలేదు."</string>
     <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"పిన్ లేదా పాస్‌కీ చెల్లని కారణంగా <xliff:g id="DEVICE_NAME">%1$s</xliff:g>తో పెయిర్ చేయడం సాధ్యపడలేదు."</string>
     <string name="bluetooth_pairing_device_down_error_message" msgid="2554424863101358857">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>తో కమ్యూనికేట్ చేయడం సాధ్యపడదు."</string>
@@ -172,7 +172,7 @@
     <string name="tts_engine_security_warning" msgid="3372432853837988146">"ఈ ప్రసంగ సమన్వయ ఇంజిన్ చదివి వినిపించబడే మొత్తం వచనాన్ని అలాగే పాస్‌వర్డ‌లు మరియు క్రెడిట్ కార్డు నంబర్‌ల వంటి వ్యక్తిగత డేటాను సేకరించగలదు. ఇది <xliff:g id="TTS_PLUGIN_ENGINE_NAME">%s</xliff:g> ఇంజిన్‌లో అందించబడుతుంది. ఈ ప్రసంగ సమన్వయ ఇంజిన్ యొక్క వినియోగాన్ని ప్రారంభించాలా?"</string>
     <string name="tts_engine_network_required" msgid="8722087649733906851">"వచనం నుండి ప్రసంగం అవుట్‌పుట్ కోసం ఈ భాషకు పని చేస్తున్న నెట్‌వర్క్ కనెక్షన్ కావాలి."</string>
     <string name="tts_default_sample_string" msgid="6388016028292967973">"ఇది ప్రసంగ సమన్వయానికి ఉదాహరణ"</string>
-    <string name="tts_status_title" msgid="8190784181389278640">"డిఫాల్ట్ భాష స్థితి"</string>
+    <string name="tts_status_title" msgid="8190784181389278640">"ఆటోమేటిక్ భాష స్టేటస్"</string>
     <string name="tts_status_ok" msgid="8583076006537547379">"<xliff:g id="LOCALE">%1$s</xliff:g>కి పూర్తి మద్దతు ఉంది"</string>
     <string name="tts_status_requires_network" msgid="8327617638884678896">"<xliff:g id="LOCALE">%1$s</xliff:g>కి నెట్‌వర్క్ కనెక్షన్ అవసరం"</string>
     <string name="tts_status_not_supported" msgid="2702997696245523743">"<xliff:g id="LOCALE">%1$s</xliff:g>కు మద్దతు లేదు"</string>
@@ -182,7 +182,7 @@
     <string name="tts_engine_preference_section_title" msgid="3861562305498624904">"ప్రాధాన్య ఇంజిన్"</string>
     <string name="tts_general_section_title" msgid="8919671529502364567">"సాధారణం"</string>
     <string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"ప్రసంగ స్వర స్థాయిని రీసెట్ చేయండి"</string>
-    <string name="tts_reset_speech_pitch_summary" msgid="6822904157021406449">"వచనాన్ని చదివి వినిపించే స్వర స్థాయిని డిఫాల్ట్‌కి రీసెట్ చేస్తుంది."</string>
+    <string name="tts_reset_speech_pitch_summary" msgid="6822904157021406449">"టెక్స్ట్‌ను చదివి వినిపించే స్వర స్థాయిని ఆటోమేటిక్‌కు రీసెట్ చేస్తుంది."</string>
   <string-array name="tts_rate_entries">
     <item msgid="9004239613505400644">"చాలా నెమ్మది"</item>
     <item msgid="1815382991399815061">"నెమ్మది"</item>
@@ -199,7 +199,7 @@
     <string name="category_work" msgid="4014193632325996115">"ఆఫీస్"</string>
     <string name="development_settings_title" msgid="140296922921597393">"డెవలపర్ ఆప్షన్‌లు"</string>
     <string name="development_settings_enable" msgid="4285094651288242183">"డెవలపర్ ఎంపికలను ప్రారంభించండి"</string>
-    <string name="development_settings_summary" msgid="8718917813868735095">"అనువర్తన అభివృద్ధి కోసం ఎంపికలను సెట్ చేయండి"</string>
+    <string name="development_settings_summary" msgid="8718917813868735095">"యాప్‌ అభివృద్ధి కోసం ఎంపికలను సెట్ చేయండి"</string>
     <string name="development_settings_not_available" msgid="355070198089140951">"ఈ వినియోగదారు కోసం డెవలపర్ ఎంపికలు అందుబాటులో లేవు"</string>
     <string name="vpn_settings_not_available" msgid="2894137119965668920">"VPN సెట్టింగ్‌లు ఈ వినియోగదారుకి అందుబాటులో లేవు"</string>
     <string name="tethering_settings_not_available" msgid="266821736434699780">"టీథరింగ్ సెట్టింగ్‌లు ఈ వినియోగదారుకి అందుబాటులో లేవు"</string>
@@ -227,12 +227,12 @@
     <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Wi‑Fi పెయిరింగ్ కోడ్"</string>
     <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"పెయిరింగ్ విఫలమైంది"</string>
     <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"పరికరం అదే నెట్‌వర్క్‌కు కనెక్ట్ చేయబడి ఉందని నిర్ధారించుకోండి."</string>
-    <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"QR కోడ్‌ను స్కాన్ చేయడం ద్వారా Wi-Fiని ఉపయోగించి పరికరాన్ని పెయిర్ చెయ్యండి"</string>
+    <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"QR కోడ్‌ను స్కాన్ చేయడం ద్వారా Wi-Fiని ఉపయోగించి పరికరాన్ని పెయిర్ చేయండి"</string>
     <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"పరికరం పెయిర్ చేయబడుతోంది…"</string>
     <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"పరికరాన్ని పెయిర్ చేయడం విఫలమైంది. QR కోడ్ తప్పుగా ఉండడం గాని, లేదా పరికరం అదే నెట్‌వర్క్‌కు కనెక్ట్ అయి లేకపోవడం గాని జరిగింది."</string>
     <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP అడ్రస్ &amp; పోర్ట్"</string>
     <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"QR కోడ్‌ను స్కాన్ చేయండి"</string>
-    <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"QR కోడ్‌ను స్కాన్ చేయడం ద్వారా Wi-Fiని ఉపయోగించి పరికరాన్ని పెయిర్ చెయ్యండి"</string>
+    <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"QR కోడ్‌ను స్కాన్ చేయడం ద్వారా Wi-Fiని ఉపయోగించి పరికరాన్ని పెయిర్ చేయండి"</string>
     <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"దయచేసి Wi-Fi నెట్‌వర్క్‌కు కనెక్ట్ చేయండి"</string>
     <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, డీబగ్, dev"</string>
     <string name="bugreport_in_power" msgid="8664089072534638709">"బగ్ రిపోర్ట్ షార్ట్‌కట్"</string>
@@ -271,8 +271,8 @@
     <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4898693684282596143">"బ్లూటూత్ ఆడియో కోడెక్‌ని సక్రియం చేయండి\nఎంపిక: ఒక్కో నమూనాలో బిట్‌లు"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="364277285688014427">"బ్లూటూత్ ఆడియో ఛానెల్ మోడ్"</string>
     <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="2076949781460359589">"బ్లూటూత్ ఆడియో కోడెక్‌ని సక్రియం చేయండి\nఎంపిక: ఛానెల్ మోడ్"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3233402355917446304">"బ్లూటూత్ ఆడియో LDAC కోడెక్: ప్లేబ్యాక్ నాణ్యత"</string>
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="7274396574659784285">"బ్లూటూత్ ఆడియో LDAC యాక్టివ్ చేయండి\nకోడెక్ ఎంపిక: ప్లేబ్యాక్ నాణ్యత"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality" msgid="3233402355917446304">"బ్లూటూత్ ఆడియో LDAC కోడెక్: ప్లేబ్యాక్ క్వాలిటీ"</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="7274396574659784285">"బ్లూటూత్ ఆడియో LDAC యాక్టివ్ చేయండి\nకోడెక్ ఎంపిక: ప్లేబ్యాక్ క్వాలిటీ"</string>
     <string name="bluetooth_select_a2dp_codec_streaming_label" msgid="2040810756832027227">"ప్రసారం చేస్తోంది: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
     <string name="select_private_dns_configuration_title" msgid="7887550926056143018">"ప్రైవేట్ DNS"</string>
     <string name="select_private_dns_configuration_dialog_title" msgid="3731422918335951912">"ప్రైవేట్ DNS మోడ్‌ను ఎంచుకోండి"</string>
@@ -301,10 +301,10 @@
     <string name="mobile_data_always_on_summary" msgid="1112156365594371019">"ఎల్లప్పుడూ మొబైల్ డేటాను యాక్టివ్‌గా ఉంచు, Wi‑Fi యాక్టివ్‌గా ఉన్నా కూడా (వేగవంతమైన నెట్‌వర్క్ మార్పు కోసం)."</string>
     <string name="tethering_hardware_offload_summary" msgid="7801345335142803029">"అందుబాటులో ఉంటే టెథెరింగ్ హార్డ్‌వేర్ వేగవృద్ధిని ఉపయోగించండి"</string>
     <string name="adb_warning_title" msgid="7708653449506485728">"USB డీబగ్గింగ్‌ను అనుమతించాలా?"</string>
-    <string name="adb_warning_message" msgid="8145270656419669221">"USB డీబగ్గింగ్ అనేది అభివృద్ధి ప్రయోజనాల కోసం మాత్రమే ఉద్దేశించబడింది. మీ కంప్యూటర్ మరియు మీ పరికరం మధ్య డేటాను కాపీ చేయడానికి, నోటిఫికేషన్ లేకుండా మీ పరికరంలో అనువర్తనాలను ఇన్‌స్టాల్ చేయడానికి మరియు లాగ్ డేటాను చదవడానికి దీన్ని ఉపయోగించండి."</string>
+    <string name="adb_warning_message" msgid="8145270656419669221">"USB డీబగ్గింగ్ అనేది అభివృద్ధి ప్రయోజనాల కోసం మాత్రమే ఉద్దేశించబడింది. మీ కంప్యూటర్ మరియు మీ పరికరం మధ్య డేటాను కాపీ చేయడానికి, నోటిఫికేషన్ లేకుండా మీ పరికరంలో యాప్‌లను ఇన్‌స్టాల్ చేయడానికి మరియు లాగ్ డేటాను చదవడానికి దీన్ని ఉపయోగించండి."</string>
     <string name="adbwifi_warning_title" msgid="727104571653031865">"వైర్‌లెస్ డీబగ్గింగ్‌ను అనుమతించాలా?"</string>
     <string name="adbwifi_warning_message" msgid="8005936574322702388">"వైర్‌లెస్ డీబగ్గింగ్ అనేది అభివృద్ధి ప్రయోజనాల కోసం మాత్రమే ఉద్దేశించబడింది. మీ కంప్యూటర్, పరికరాల మధ్య డేటాను కాపీ చేయడానికి, నోటిఫికేషన్ లేకుండా మీ పరికరంలో యాప్‌లను ఇన్‌స్టాల్ చేయడానికి, లాగ్ డేటాను చదవడానికి దీన్ని ఉపయోగించండి."</string>
-    <string name="adb_keys_warning_message" msgid="2968555274488101220">"మీరు గతంలో ప్రామాణీకరించిన అన్ని కంప్యూటర్‌ల నుండి USB డీబగ్గింగ్‌కు ప్రాప్యతను ఉపసంహరించాలా?"</string>
+    <string name="adb_keys_warning_message" msgid="2968555274488101220">"మీరు గతంలో ప్రామాణీకరించిన అన్ని కంప్యూటర్‌ల నుండి USB డీబగ్గింగ్‌కు యాక్సెస్‌ను ఉపసంహరించాలా?"</string>
     <string name="dev_settings_warning_title" msgid="8251234890169074553">"అభివృద్ధి సెట్టింగ్‌లను అనుమతించాలా?"</string>
     <string name="dev_settings_warning_message" msgid="37741686486073668">"ఈ సెట్టింగ్‌లు అభివృద్ధి వినియోగం కోసం మాత్రమే ఉద్దేశించబడినవి. వీటి వలన మీ పరికరం మరియు దీనిలోని యాప్‌లు విచ్ఛిన్నం కావచ్చు లేదా తప్పుగా ప్రవర్తించవచ్చు."</string>
     <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"USB ద్వారా యాప్‌లను వెరిఫై చేయి"</string>
@@ -314,7 +314,7 @@
     <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"బ్లూటూత్ Gabeldorsche ఫీచర్ స్ట్యాక్‌ను ఎనేబుల్ చేస్తుంది."</string>
     <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"మెరుగైన కనెక్టివిటీ ఫీచర్‌ను ఎనేబుల్ చేస్తుంది."</string>
     <string name="enable_terminal_title" msgid="3834790541986303654">"స్థానిక టెర్మినల్"</string>
-    <string name="enable_terminal_summary" msgid="2481074834856064500">"స్థానిక షెల్ ప్రాప్యతను అందించే టెర్మినల్ యాప్‌ను ప్రారంభించు"</string>
+    <string name="enable_terminal_summary" msgid="2481074834856064500">"స్థానిక షెల్ యాక్సెస్‌ను అందించే టెర్మినల్ యాప్‌ను ప్రారంభించు"</string>
     <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP చెకింగ్‌"</string>
     <string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"HDCP తనిఖీ ప్రవర్తనను సెట్ చేయండి"</string>
     <string name="debug_debugging_category" msgid="535341063709248842">"డీబగ్గింగ్"</string>
@@ -383,12 +383,12 @@
     <string name="local_backup_password_title" msgid="4631017948933578709">"డెస్క్‌టాప్ బ్యాకప్ పాస్‌వర్డ్"</string>
     <string name="local_backup_password_summary_none" msgid="7646898032616361714">"డెస్క్‌టాప్ పూర్తి బ్యాకప్‌లు ప్రస్తుతం రక్షించబడలేదు"</string>
     <string name="local_backup_password_summary_change" msgid="1707357670383995567">"డెస్క్‌టాప్ పూర్తి బ్యాకప్‌ల కోసం పాస్‌వర్డ్‌ను మార్చడానికి లేదా తీసివేయడానికి నొక్కండి"</string>
-    <string name="local_backup_password_toast_success" msgid="4891666204428091604">"కొత్త బ్యాకప్ పాస్‌వర్డ్‌ను సెట్ చేసారు"</string>
+    <string name="local_backup_password_toast_success" msgid="4891666204428091604">"కొత్త బ్యాకప్ పాస్‌వర్డ్‌ను సెట్ చేశారు"</string>
     <string name="local_backup_password_toast_confirmation_mismatch" msgid="2994718182129097733">"కొత్త పాస్‌వర్డ్ మరియు నిర్ధారణ సరిపోలడం లేదు"</string>
     <string name="local_backup_password_toast_validation_failure" msgid="714669442363647122">"బ్యాకప్ పాస్‌వర్డ్‌ను సెట్ చేయడంలో వైఫల్యం"</string>
     <string name="loading_injected_setting_summary" msgid="8394446285689070348">"లోడ్ చేస్తోంది…"</string>
   <string-array name="color_mode_names">
-    <item msgid="3836559907767149216">"సచేతనం (డిఫాల్ట్)"</item>
+    <item msgid="3836559907767149216">"వైబ్రంట్ (ఆటోమేటిక్)"</item>
     <item msgid="9112200311983078311">"సహజం"</item>
     <item msgid="6564241960833766170">"స్టాండర్డ్"</item>
   </string-array>
@@ -408,7 +408,7 @@
     <string name="transcode_notification" msgid="5560515979793436168">"ట్రాన్స్‌కోడింగ్ నోటిఫికేషన్‌లను చూపండి"</string>
     <string name="transcode_disable_cache" msgid="3160069309377467045">"ట్రాన్స్‌కోడింగ్ కాష్‌ను డిజేబుల్ చేయండి"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"అమలులో ఉన్న సర్వీస్‌లు"</string>
-    <string name="runningservices_settings_summary" msgid="1046080643262665743">"ప్రస్తుతం అమలులో ఉన్న సర్వీస్‌లను వీక్షించండి, కంట్రోల్‌ చేయండి"</string>
+    <string name="runningservices_settings_summary" msgid="1046080643262665743">"ప్రస్తుతం అమలులో ఉన్న సర్వీస్‌లను చూడండి, కంట్రోల్‌ చేయండి"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"వెబ్ వీక్షణ అమలు"</string>
     <string name="select_webview_provider_dialog_title" msgid="2444261109877277714">"వెబ్ వీక్షణ అమలుని సెట్ చేయండి"</string>
     <string name="select_webview_provider_toast_text" msgid="8512254949169359848">"ఈ ఎంపిక ఇప్పుడు లేదు. మళ్లీ ప్రయత్నించండి."</string>
@@ -489,7 +489,7 @@
     <string name="active_input_method_subtypes" msgid="4232680535471633046">"సక్రియ ఇన్‌పుట్ పద్ధతులు"</string>
     <string name="use_system_language_to_select_input_method_subtypes" msgid="4865195835541387040">"సిస్టమ్ భాషలను ఉపయోగించు"</string>
     <string name="failed_to_open_app_settings_toast" msgid="764897252657692092">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> యొక్క సెట్టింగ్‌లను తెరవడం విఫలమైంది"</string>
-    <string name="ime_security_warning" msgid="6547562217880551450">"ఈ ఇన్‌పుట్ పద్ధతి మీరు టైప్ చేసే మొత్తం వచనాన్ని అలాగే పాస్‌వర్డ్‌లు మరియు క్రెడిట్ కార్డు నంబర్‌ల వంటి వ్యక్తిగత డేటాను సేకరించగలదు. ఇది <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> అనువర్తనంలో అందించబడుతుంది. ఈ ఇన్‌పుట్ పద్ధతిని ఉపయోగించాలా?"</string>
+    <string name="ime_security_warning" msgid="6547562217880551450">"ఈ ఇన్‌పుట్ పద్ధతి మీరు టైప్ చేసే మొత్తం వచనాన్ని అలాగే పాస్‌వర్డ్‌లు మరియు క్రెడిట్ కార్డు నంబర్‌ల వంటి వ్యక్తిగత డేటాను సేకరించగలదు. ఇది <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> యాప్‌లో అందించబడుతుంది. ఈ ఇన్‌పుట్ పద్ధతిని ఉపయోగించాలా?"</string>
     <string name="direct_boot_unaware_dialog_message" msgid="7845398276735021548">"గమనిక: రీబూట్ చేసాక, మీరు మీ ఫోన్‌ను అన్‌లాక్ చేసే వరకు ఈ యాప్ ప్రారంభం కాదు"</string>
     <string name="ims_reg_title" msgid="8197592958123671062">"IMS నమోదు స్థితి"</string>
     <string name="ims_reg_status_registered" msgid="884916398194885457">"నమోదు చేయబడింది"</string>
@@ -529,7 +529,7 @@
     <string name="help_label" msgid="3528360748637781274">"సహాయం &amp; ఫీడ్‌బ్యాక్"</string>
     <string name="storage_category" msgid="2287342585424631813">"స్టోరేజ్"</string>
     <string name="shared_data_title" msgid="1017034836800864953">"షేర్ చేసిన డేటా"</string>
-    <string name="shared_data_summary" msgid="5516326713822885652">"షేర్ చేసిన డేటాను చూసి, సవరించండి"</string>
+    <string name="shared_data_summary" msgid="5516326713822885652">"షేర్ చేసిన డేటాను చూసి, ఎడిట్ చేయండి"</string>
     <string name="shared_data_no_blobs_text" msgid="3108114670341737434">"ఈ యూజర్ కోసం షేర్ చేసిన డేటా ఏదీ లేదు."</string>
     <string name="shared_data_query_failure_text" msgid="3489828881998773687">"షేర్ చేసిన డేటా పొందడంలో ఎర్రర్ ఏర్పడింది. మళ్లీ ట్రై చేయండి."</string>
     <string name="blob_id_text" msgid="8680078988996308061">"షేర్ చేసిన డేటా ID: <xliff:g id="BLOB_ID">%d</xliff:g>"</string>
@@ -541,8 +541,8 @@
     <string name="accessor_expires_text" msgid="4625619273236786252">"లీజు గడువు <xliff:g id="DATE">%s</xliff:g>తో ముగుస్తుంది"</string>
     <string name="delete_blob_text" msgid="2819192607255625697">"షేర్ చేసిన డేటాను తొలగించు"</string>
     <string name="delete_blob_confirmation_text" msgid="7807446938920827280">"మీరు ఖచ్చితంగా ఈ షేర్ చేసిన డేటాను తొలగించాలనుకుంటున్నారా?"</string>
-    <string name="user_add_user_item_summary" msgid="5748424612724703400">"వినియోగదారులు వారి స్వంత అనువర్తనాలను మరియు కంటెంట్‌ను కలిగి ఉన్నారు"</string>
-    <string name="user_add_profile_item_summary" msgid="5418602404308968028">"మీరు మీ ఖాతా నుండి అనువర్తనాలకు మరియు కంటెంట్‌కు ప్రాప్యతను పరిమితం చేయవచ్చు"</string>
+    <string name="user_add_user_item_summary" msgid="5748424612724703400">"వినియోగదారులు వారి స్వంత యాప్‌లను మరియు కంటెంట్‌ను కలిగి ఉన్నారు"</string>
+    <string name="user_add_profile_item_summary" msgid="5418602404308968028">"మీరు మీ ఖాతా నుండి యాప్‌లకు మరియు కంటెంట్‌కు యాక్సెస్‌ను పరిమితం చేయవచ్చు"</string>
     <string name="user_add_user_item_title" msgid="2394272381086965029">"యూజర్"</string>
     <string name="user_add_profile_item_title" msgid="3111051717414643029">"పరిమితం చేయబడిన ప్రొఫైల్"</string>
     <string name="user_add_user_title" msgid="5457079143694924885">"కొత్త వినియోగదారుని జోడించాలా?"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java b/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java
similarity index 76%
rename from packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
rename to packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java
index 877dd2d..2e7cfcb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationAccesses.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 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.
@@ -14,7 +14,8 @@
  * limitations under the License.
  */
 
-package com.android.settingslib.location;
+package com.android.settingslib.applications;
+
 
 import android.app.AppOpsManager;
 import android.content.Context;
@@ -39,14 +40,24 @@
 import java.util.List;
 
 /**
- * Retrieves the information of applications which accessed location recently.
+ * Retrieval of app ops information for the specified ops.
  */
-public class RecentLocationAccesses {
-    private static final String TAG = RecentLocationAccesses.class.getSimpleName();
+public class RecentAppOpsAccess {
     @VisibleForTesting
-    static final String ANDROID_SYSTEM_PACKAGE_NAME = "android";
+    static final int[] LOCATION_OPS = new int[]{
+            AppOpsManager.OP_FINE_LOCATION,
+            AppOpsManager.OP_COARSE_LOCATION,
+    };
+    private static final int[] MICROPHONE_OPS = new int[]{
+            AppOpsManager.OP_RECORD_AUDIO,
+    };
 
-    // Keep last 24 hours of location app information.
+
+    private static final String TAG = RecentAppOpsAccess.class.getSimpleName();
+    @VisibleForTesting
+    public static final String ANDROID_SYSTEM_PACKAGE_NAME = "android";
+
+    // Keep last 24 hours of access app information.
     private static final long RECENT_TIME_INTERVAL_MILLIS = DateUtils.DAY_IN_MILLIS;
 
     /** The flags for querying ops that are trusted for showing in the UI. */
@@ -54,47 +65,55 @@
             | AppOpsManager.OP_FLAG_UNTRUSTED_PROXY
             | AppOpsManager.OP_FLAG_TRUSTED_PROXIED;
 
-    @VisibleForTesting
-    static final int[] LOCATION_OPS = new int[]{
-            AppOpsManager.OP_FINE_LOCATION,
-            AppOpsManager.OP_COARSE_LOCATION,
-    };
-
     private final PackageManager mPackageManager;
     private final Context mContext;
+    private final int[] mOps;
     private final IconDrawableFactory mDrawableFactory;
     private final Clock mClock;
 
-    public RecentLocationAccesses(Context context) {
-        this(context, Clock.systemDefaultZone());
+    public RecentAppOpsAccess(Context context, int[] ops) {
+        this(context, Clock.systemDefaultZone(), ops);
     }
 
     @VisibleForTesting
-    RecentLocationAccesses(Context context, Clock clock) {
+    RecentAppOpsAccess(Context context, Clock clock, int[] ops) {
         mContext = context;
         mPackageManager = context.getPackageManager();
+        mOps = ops;
         mDrawableFactory = IconDrawableFactory.newInstance(context);
         mClock = clock;
     }
 
     /**
-     * Fills a list of applications which queried location recently within specified time.
-     * Apps are sorted by recency. Apps with more recent location accesses are in the front.
+     * Creates an instance of {@link RecentAppOpsAccess} for location (coarse and fine) access.
+     */
+    public static RecentAppOpsAccess createForLocation(Context context) {
+        return new RecentAppOpsAccess(context, LOCATION_OPS);
+    }
+
+    /**
+     * Creates an instance of {@link RecentAppOpsAccess} for microphone access.
+     */
+    public static RecentAppOpsAccess createForMicrophone(Context context) {
+        return new RecentAppOpsAccess(context, MICROPHONE_OPS);
+    }
+
+    /**
+     * Fills a list of applications which queried for access recently within specified time.
+     * Apps are sorted by recency. Apps with more recent accesses are in the front.
      */
     @VisibleForTesting
-    List<Access> getAppList(boolean showSystemApps) {
-        // Retrieve a location usage list from AppOps
-        PackageManager pm = mContext.getPackageManager();
-        AppOpsManager aoManager =
-                (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
-        List<AppOpsManager.PackageOps> appOps = aoManager.getPackagesForOps(LOCATION_OPS);
+    public List<Access> getAppList(boolean showSystemApps) {
+        // Retrieve a access usage list from AppOps
+        AppOpsManager aoManager = mContext.getSystemService(AppOpsManager.class);
+        List<AppOpsManager.PackageOps> appOps = aoManager.getPackagesForOps(mOps);
 
         final int appOpsCount = appOps != null ? appOps.size() : 0;
 
         // Process the AppOps list and generate a preference list.
         ArrayList<Access> accesses = new ArrayList<>(appOpsCount);
         final long now = mClock.millis();
-        final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        final UserManager um = mContext.getSystemService(UserManager.class);
         final List<UserHandle> profiles = um.getUserProfiles();
 
         for (int i = 0; i < appOpsCount; ++i) {
@@ -111,9 +130,10 @@
             // Don't show apps that do not have user sensitive location permissions
             boolean showApp = true;
             if (!showSystemApps) {
-                for (int op : LOCATION_OPS) {
+                for (int op : mOps) {
                     final String permission = AppOpsManager.opToPermission(op);
-                    final int permissionFlags = pm.getPermissionFlags(permission, packageName,
+                    final int permissionFlags = mPackageManager.getPermissionFlags(permission,
+                            packageName,
                             user);
                     if (PermissionChecker.checkPermissionForPreflight(mContext, permission,
                             PermissionChecker.PID_UNKNOWN, uid, packageName)
@@ -144,12 +164,11 @@
         return accesses;
     }
 
-
     /**
-     * Gets a list of apps that accessed location recently, sorting by recency.
+     * Gets a list of apps that accessed the app op recently, sorting by recency.
      *
      * @param showSystemApps whether includes system apps in the list.
-     * @return the list of apps that recently accessed location.
+     * @return the list of apps that recently accessed the app op.
      */
     public List<Access> getAppListSorted(boolean showSystemApps) {
         List<Access> accesses = getAppList(showSystemApps);
@@ -174,18 +193,18 @@
             AppOpsManager.PackageOps ops) {
         String packageName = ops.getPackageName();
         List<AppOpsManager.OpEntry> entries = ops.getOps();
-        long locationAccessFinishTime = 0L;
-        // Earliest time for a location access to end and still be shown in list.
-        long recentLocationCutoffTime = now - RECENT_TIME_INTERVAL_MILLIS;
+        long accessFinishTime = 0L;
+        // Earliest time for a access to end and still be shown in list.
+        long recentAccessCutoffTime = now - RECENT_TIME_INTERVAL_MILLIS;
         // Compute the most recent access time from all op entries.
         for (AppOpsManager.OpEntry entry : entries) {
             long lastAccessTime = entry.getLastAccessTime(TRUSTED_STATE_FLAGS);
-            if (lastAccessTime > locationAccessFinishTime) {
-                locationAccessFinishTime = lastAccessTime;
+            if (lastAccessTime > accessFinishTime) {
+                accessFinishTime = lastAccessTime;
             }
         }
         // Bail out if the entry is out of date.
-        if (locationAccessFinishTime < recentLocationCutoffTime) {
+        if (accessFinishTime < recentAccessCutoffTime) {
             return null;
         }
 
@@ -213,13 +232,16 @@
                 badgedAppLabel = null;
             }
             access = new Access(packageName, userHandle, icon, appLabel, badgedAppLabel,
-                    locationAccessFinishTime);
+                    accessFinishTime);
         } catch (NameNotFoundException e) {
             Log.w(TAG, "package name not found for " + packageName + ", userId " + userId);
         }
         return access;
     }
 
+    /**
+     * Information about when an app last accessed a particular app op.
+     */
     public static class Access {
         public final String packageName;
         public final UserHandle userHandle;
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
index bd9e0d3..7275d6b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
@@ -18,6 +18,9 @@
 
 import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
 
+import static com.android.settingslib.enterprise.ActionDisabledLearnMoreButtonLauncher.DEFAULT_RESOLVE_ACTIVITY_CHECKER;
+import static com.android.settingslib.enterprise.ManagedDeviceActionDisabledByAdminController.DEFAULT_FOREGROUND_USER_CHECKER;
+
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.hardware.biometrics.BiometricAuthenticator;
@@ -43,7 +46,11 @@
         } else if (isFinancedDevice(context)) {
             return new FinancedDeviceActionDisabledByAdminController(stringProvider);
         } else {
-            return new ManagedDeviceActionDisabledByAdminController(stringProvider, userHandle);
+            return new ManagedDeviceActionDisabledByAdminController(
+                    stringProvider,
+                    userHandle,
+                    DEFAULT_FOREGROUND_USER_CHECKER,
+                    DEFAULT_RESOLVE_ACTIVITY_CHECKER);
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledLearnMoreButtonLauncher.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledLearnMoreButtonLauncher.java
index 4114879..f9d3aaf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledLearnMoreButtonLauncher.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledLearnMoreButtonLauncher.java
@@ -22,6 +22,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -34,6 +35,17 @@
  */
 public abstract class ActionDisabledLearnMoreButtonLauncher {
 
+    public static ResolveActivityChecker DEFAULT_RESOLVE_ACTIVITY_CHECKER =
+            (packageManager, url, userHandle) -> packageManager.resolveActivityAsUser(
+                    createLearnMoreIntent(url),
+                    PackageManager.MATCH_DEFAULT_ONLY,
+                    userHandle.getIdentifier()) != null;
+
+    interface ResolveActivityChecker {
+        boolean canResolveActivityAsUser(
+                PackageManager packageManager, String url, UserHandle userHandle);
+    }
+
     /**
      * Sets up a "learn more" button which shows a screen with device policy settings
      */
@@ -111,6 +123,14 @@
         finishSelf();
     }
 
+    protected final boolean canLaunchHelpPage(
+            PackageManager packageManager,
+            String url,
+            UserHandle userHandle,
+            ResolveActivityChecker resolveActivityChecker) {
+        return resolveActivityChecker.canResolveActivityAsUser(packageManager, url, userHandle);
+    }
+
     private void showAdminPolicies(Context context, EnforcedAdmin enforcedAdmin) {
         if (enforcedAdmin.component != null) {
             launchShowAdminPolicies(context, enforcedAdmin.user, enforcedAdmin.component);
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java
index 1472980..d205eaa 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java
@@ -64,9 +64,8 @@
         return (dialog, which) -> {
             Log.d(TAG, "Positive button clicked, component: " + enforcedAdmin.component);
             final Intent intent = new Intent(ACTION_LEARN_MORE)
-                    .setComponent(enforcedAdmin.component)
                     .putExtra(EXTRA_SETTING_KEY, EXTRA_SETTING_VALUE)
-                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    .setPackage(enforcedAdmin.component.getPackageName());
             context.startActivity(intent);
         };
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminController.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminController.java
index 93e811d..c2034f8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminController.java
@@ -20,13 +20,14 @@
 
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.text.TextUtils;
 
 import androidx.annotation.Nullable;
 
-import java.util.Objects;
+import com.android.settingslib.enterprise.ActionDisabledLearnMoreButtonLauncher.ResolveActivityChecker;
 
 
 /**
@@ -35,17 +36,37 @@
 final class ManagedDeviceActionDisabledByAdminController
         extends BaseActionDisabledByAdminController {
 
-    private final UserHandle mUserHandle;
+    interface ForegroundUserChecker {
+        boolean isUserForeground(Context context, UserHandle userHandle);
+    }
+
+    public final static ForegroundUserChecker DEFAULT_FOREGROUND_USER_CHECKER =
+            ManagedDeviceActionDisabledByAdminController::isUserForeground;
+
+    /**
+     * The {@link UserHandle} which is preferred for launching the web help page in
+     * <p>If not able to launch the web help page in this user, the current user will be used as
+     * fallback instead. If the current user cannot open it either, the admin policies page will
+     * be used instead.
+     */
+    private final UserHandle mPreferredUserHandle;
+
+    private final ForegroundUserChecker mForegroundUserChecker;
+    private final ResolveActivityChecker mResolveActivityChecker;
 
     /**
      * Constructs a {@link ManagedDeviceActionDisabledByAdminController}
-     * @param userHandle - user on which to launch the help web page, if necessary
+     * @param preferredUserHandle - user on which to launch the help web page, if necessary
      */
     ManagedDeviceActionDisabledByAdminController(
             DeviceAdminStringProvider stringProvider,
-            UserHandle userHandle) {
+            UserHandle preferredUserHandle,
+            ForegroundUserChecker foregroundUserChecker,
+            ResolveActivityChecker resolveActivityChecker) {
         super(stringProvider);
-        mUserHandle = requireNonNull(userHandle);
+        mPreferredUserHandle = requireNonNull(preferredUserHandle);
+        mForegroundUserChecker = requireNonNull(foregroundUserChecker);
+        mResolveActivityChecker = requireNonNull(resolveActivityChecker);
     }
 
     @Override
@@ -53,14 +74,52 @@
         assertInitialized();
 
         String url = mStringProvider.getLearnMoreHelpPageUrl();
-        if (TextUtils.isEmpty(url)) {
+
+        if (!TextUtils.isEmpty(url)
+                && canLaunchHelpPageInPreferredOrCurrentUser(context, url, mPreferredUserHandle)) {
+            setupLearnMoreButtonToLaunchHelpPage(context, url, mPreferredUserHandle);
+        } else {
             mLauncher.setupLearnMoreButtonToShowAdminPolicies(context, mEnforcementAdminUserId,
                     mEnforcedAdmin);
-        } else {
-            mLauncher.setupLearnMoreButtonToLaunchHelpPage(context, url, mUserHandle);
         }
     }
 
+    private boolean canLaunchHelpPageInPreferredOrCurrentUser(
+            Context context, String url, UserHandle preferredUserHandle) {
+        PackageManager packageManager = context.getPackageManager();
+        if (mLauncher.canLaunchHelpPage(
+                packageManager, url, preferredUserHandle, mResolveActivityChecker)
+                && mForegroundUserChecker.isUserForeground(context, preferredUserHandle)) {
+            return true;
+        }
+        return mLauncher.canLaunchHelpPage(
+                packageManager, url, context.getUser(), mResolveActivityChecker);
+    }
+
+    /**
+     * Sets up the "Learn more" button to launch the web help page in the {@code
+     * preferredUserHandle} user. If not possible to launch it there, it sets up the button to
+     * launch it in the current user instead.
+     */
+    private void setupLearnMoreButtonToLaunchHelpPage(
+            Context context, String url, UserHandle preferredUserHandle) {
+        PackageManager packageManager = context.getPackageManager();
+        if (mLauncher.canLaunchHelpPage(
+                packageManager, url, preferredUserHandle, mResolveActivityChecker)
+                && mForegroundUserChecker.isUserForeground(context, preferredUserHandle)) {
+            mLauncher.setupLearnMoreButtonToLaunchHelpPage(context, url, preferredUserHandle);
+        }
+        if (mLauncher.canLaunchHelpPage(
+                packageManager, url, context.getUser(), mResolveActivityChecker)) {
+            mLauncher.setupLearnMoreButtonToLaunchHelpPage(context, url, context.getUser());
+        }
+    }
+
+    private static boolean isUserForeground(Context context, UserHandle userHandle) {
+        return context.createContextAsUser(userHandle, /* flags= */ 0)
+                .getSystemService(UserManager.class).isUserForeground();
+    }
+
     @Override
     public String getAdminSupportTitle(@Nullable String restriction) {
         if (restriction == null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 79446e4..3c43f4a6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -15,7 +15,6 @@
  */
 package com.android.settingslib.media;
 
-import static android.media.MediaRoute2Info.FEATURE_REMOTE_GROUP_PLAYBACK;
 import static android.media.MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
 import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
 import static android.media.MediaRoute2Info.TYPE_DOCK;
@@ -44,6 +43,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import androidx.annotation.RequiresApi;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -56,6 +57,7 @@
 /**
  * InfoMediaManager provide interface to get InfoMediaDevice list.
  */
+@RequiresApi(Build.VERSION_CODES.R)
 public class InfoMediaManager extends MediaManager {
 
     private static final String TAG = "InfoMediaManager";
@@ -145,9 +147,16 @@
     }
 
     private RoutingSessionInfo getRoutingSessionInfo() {
-        final List<RoutingSessionInfo> sessionInfos =
-                mRouterManager.getRoutingSessions(mPackageName);
+        return getRoutingSessionInfo(mPackageName);
+    }
 
+    private RoutingSessionInfo getRoutingSessionInfo(String packageName) {
+        final List<RoutingSessionInfo> sessionInfos =
+                mRouterManager.getRoutingSessions(packageName);
+
+        if (sessionInfos == null || sessionInfos.isEmpty()) {
+            return null;
+        }
         return sessionInfos.get(sessionInfos.size() - 1);
     }
 
@@ -367,65 +376,18 @@
     }
 
     boolean shouldDisableMediaOutput(String packageName) {
-        boolean shouldDisableMediaOutput = false;
         if (TextUtils.isEmpty(packageName)) {
             Log.w(TAG, "shouldDisableMediaOutput() package name is null or empty!");
-            return false;
+            return true;
         }
-        final List<MediaRoute2Info> infos = mRouterManager.getTransferableRoutes(packageName);
-        if (infos.size() == 1) {
-            final MediaRoute2Info info = infos.get(0);
-            final int deviceType = info.getType();
-            switch (deviceType) {
-                case TYPE_UNKNOWN:
-                case TYPE_REMOTE_TV:
-                case TYPE_REMOTE_SPEAKER:
-                case TYPE_GROUP:
-                    shouldDisableMediaOutput = true;
-                    break;
-                default:
-                    shouldDisableMediaOutput = false;
-                    break;
-            }
-        }
-        if (DEBUG) {
-            Log.d(TAG, "shouldDisableMediaOutput() MediaRoute2Info size : " + infos.size()
-                    + ", package name : " + packageName + ", shouldDisableMediaOutput : "
-                    + shouldDisableMediaOutput);
-        }
-        return shouldDisableMediaOutput;
+
+        // Disable when there is no transferable route
+        return mRouterManager.getTransferableRoutes(packageName).isEmpty();
     }
 
     @TargetApi(Build.VERSION_CODES.R)
     boolean shouldEnableVolumeSeekBar(RoutingSessionInfo sessionInfo) {
-        if (sessionInfo == null) {
-            Log.w(TAG, "shouldEnableVolumeSeekBar() package name is null or empty!");
-            return false;
-        }
-        final List<MediaRoute2Info> mediaRoute2Infos =
-                mRouterManager.getSelectedRoutes(sessionInfo);
-        // More than one selected route
-        if (mediaRoute2Infos.size() > 1) {
-            if (DEBUG) {
-                Log.d(TAG, "shouldEnableVolumeSeekBar() package name : "
-                        + sessionInfo.getClientPackageName()
-                        + ", mediaRoute2Infos.size() " + mediaRoute2Infos.size());
-            }
-            return false;
-        }
-        // Route contains group feature
-        for (MediaRoute2Info mediaRoute2Info : mediaRoute2Infos) {
-            final List<String> features = mediaRoute2Info.getFeatures();
-            if (features.contains(FEATURE_REMOTE_GROUP_PLAYBACK)) {
-                if (DEBUG) {
-                    Log.d(TAG, "shouldEnableVolumeSeekBar() package name : "
-                            + mediaRoute2Info.getClientPackageName()
-                            + "contain group playback ");
-                }
-                return false;
-            }
-        }
-        return true;
+        return false;
     }
 
     private void refreshDevices() {
@@ -456,7 +418,7 @@
     }
 
     private void buildAvailableRoutes() {
-        for (MediaRoute2Info route : mRouterManager.getTransferableRoutes(mPackageName)) {
+        for (MediaRoute2Info route : getAvailableRoutes(mPackageName)) {
             if (DEBUG) {
                 Log.d(TAG, "buildAvailableRoutes() route : " + route.getName() + ", volume : "
                         + route.getVolume() + ", type : " + route.getType());
@@ -465,6 +427,29 @@
         }
     }
 
+    private List<MediaRoute2Info> getAvailableRoutes(String packageName) {
+        final List<MediaRoute2Info> infos = new ArrayList<>();
+        RoutingSessionInfo routingSessionInfo = getRoutingSessionInfo(packageName);
+        if (routingSessionInfo != null) {
+            infos.addAll(mRouterManager.getSelectedRoutes(routingSessionInfo));
+        }
+        final List<MediaRoute2Info> transferableRoutes =
+                mRouterManager.getTransferableRoutes(packageName);
+        for (MediaRoute2Info transferableRoute : transferableRoutes) {
+            boolean alreadyAdded = false;
+            for (MediaRoute2Info mediaRoute2Info : infos) {
+                if (TextUtils.equals(transferableRoute.getId(), mediaRoute2Info.getId())) {
+                    alreadyAdded = true;
+                    break;
+                }
+            }
+            if (!alreadyAdded) {
+                infos.add(transferableRoute);
+            }
+        }
+        return infos;
+    }
+
     @VisibleForTesting
     void addMediaDevice(MediaRoute2Info route) {
         final int deviceType = route.getType();
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index a8da2c0..22001c9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -22,11 +22,13 @@
 import android.bluetooth.BluetoothDevice;
 import android.content.Context;
 import android.media.RoutingSessionInfo;
+import android.os.Build;
 import android.text.TextUtils;
 import android.util.Log;
 
 import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settingslib.bluetooth.A2dpProfile;
@@ -49,6 +51,7 @@
 /**
  * LocalMediaManager provide interface to get MediaDevice list and transfer media to MediaDevice.
  */
+@RequiresApi(Build.VERSION_CODES.R)
 public class LocalMediaManager implements BluetoothCallback {
     private static final Comparator<MediaDevice> COMPARATOR = Comparator.naturalOrder();
     private static final String TAG = "LocalMediaManager";
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 72fa25f..bf0dc7b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -157,7 +157,6 @@
     private Network mDefaultNetwork = null;
     private NetworkCapabilities mDefaultNetworkCapabilities = null;
     private final Runnable mCallback;
-    private final boolean mSupportMergedUi;
 
     private WifiInfo mWifiInfo;
     public boolean enabled;
@@ -181,7 +180,6 @@
         mNetworkScoreManager = networkScoreManager;
         mConnectivityManager = connectivityManager;
         mCallback = callback;
-        mSupportMergedUi = false;
     }
 
     public void setListening(boolean listening) {
@@ -223,10 +221,8 @@
                 } else {
                     ssid = getValidSsid(mWifiInfo);
                 }
-                if (mSupportMergedUi) {
-                    isCarrierMerged = mWifiInfo.isCarrierMerged();
-                    subId = mWifiInfo.getSubscriptionId();
-                }
+                isCarrierMerged = mWifiInfo.isCarrierMerged();
+                subId = mWifiInfo.getSubscriptionId();
                 updateRssi(mWifiInfo.getRssi());
                 maybeRequestNetworkScore();
             }
@@ -255,10 +251,8 @@
             } else {
                 ssid = getValidSsid(mWifiInfo);
             }
-            if (mSupportMergedUi) {
-                isCarrierMerged = mWifiInfo.isCarrierMerged();
-                subId = mWifiInfo.getSubscriptionId();
-            }
+            isCarrierMerged = mWifiInfo.isCarrierMerged();
+            subId = mWifiInfo.getSubscriptionId();
             updateRssi(mWifiInfo.getRssi());
             maybeRequestNetworkScore();
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
index 6100615..56454e9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
@@ -20,10 +20,13 @@
 import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.getMaxNetworkSelectionDisableReason;
 
 import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
 import android.net.wifi.WifiInfo;
+import android.os.Bundle;
 import android.os.SystemClock;
 
 import androidx.annotation.VisibleForTesting;
@@ -36,6 +39,23 @@
 
     private static final int INVALID_RSSI = -127;
 
+    /**
+     * The intent action shows network details settings to allow configuration of Wi-Fi.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * Input: The calling package should put the chosen
+     * com.android.wifitrackerlib.WifiEntry#getKey() to a string extra in the request bundle into
+     * the {@link #KEY_CHOSEN_WIFIENTRY_KEY}.
+     * <p>
+     * Output: Nothing.
+     */
+    public static final String ACTION_WIFI_DETAILS_SETTINGS =
+            "android.settings.WIFI_DETAILS_SETTINGS";
+    public static final String KEY_CHOSEN_WIFIENTRY_KEY = "key_chosen_wifientry_key";
+    public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
+
     static final int[] WIFI_PIE = {
             com.android.internal.R.drawable.ic_wifi_signal_0,
             com.android.internal.R.drawable.ic_wifi_signal_1,
@@ -275,7 +295,42 @@
         return noInternet ? NO_INTERNET_WIFI_PIE[level] : WIFI_PIE[level];
     }
 
+    /**
+     * Wrapper the {@link #getInternetIconResource} for testing compatibility.
+     */
+    public static class InternetIconInjector {
+
+        protected final Context mContext;
+
+        public InternetIconInjector(Context context) {
+            mContext = context;
+        }
+
+        /**
+         * Returns the Internet icon for a given RSSI level.
+         *
+         * @param noInternet True if a connected Wi-Fi network cannot access the Internet
+         * @param level The number of bars to show (0-4)
+         */
+        public Drawable getIcon(boolean noInternet, int level) {
+            return mContext.getDrawable(WifiUtils.getInternetIconResource(level, noInternet));
+        }
+    }
+
     public static boolean isMeteredOverridden(WifiConfiguration config) {
         return config.meteredOverride != WifiConfiguration.METERED_OVERRIDE_NONE;
     }
+
+    /**
+     * Returns the Intent for Wi-Fi network details settings.
+     *
+     * @param key The Wi-Fi entry key
+     */
+    public static Intent getWifiDetailsSettingsIntent(String key) {
+        final Intent intent = new Intent(ACTION_WIFI_DETAILS_SETTINGS);
+        final Bundle bundle = new Bundle();
+        bundle.putString(KEY_CHOSEN_WIFIENTRY_KEY, key);
+        intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, bundle);
+        return intent;
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/RecentAppOpsAccessesTest.java
similarity index 72%
rename from packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java
rename to packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/RecentAppOpsAccessesTest.java
index 16d73a3..cb62a73 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/location/RecentLocationAccessesTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/RecentAppOpsAccessesTest.java
@@ -1,15 +1,34 @@
-package com.android.settingslib.location;
+/*
+ * 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.settingslib.applications;
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.isA;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
+import android.Manifest;
 import android.app.AppOpsManager;
 import android.app.AppOpsManager.OpEntry;
 import android.app.AppOpsManager.PackageOps;
 import android.content.Context;
+import android.content.PermissionChecker;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -19,13 +38,14 @@
 import android.util.LongSparseArray;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowPermissionChecker;
 
 import java.time.Clock;
 import java.util.ArrayList;
@@ -34,7 +54,8 @@
 import java.util.concurrent.TimeUnit;
 
 @RunWith(RobolectricTestRunner.class)
-public class RecentLocationAccessesTest {
+@Config(shadows = {ShadowPermissionChecker.class})
+public class RecentAppOpsAccessesTest {
 
     private static final int TEST_UID = 1234;
     private static final long NOW = 1_000_000_000;  // Approximately 9/8/2001
@@ -54,7 +75,7 @@
     private Clock mClock;
     private Context mContext;
     private int mTestUserId;
-    private RecentLocationAccesses mRecentLocationAccesses;
+    private RecentAppOpsAccess mRecentAppOpsAccess;
 
     @Before
     public void setUp() throws NameNotFoundException {
@@ -69,24 +90,37 @@
                 .thenReturn("testApplicationLabel");
         when(mPackageManager.getUserBadgedLabel(isA(CharSequence.class), isA(UserHandle.class)))
                 .thenReturn("testUserBadgedLabel");
+        when(mPackageManager.getPermissionFlags(any(), any(), any()))
+                .thenReturn(PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED
+                        | PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED);
+        for (String testPackageName : TEST_PACKAGE_NAMES) {
+            ShadowPermissionChecker.setResult(
+                    testPackageName,
+                    Manifest.permission.ACCESS_COARSE_LOCATION,
+                    PermissionChecker.PERMISSION_GRANTED);
+            ShadowPermissionChecker.setResult(
+                    testPackageName,
+                    Manifest.permission.ACCESS_FINE_LOCATION,
+                    PermissionChecker.PERMISSION_GRANTED);
+        }
         mTestUserId = UserHandle.getUserId(TEST_UID);
         when(mUserManager.getUserProfiles())
                 .thenReturn(Collections.singletonList(new UserHandle(mTestUserId)));
 
         long[] testRequestTime = {ONE_MIN_AGO, TWENTY_THREE_HOURS_AGO, TWO_DAYS_AGO};
         List<PackageOps> appOps = createTestPackageOpsList(TEST_PACKAGE_NAMES, testRequestTime);
-        when(mAppOpsManager.getPackagesForOps(RecentLocationAccesses.LOCATION_OPS)).thenReturn(
+        when(mAppOpsManager.getPackagesForOps(RecentAppOpsAccess.LOCATION_OPS)).thenReturn(
                 appOps);
         mockTestApplicationInfos(mTestUserId, TEST_PACKAGE_NAMES);
 
         when(mClock.millis()).thenReturn(NOW);
-        mRecentLocationAccesses = new RecentLocationAccesses(mContext, mClock);
+        mRecentAppOpsAccess = new RecentAppOpsAccess(mContext, mClock,
+                RecentAppOpsAccess.LOCATION_OPS);
     }
 
     @Test
-    @Ignore
     public void testGetAppList_shouldFilterRecentAccesses() {
-        List<RecentLocationAccesses.Access> requests = mRecentLocationAccesses.getAppList(false);
+        List<RecentAppOpsAccess.Access> requests = mRecentAppOpsAccess.getAppList(false);
         // Only two of the apps have requested location within 15 min.
         assertThat(requests).hasSize(2);
         // Make sure apps are ordered by recency
@@ -97,12 +131,11 @@
     }
 
     @Test
-    @Ignore
     public void testGetAppList_shouldNotShowAndroidOS() throws NameNotFoundException {
         // Add android OS to the list of apps.
         PackageOps androidSystemPackageOps =
                 createPackageOps(
-                        RecentLocationAccesses.ANDROID_SYSTEM_PACKAGE_NAME,
+                        RecentAppOpsAccess.ANDROID_SYSTEM_PACKAGE_NAME,
                         Process.SYSTEM_UID,
                         AppOpsManager.OP_FINE_LOCATION,
                         ONE_MIN_AGO);
@@ -110,12 +143,12 @@
                 {ONE_MIN_AGO, TWENTY_THREE_HOURS_AGO, TWO_DAYS_AGO, ONE_MIN_AGO};
         List<PackageOps> appOps = createTestPackageOpsList(TEST_PACKAGE_NAMES, testRequestTime);
         appOps.add(androidSystemPackageOps);
-        when(mAppOpsManager.getPackagesForOps(RecentLocationAccesses.LOCATION_OPS)).thenReturn(
+        when(mAppOpsManager.getPackagesForOps(RecentAppOpsAccess.LOCATION_OPS)).thenReturn(
                 appOps);
         mockTestApplicationInfos(
-                Process.SYSTEM_UID, RecentLocationAccesses.ANDROID_SYSTEM_PACKAGE_NAME);
+                Process.SYSTEM_UID, RecentAppOpsAccess.ANDROID_SYSTEM_PACKAGE_NAME);
 
-        List<RecentLocationAccesses.Access> requests = mRecentLocationAccesses.getAppList(true);
+        List<RecentAppOpsAccess.Access> requests = mRecentAppOpsAccess.getAppList(true);
         // Android OS shouldn't show up in the list of apps.
         assertThat(requests).hasSize(2);
         // Make sure apps are ordered by recency
@@ -159,7 +192,7 @@
         // Slot for background access timestamp.
         final LongSparseArray<AppOpsManager.NoteOpEvent> accessEvents = new LongSparseArray<>();
         accessEvents.put(AppOpsManager.makeKey(AppOpsManager.UID_STATE_BACKGROUND,
-            AppOpsManager.OP_FLAG_SELF), new AppOpsManager.NoteOpEvent(time, -1, null));
+                AppOpsManager.OP_FLAG_SELF), new AppOpsManager.NoteOpEvent(time, -1, null));
 
         return new OpEntry(op, AppOpsManager.MODE_ALLOWED, Collections.singletonMap(null,
                 new AppOpsManager.AttributedOpEntry(op, false, accessEvents, null)));
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminControllerTest.java
index d9be4f3..509e12d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminControllerTest.java
@@ -30,6 +30,8 @@
 
 import android.app.Activity;
 import android.content.Context;
+import android.content.pm.ResolveInfo;
+import android.os.UserHandle;
 import android.os.UserManager;
 
 import androidx.test.core.app.ApplicationProvider;
@@ -45,9 +47,11 @@
 @RunWith(RobolectricTestRunner.class)
 public class ManagedDeviceActionDisabledByAdminControllerTest {
 
+    private static UserHandle MANAGED_USER = UserHandle.of(123);
     private static final String RESTRICTION = UserManager.DISALLOW_ADJUST_VOLUME;
     private static final String EMPTY_URL = "";
     private static final String SUPPORT_TITLE_FOR_RESTRICTION = DISALLOW_ADJUST_VOLUME_TITLE;
+    public static final ResolveInfo TEST_RESULT_INFO = new ResolveInfo();
 
     private final Context mContext = ApplicationProvider.getApplicationContext();
     private final Activity mActivity = ActivityController.of(new Activity()).get();
@@ -60,8 +64,21 @@
     }
 
     @Test
-    public void setupLearnMoreButton_validUrl_negativeButtonSet() {
-        ManagedDeviceActionDisabledByAdminController controller = createController(URL);
+    public void setupLearnMoreButton_noUrl_negativeButtonSet() {
+        ManagedDeviceActionDisabledByAdminController controller = createController(EMPTY_URL);
+
+        controller.setupLearnMoreButton(mContext);
+
+        mTestUtils.assertLearnMoreAction(LEARN_MORE_ACTION_SHOW_ADMIN_POLICIES);
+    }
+
+    @Test
+    public void setupLearnMoreButton_validUrl_foregroundUser_launchesHelpPage() {
+        ManagedDeviceActionDisabledByAdminController controller = createController(
+                URL,
+                /* isUserForeground= */ true,
+                /* preferredUserHandle= */ MANAGED_USER,
+                /* userContainingBrowser= */ MANAGED_USER);
 
         controller.setupLearnMoreButton(mContext);
 
@@ -69,8 +86,38 @@
     }
 
     @Test
-    public void setupLearnMoreButton_noUrl_negativeButtonSet() {
-        ManagedDeviceActionDisabledByAdminController controller = createController(EMPTY_URL);
+    public void setupLearnMoreButton_validUrl_browserInPreferredUser_notForeground_showsAdminPolicies() {
+        ManagedDeviceActionDisabledByAdminController controller = createController(
+                URL,
+                /* isUserForeground= */ false,
+                /* preferredUserHandle= */ MANAGED_USER,
+                /* userContainingBrowser= */ MANAGED_USER);
+
+        controller.setupLearnMoreButton(mContext);
+
+        mTestUtils.assertLearnMoreAction(LEARN_MORE_ACTION_SHOW_ADMIN_POLICIES);
+    }
+
+    @Test
+    public void setupLearnMoreButton_validUrl_browserInCurrentUser_launchesHelpPage() {
+        ManagedDeviceActionDisabledByAdminController controller = createController(
+                URL,
+                /* isUserForeground= */ false,
+                /* preferredUserHandle= */ MANAGED_USER,
+                /* userContainingBrowser= */ mContext.getUser());
+
+        controller.setupLearnMoreButton(mContext);
+
+        mTestUtils.assertLearnMoreAction(LEARN_MORE_ACTION_LAUNCH_HELP_PAGE);
+    }
+
+    @Test
+    public void setupLearnMoreButton_validUrl_browserNotOnAnyUser_showsAdminPolicies() {
+        ManagedDeviceActionDisabledByAdminController controller = createController(
+                URL,
+                /* isUserForeground= */ false,
+                /* preferredUserHandle= */ MANAGED_USER,
+                /* userContainingBrowser= */ null);
 
         controller.setupLearnMoreButton(mContext);
 
@@ -110,13 +157,33 @@
     }
 
     private ManagedDeviceActionDisabledByAdminController createController() {
-        return createController(/* url= */ null);
+        return createController(
+                /* url= */ null,
+                /* foregroundUserChecker= */ true,
+                mContext.getUser(),
+                /* userContainingBrowser= */ null);
     }
 
     private ManagedDeviceActionDisabledByAdminController createController(String url) {
+        return createController(
+                url,
+                /* foregroundUserChecker= */ true,
+                mContext.getUser(),
+                /* userContainingBrowser= */ null);
+    }
+
+    private ManagedDeviceActionDisabledByAdminController createController(
+            String url,
+            boolean isUserForeground,
+            UserHandle preferredUserHandle,
+            UserHandle userContainingBrowser) {
         ManagedDeviceActionDisabledByAdminController controller =
                 new ManagedDeviceActionDisabledByAdminController(
-                        new FakeDeviceAdminStringProvider(url), mContext.getUser());
+                        new FakeDeviceAdminStringProvider(url),
+                        preferredUserHandle,
+                        /* foregroundUserChecker= */ (context, userHandle) -> isUserForeground,
+                        /* resolveActivityChecker= */ (packageManager, __, userHandle) ->
+                                userHandle.equals(userContainingBrowser));
         controller.initialize(mTestUtils.createLearnMoreButtonLauncher());
         controller.updateEnforcedAdmin(ENFORCED_ADMIN, ENFORCEMENT_ADMIN_USER_ID);
         return controller;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
index 89960cb..7c2b904 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
@@ -20,9 +20,12 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.content.Intent;
 import android.net.NetworkKey;
 import android.net.RssiCurve;
 import android.net.ScoredNetwork;
@@ -36,6 +39,8 @@
 import android.text.format.DateUtils;
 import android.util.ArraySet;
 
+import androidx.test.core.app.ApplicationProvider;
+
 import com.android.settingslib.R;
 
 import org.junit.Before;
@@ -44,7 +49,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
 
 import java.util.ArrayList;
 import java.util.Set;
@@ -69,7 +73,7 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mContext = RuntimeEnvironment.application;
+        mContext = spy(ApplicationProvider.getApplicationContext());
     }
 
     @Test
@@ -148,6 +152,32 @@
         assertThat(WifiUtils.isMeteredOverridden(mWifiConfig)).isTrue();
     }
 
+    @Test
+    public void getWifiDetailsSettingsIntent_returnsCorrectValues() {
+        final String key = "test_key";
+
+        final Intent intent = WifiUtils.getWifiDetailsSettingsIntent(key);
+
+        assertThat(intent.getAction()).isEqualTo(WifiUtils.ACTION_WIFI_DETAILS_SETTINGS);
+        final Bundle bundle = intent.getBundleExtra(WifiUtils.EXTRA_SHOW_FRAGMENT_ARGUMENTS);
+        assertThat(bundle.getString(WifiUtils.KEY_CHOSEN_WIFIENTRY_KEY)).isEqualTo(key);
+    }
+
+    @Test
+    public void testInternetIconInjector_getIcon_returnsCorrectValues() {
+        WifiUtils.InternetIconInjector iconInjector = new WifiUtils.InternetIconInjector(mContext);
+
+        for (int level = 0; level <= 4; level++) {
+            iconInjector.getIcon(false /* noInternet */, level);
+            verify(mContext).getDrawable(
+                    WifiUtils.getInternetIconResource(level, false /* noInternet */));
+
+            iconInjector.getIcon(true /* noInternet */, level);
+            verify(mContext).getDrawable(
+                    WifiUtils.getInternetIconResource(level, true /* noInternet */));
+        }
+    }
+
     private static ArrayList<ScanResult> buildScanResultCache() {
         ArrayList<ScanResult> scanResults = new ArrayList<>();
         for (int i = 0; i < 5; i++) {
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index 01ae1e9..db33c3f 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -78,5 +78,6 @@
         Settings.Global.POWER_BUTTON_LONG_PRESS,
         Settings.Global.AUTOMATIC_POWER_SAVE_MODE,
         Settings.Global.ADVANCED_BATTERY_USAGE_AMOUNT,
+        Settings.Global.POWER_BUTTON_LONG_PRESS_DURATION_MS
     };
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 6022608..96f127b 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -101,6 +101,7 @@
         Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED,
         Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED,
         Settings.Secure.QS_TILES,
+        Settings.Secure.QS_AUTO_ADDED_TILES,
         Settings.Secure.CONTROLS_ENABLED,
         Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT,
         Settings.Secure.DOZE_ENABLED,
@@ -118,7 +119,6 @@
         Settings.Secure.VR_DISPLAY_MODE,
         Settings.Secure.NOTIFICATION_BADGING,
         Settings.Secure.NOTIFICATION_DISMISS_RTL,
-        Settings.Secure.QS_AUTO_ADDED_TILES,
         Settings.Secure.SCREENSAVER_ENABLED,
         Settings.Secure.SCREENSAVER_COMPONENTS,
         Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 3c7d7a8..d0448ef 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -20,6 +20,7 @@
 import static android.provider.settings.validators.SettingsValidators.ANY_INTEGER_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.PACKAGE_NAME_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.PERCENTAGE_INTEGER_VALIDATOR;
 import static android.view.Display.HdrCapabilities.HDR_TYPES;
@@ -142,6 +143,7 @@
         VALIDATORS.put(Global.DEVICE_CONFIG_SYNC_DISABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.AUTOMATIC_POWER_SAVE_MODE, ANY_INTEGER_VALIDATOR);
         VALIDATORS.put(Global.ADVANCED_BATTERY_USAGE_AMOUNT, PERCENTAGE_INTEGER_VALIDATOR);
+        VALIDATORS.put(Global.POWER_BUTTON_LONG_PRESS_DURATION_MS, NONE_NEGATIVE_LONG_VALIDATOR);
     }
 }
 
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 7a9f81e0..7aeacdc 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -34,7 +34,9 @@
 import static android.provider.settings.validators.SettingsValidators.TTS_LIST_VALIDATOR;
 
 import android.provider.Settings.Secure;
+import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 
 import java.util.Map;
 
@@ -287,5 +289,32 @@
         VALIDATORS.put(Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.NOTIFICATION_BUBBLES, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.DEVICE_STATE_ROTATION_LOCK, value -> {
+            if (TextUtils.isEmpty(value)) {
+                return true;
+            }
+            String[] intValues = value.split(":");
+            if (intValues.length % 2 != 0) {
+                return false;
+            }
+            InclusiveIntegerRangeValidator enumValidator =
+                    new InclusiveIntegerRangeValidator(
+                            Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED,
+                            Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
+            ArraySet<String> keys = new ArraySet<>();
+            for (int i = 0; i < intValues.length - 1; ) {
+                String entryKey = intValues[i++];
+                String entryValue = intValues[i++];
+                if (!NON_NEGATIVE_INTEGER_VALIDATOR.validate(entryKey)
+                        || !enumValidator.validate(entryValue)) {
+                    return false;
+                }
+                // If the same device state key was specified more than once, this is invalid
+                if (!keys.add(entryKey)) {
+                    return false;
+                }
+            }
+            return true;
+        });
     }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index c577868..6cfcb51 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -72,8 +72,9 @@
      * {@hide}
      */
     private static final ArraySet<String> sBroadcastOnRestore;
+    private static final ArraySet<String> sBroadcastOnRestoreSystemUI;
     static {
-        sBroadcastOnRestore = new ArraySet<String>(4);
+        sBroadcastOnRestore = new ArraySet<String>(9);
         sBroadcastOnRestore.add(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
         sBroadcastOnRestore.add(Settings.Secure.ENABLED_VR_LISTENERS);
         sBroadcastOnRestore.add(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
@@ -83,6 +84,9 @@
         sBroadcastOnRestore.add(Settings.Secure.DARK_THEME_CUSTOM_END_TIME);
         sBroadcastOnRestore.add(Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
         sBroadcastOnRestore.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
+        sBroadcastOnRestoreSystemUI = new ArraySet<String>(2);
+        sBroadcastOnRestoreSystemUI.add(Settings.Secure.QS_TILES);
+        sBroadcastOnRestoreSystemUI.add(Settings.Secure.QS_AUTO_ADDED_TILES);
     }
 
     private interface SettingsLookup {
@@ -133,6 +137,7 @@
         // Will we need a post-restore broadcast for this element?
         String oldValue = null;
         boolean sendBroadcast = false;
+        boolean sendBroadcastSystemUI = false;
         final SettingsLookup table;
 
         if (destination.equals(Settings.Secure.CONTENT_URI)) {
@@ -143,10 +148,12 @@
             table = sGlobalLookup;
         }
 
-        if (sBroadcastOnRestore.contains(name)) {
+        sendBroadcast = sBroadcastOnRestore.contains(name);
+        sendBroadcastSystemUI = sBroadcastOnRestoreSystemUI.contains(name);
+
+        if (sendBroadcast || sendBroadcastSystemUI) {
             // TODO: http://b/22388012
             oldValue = table.lookup(cr, name, UserHandle.USER_SYSTEM);
-            sendBroadcast = true;
         }
 
         try {
@@ -193,18 +200,28 @@
         } catch (Exception e) {
             // If we fail to apply the setting, by definition nothing happened
             sendBroadcast = false;
+            sendBroadcastSystemUI = false;
         } finally {
             // If this was an element of interest, send the "we just restored it"
             // broadcast with the historical value now that the new value has
             // been committed and observers kicked off.
-            if (sendBroadcast) {
+            if (sendBroadcast || sendBroadcastSystemUI) {
                 Intent intent = new Intent(Intent.ACTION_SETTING_RESTORED)
-                        .setPackage("android").addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
+                        .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
                         .putExtra(Intent.EXTRA_SETTING_NAME, name)
                         .putExtra(Intent.EXTRA_SETTING_NEW_VALUE, value)
                         .putExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE, oldValue)
                         .putExtra(Intent.EXTRA_SETTING_RESTORED_FROM_SDK_INT, restoredFromSdkInt);
-                context.sendBroadcastAsUser(intent, UserHandle.SYSTEM, null);
+
+                if (sendBroadcast) {
+                    intent.setPackage("android");
+                    context.sendBroadcastAsUser(intent, UserHandle.SYSTEM, null);
+                }
+                if (sendBroadcastSystemUI) {
+                    intent.setPackage(
+                            context.getString(com.android.internal.R.string.config_systemUi));
+                    context.sendBroadcastAsUser(intent, UserHandle.SYSTEM, null);
+                }
             }
         }
     }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 073b4d0..20735cb 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -763,9 +763,6 @@
                 Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES,
                 GlobalSettingsProto.Gpu.ANGLE_GL_DRIVER_SELECTION_VALUES);
         dumpSetting(s, p,
-                Settings.Global.ANGLE_ALLOWLIST,
-                GlobalSettingsProto.Gpu.ANGLE_ALLOWLIST);
-        dumpSetting(s, p,
                 Settings.Global.ANGLE_EGL_FEATURES,
                 GlobalSettingsProto.Gpu.ANGLE_EGL_FEATURES);
         dumpSetting(s, p,
@@ -1195,6 +1192,9 @@
         dumpSetting(s, p,
                 Settings.Global.POWER_MANAGER_CONSTANTS,
                 GlobalSettingsProto.POWER_MANAGER_CONSTANTS);
+        dumpSetting(s, p,
+                Settings.Global.POWER_BUTTON_LONG_PRESS_DURATION_MS,
+                GlobalSettingsProto.POWER_BUTTON_LONG_PRESS_DURATION_MS);
 
         final long prepaidSetupToken = p.start(GlobalSettingsProto.PREPAID_SETUP);
         dumpSetting(s, p,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 1e3ee22..db301f6 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3585,7 +3585,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 203;
+            private static final int SETTINGS_VERSION = 204;
 
             private final int mUserId;
 
@@ -5189,6 +5189,44 @@
                     currentVersion = 203;
                 }
 
+                if (currentVersion == 203) {
+                    // Version 204: Replace 'wifi' or 'cell' tiles with 'internet' if existed.
+                    final SettingsState secureSettings = getSecureSettingsLocked(userId);
+                    final Setting currentValue = secureSettings.getSettingLocked(Secure.QS_TILES);
+                    if (!currentValue.isNull()) {
+                        String tileList = currentValue.getValue();
+                        String[] tileSplit = tileList.split(",");
+                        final ArrayList<String> tiles = new ArrayList<String>();
+                        boolean hasInternetTile = false;
+                        for (int i = 0; i < tileSplit.length; i++) {
+                            String tile = tileSplit[i].trim();
+                            if (tile.isEmpty()) continue;
+                            tiles.add(tile);
+                            if (tile.equals("internet")) hasInternetTile = true;
+                        }
+                        if (!hasInternetTile) {
+                            if (tiles.contains("wifi")) {
+                                // Replace the WiFi with Internet, and remove the Cell
+                                tiles.set(tiles.indexOf("wifi"), "internet");
+                                tiles.remove("cell");
+                            } else if (tiles.contains("cell")) {
+                                // Replace the Cell with Internet
+                                tiles.set(tiles.indexOf("cell"), "internet");
+                            }
+                        } else {
+                            tiles.remove("wifi");
+                            tiles.remove("cell");
+                        }
+                        secureSettings.insertSettingOverrideableByRestoreLocked(
+                                Secure.QS_TILES,
+                                TextUtils.join(",", tiles),
+                                null /* tag */,
+                                true /* makeDefault */,
+                                SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
+                    currentVersion = 204;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 if (currentVersion != newVersion) {
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 9362a18..06cadaf3 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -508,7 +508,6 @@
                     Settings.Global.ANGLE_GL_DRIVER_ALL_ANGLE,
                     Settings.Global.ANGLE_GL_DRIVER_SELECTION_PKGS,
                     Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES,
-                    Settings.Global.ANGLE_ALLOWLIST,
                     Settings.Global.ANGLE_EGL_FEATURES,
                     Settings.Global.UPDATABLE_DRIVER_ALL_APPS,
                     Settings.Global.UPDATABLE_DRIVER_PRODUCTION_OPT_IN_APPS,
diff --git a/packages/Shell/res/values-te/strings.xml b/packages/Shell/res/values-te/strings.xml
index 989b53e..50b5c85 100644
--- a/packages/Shell/res/values-te/strings.xml
+++ b/packages/Shell/res/values-te/strings.xml
@@ -18,30 +18,30 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"షెల్"</string>
     <string name="bugreport_notification_channel" msgid="2574150205913861141">"బగ్ రిపోర్ట్స్"</string>
-    <string name="bugreport_in_progress_title" msgid="4311705936714972757">"బగ్ నివేదిక <xliff:g id="ID">#%d</xliff:g> ఉత్పాదించబడుతోంది"</string>
-    <string name="bugreport_finished_title" msgid="4429132808670114081">"బగ్ నివేదిక <xliff:g id="ID">#%d</xliff:g> సంగ్రహించబడింది"</string>
-    <string name="bugreport_updating_title" msgid="4423539949559634214">"బగ్ నివేదికకు వివరాలను జోడిస్తోంది"</string>
+    <string name="bugreport_in_progress_title" msgid="4311705936714972757">"బగ్ రిపోర్ట్‌ <xliff:g id="ID">#%d</xliff:g> ఉత్పాదించబడుతోంది"</string>
+    <string name="bugreport_finished_title" msgid="4429132808670114081">"బగ్ రిపోర్ట్‌ <xliff:g id="ID">#%d</xliff:g> సంగ్రహించబడింది"</string>
+    <string name="bugreport_updating_title" msgid="4423539949559634214">"బగ్ రిపోర్ట్‌‌కు వివరాలను జోడిస్తోంది"</string>
     <string name="bugreport_updating_wait" msgid="3322151947853929470">"దయచేసి వేచి ఉండండి..."</string>
-    <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"బగ్ నివేదిక త్వరలో ఫోన్‌లో కనిపిస్తుంది"</string>
-    <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"మీ బగ్ నివేదికను భాగస్వామ్యం చేయడానికి ఎంచుకోండి"</string>
-    <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"మీ బగ్ నివేదికను షేర్ చేయడానికి నొక్కండి"</string>
-    <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"స్క్రీన్‌షాట్ లేకుండా మీ బగ్ నివేదికను భాగస్వామ్యం చేయడానికి ఎంచుకోండి లేదా స్క్రీన్‌షాట్ ముగిసేదాకా వేచి ఉండండి"</string>
+    <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"బగ్ రిపోర్ట్‌ త్వరలో ఫోన్‌లో కనిపిస్తుంది"</string>
+    <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"మీ బగ్ రిపోర్ట్‌ను భాగస్వామ్యం చేయడానికి ఎంచుకోండి"</string>
+    <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"మీ బగ్ రిపోర్ట్‌ను షేర్ చేయడానికి నొక్కండి"</string>
+    <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"స్క్రీన్‌షాట్ లేకుండా మీ బగ్ రిపోర్ట్‌ను భాగస్వామ్యం చేయడానికి ఎంచుకోండి లేదా స్క్రీన్‌షాట్ ముగిసేదాకా వేచి ఉండండి"</string>
     <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"స్క్రీన్‌షాట్ లేకుండా మీ బగ్ నివే. భాగ. చేయడానికి నొక్కండి లేదా స్క్రీన్‌షాట్ ముగిసేదాకా వేచి ఉండండి"</string>
     <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"స్క్రీన్‌షాట్ లేకుండా మీ బగ్ నివే. భాగ. చేయడానికి నొక్కండి లేదా స్క్రీన్‌షాట్ ముగిసేదాకా వేచి ఉండండి"</string>
-    <string name="bugreport_confirm" msgid="5917407234515812495">"బగ్ రిపోర్ట్స్‌లో మీరు గోప్యమైనదిగా పరిగణించే (యాప్ వినియోగం, లొకేష‌న్‌ డేటా వంటి) డేటాతో పాటు సిస్టమ్‌కు సంబంధించిన విభిన్న లాగ్ ఫైల్‌ల డేటా ఉంటుంది. బగ్ నివేదికలను మీరు విశ్వసించే యాప్‌లు, వ్యక్తులతో మాత్రమే షేర్ చేయండి."</string>
+    <string name="bugreport_confirm" msgid="5917407234515812495">"బగ్ రిపోర్ట్స్‌లో మీరు గోప్యమైనదిగా పరిగణించే (యాప్ వినియోగం, లొకేష‌న్‌ డేటా వంటి) డేటాతో పాటు సిస్టమ్‌కు సంబంధించిన విభిన్న లాగ్ ఫైళ్ల డేటా ఉంటుంది. బగ్ రిపోర్ట్‌లను మీరు విశ్వసించే యాప్‌లు, వ్యక్తులతో మాత్రమే షేర్ చేయండి."</string>
     <string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"మళ్లీ చూపవద్దు"</string>
     <string name="bugreport_storage_title" msgid="5332488144740527109">"బగ్ రిపోర్ట్స్"</string>
-    <string name="bugreport_unreadable_text" msgid="586517851044535486">"బగ్ నివేదిక ఫైల్‌ను చదవడం సాధ్యపడలేదు"</string>
-    <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"బగ్ నివేదిక వివరాలను జిప్ ఫైల్‌కు జోడించడం సాధ్యపడలేదు"</string>
+    <string name="bugreport_unreadable_text" msgid="586517851044535486">"బగ్ రిపోర్ట్‌ ఫైల్‌ను చదవడం సాధ్యపడలేదు"</string>
+    <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"బగ్ రిపోర్ట్‌ వివరాలను జిప్ ఫైల్‌కు జోడించడం సాధ్యపడలేదు"</string>
     <string name="bugreport_unnamed" msgid="2800582406842092709">"పేరు లేనివి"</string>
     <string name="bugreport_info_action" msgid="2158204228510576227">"వివరాలు"</string>
     <string name="bugreport_screenshot_action" msgid="8677781721940614995">"స్క్రీన్‌షాట్"</string>
     <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"స్క్రీన్‌షాట్ విజయవంతంగా తీయబడింది."</string>
     <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"స్క్రీన్‌షాట్‌ను తీయడం సాధ్యపడలేదు."</string>
-    <string name="bugreport_info_dialog_title" msgid="1355948594292983332">"బగ్ నివేదిక <xliff:g id="ID">#%d</xliff:g> వివరాలు"</string>
+    <string name="bugreport_info_dialog_title" msgid="1355948594292983332">"బగ్ రిపోర్ట్‌ <xliff:g id="ID">#%d</xliff:g> వివరాలు"</string>
     <string name="bugreport_info_name" msgid="4414036021935139527">"ఫైల్ పేరు"</string>
     <string name="bugreport_info_title" msgid="2306030793918239804">"బగ్ శీర్షిక"</string>
     <string name="bugreport_info_description" msgid="5072835127481627722">"బగ్ సారాంశం"</string>
     <string name="save" msgid="4781509040564835759">"సేవ్ చేయి"</string>
-    <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"బగ్ నివేదిక షేర్ చేయండి"</string>
+    <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"బగ్ రిపోర్ట్‌ షేర్ చేయండి"</string>
 </resources>
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 a50efd7..3cbe435 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -5,7 +5,6 @@
 import android.animation.ValueAnimator
 import android.app.ActivityManager
 import android.app.ActivityTaskManager
-import android.app.AppGlobals
 import android.app.PendingIntent
 import android.app.TaskInfo
 import android.content.Context
@@ -44,6 +43,7 @@
     context: Context
 ) {
     companion object {
+        private const val DEBUG = false
         const val ANIMATION_DURATION = 500L
         private const val ANIMATION_DURATION_FADE_OUT_CONTENT = 150L
         private const val ANIMATION_DURATION_FADE_IN_WINDOW = 183L
@@ -75,8 +75,6 @@
         }
     }
 
-    private val packageManager = AppGlobals.getPackageManager()
-
     /** The interpolator used for the width, height, Y position and corner radius. */
     private val animationInterpolator = AnimationUtils.loadInterpolator(context,
             R.interpolator.launch_animation_interpolator_y)
@@ -100,6 +98,10 @@
      * If possible, you should pass the [packageName] of the intent that will be started so that
      * trampoline activity launches will also be animated.
      *
+     * If the device is currently locked, the user will have to unlock it before the intent is
+     * started unless [showOverLockscreen] is true. In that case, the activity will be started
+     * directly over the lockscreen.
+     *
      * This method will throw any exception thrown by [intentStarter].
      */
     @JvmOverloads
@@ -107,21 +109,22 @@
         controller: Controller?,
         animate: Boolean = true,
         packageName: String? = null,
+        showOverLockscreen: Boolean = false,
         intentStarter: (RemoteAnimationAdapter?) -> Int
     ) {
         if (controller == null || !animate) {
-            Log.d(TAG, "Starting intent with no animation")
+            Log.i(TAG, "Starting intent with no animation")
             intentStarter(null)
             controller?.callOnIntentStartedOnMainThread(willAnimate = false)
             return
         }
 
-        Log.d(TAG, "Starting intent with a launch animation")
         val runner = Runner(controller)
-        val isOnKeyguard = callback.isOnKeyguard()
+        val hideKeyguardWithAnimation = callback.isOnKeyguard() && !showOverLockscreen
 
-        // Pass the RemoteAnimationAdapter to the intent starter only if we are not on the keyguard.
-        val animationAdapter = if (!isOnKeyguard) {
+        // Pass the RemoteAnimationAdapter to the intent starter only if we are not hiding the
+        // keyguard with the animation
+        val animationAdapter = if (!hideKeyguardWithAnimation) {
             RemoteAnimationAdapter(
                     runner,
                     ANIMATION_DURATION,
@@ -149,9 +152,11 @@
         val willAnimate =
                 launchResult == ActivityManager.START_TASK_TO_FRONT ||
                         launchResult == ActivityManager.START_SUCCESS ||
-                        (launchResult == ActivityManager.START_DELIVERED_TO_TOP && isOnKeyguard)
+                        (launchResult == ActivityManager.START_DELIVERED_TO_TOP &&
+                                hideKeyguardWithAnimation)
 
-        Log.d(TAG, "launchResult=$launchResult willAnimate=$willAnimate isOnKeyguard=$isOnKeyguard")
+        Log.i(TAG, "launchResult=$launchResult willAnimate=$willAnimate " +
+                "hideKeyguardWithAnimation=$hideKeyguardWithAnimation")
         controller.callOnIntentStartedOnMainThread(willAnimate)
 
         // If we expect an animation, post a timeout to cancel it in case the remote animation is
@@ -160,7 +165,7 @@
             runner.postTimeout()
 
             // Hide the keyguard using the launch animation instead of the default unlock animation.
-            if (isOnKeyguard) {
+            if (hideKeyguardWithAnimation) {
                 callback.hideKeyguardWithAnimation(runner)
             }
         }
@@ -424,13 +429,16 @@
             nonApps: Array<out RemoteAnimationTarget>?,
             iCallback: IRemoteAnimationFinishedCallback?
         ) {
-            Log.d(TAG, "Remote animation started")
+            if (DEBUG) {
+                Log.d(TAG, "Remote animation started")
+            }
+
             val window = apps?.firstOrNull {
                 it.mode == RemoteAnimationTarget.MODE_OPENING
             }
 
             if (window == null) {
-                Log.d(TAG, "Aborting the animation as no window is opening")
+                Log.i(TAG, "Aborting the animation as no window is opening")
                 removeTimeout()
                 iCallback?.invoke()
                 controller.onLaunchAnimationCancelled()
@@ -500,7 +508,10 @@
             val launchContainerOverlay = launchContainer.overlay
             animator.addListener(object : AnimatorListenerAdapter() {
                 override fun onAnimationStart(animation: Animator?, isReverse: Boolean) {
-                    Log.d(TAG, "Animation started")
+                    if (DEBUG) {
+                        Log.d(TAG, "Animation started")
+                    }
+
                     callback.setBlursDisabledForAppLaunch(true)
                     controller.onLaunchAnimationStart(isExpandingFullyAbove)
 
@@ -511,7 +522,10 @@
                 }
 
                 override fun onAnimationEnd(animation: Animator?) {
-                    Log.d(TAG, "Animation ended")
+                    if (DEBUG) {
+                        Log.d(TAG, "Animation ended")
+                    }
+
                     callback.setBlursDisabledForAppLaunch(false)
                     iCallback?.invoke()
                     controller.onLaunchAnimationEnd(isExpandingFullyAbove)
@@ -686,7 +700,7 @@
                 return
             }
 
-            Log.d(TAG, "Remote animation timed out")
+            Log.i(TAG, "Remote animation timed out")
             timedOut = true
             controller.onLaunchAnimationCancelled()
         }
@@ -696,7 +710,7 @@
                 return
             }
 
-            Log.d(TAG, "Remote animation was cancelled")
+            Log.i(TAG, "Remote animation was cancelled")
             cancelled = true
             removeTimeout()
             context.mainExecutor.execute {
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/flags/Flag.kt b/packages/SystemUI/plugin/src/com/android/systemui/flags/Flag.kt
new file mode 100644
index 0000000..68834bc
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/flags/Flag.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.flags
+
+interface Flag<T> {
+    val id: Int
+    val default: T
+}
+
+data class BooleanFlag @JvmOverloads constructor(
+    override val id: Int,
+    override val default: Boolean = false
+) : Flag<Boolean>
+
+data class StringFlag @JvmOverloads constructor(
+    override val id: Int,
+    override val default: String = ""
+) : Flag<String>
+
+data class IntFlag @JvmOverloads constructor(
+    override val id: Int,
+    override val default: Int = 0
+) : Flag<Int>
+
+data class LongFlag @JvmOverloads constructor(
+    override val id: Int,
+    override val default: Long = 0
+) : Flag<Long>
+
+data class FloatFlag @JvmOverloads constructor(
+    override val id: Int,
+    override val default: Float = 0f
+) : Flag<Float>
+
+data class DoubleFlag @JvmOverloads constructor(
+    override val id: Int,
+    override val default: Double = 0.0
+) : Flag<Double>
\ No newline at end of file
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/plugin/src/com/android/systemui/flags/Flags.java
new file mode 100644
index 0000000..d5b9243
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/flags/Flags.java
@@ -0,0 +1,23 @@
+/*
+ * 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.flags;
+
+/**
+ * List of {@link Flag} objects for use in SystemUI.
+ */
+public class Flags {
+}
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 7c81325..6d088f0 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -60,8 +60,16 @@
      */
     void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade, int flags);
     void startActivity(Intent intent, boolean dismissShade);
+
+    default void startActivity(Intent intent, boolean dismissShade,
+            @Nullable ActivityLaunchAnimator.Controller animationController) {
+        startActivity(intent, dismissShade, animationController,
+                false /* showOverLockscreenWhenLocked */);
+    }
+
     void startActivity(Intent intent, boolean dismissShade,
-            @Nullable ActivityLaunchAnimator.Controller animationController);
+            @Nullable ActivityLaunchAnimator.Controller animationController,
+            boolean showOverLockscreenWhenLocked);
     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/plugin/src/com/android/systemui/plugins/FlagReaderPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FlagReaderPlugin.java
new file mode 100644
index 0000000..ab17499
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FlagReaderPlugin.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.plugins;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+
+/**
+ * Plugin for loading flag values from an alternate source of truth.
+ */
+@ProvidesInterface(action = FlagReaderPlugin.ACTION, version = FlagReaderPlugin.VERSION)
+public interface FlagReaderPlugin extends Plugin {
+    int VERSION = 1;
+    String ACTION = "com.android.systemui.flags.FLAG_READER_PLUGIN";
+
+    /** Returns a boolean value for the given flag. */
+    default boolean isEnabled(int id, boolean def) {
+        return def;
+    }
+
+    /** Returns a string value for the given flag id. */
+    default String getValue(int id, String def) {
+        return def;
+    }
+
+    /** Returns a int value for the given flag. */
+    default int getValue(int id, int def) {
+        return def;
+    }
+
+    /** Returns a long value for the given flag. */
+    default long getValue(int id, long def) {
+        return def;
+    }
+
+    /** Returns a float value for the given flag. */
+    default float getValue(int id, float def) {
+        return def;
+    }
+
+    /** Returns a double value for the given flag. */
+    default double getValue(int id, double def) {
+        return def;
+    }
+
+    /** Add a listener to be alerted when any flag changes. */
+    default void addListener(Listener listener) {}
+
+    /** Remove a listener to be alerted when any flag changes. */
+    default void removeListener(Listener listener) {}
+
+    /** A simple listener to be alerted when a flag changes. */
+    interface Listener {
+        /** */
+        void onFlagChanged(int id);
+    }
+}
diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions.xml b/packages/SystemUI/res-keyguard/layout/footer_actions.xml
new file mode 100644
index 0000000..dfc3e63
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/footer_actions.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+** Copyright 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.
+-->
+
+<!-- Action buttons for footer in QS/QQS, containing settings button, power off button etc -->
+<com.android.systemui.qs.FooterActionsView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="48dp"
+    android:gravity="center_vertical">
+
+    <com.android.systemui.statusbar.AlphaOptimizedImageView
+        android:id="@android:id/edit"
+        android:layout_width="0dp"
+        android:layout_height="@dimen/qs_footer_action_button_size"
+        android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
+        android:layout_weight="1"
+        android:background="@drawable/qs_footer_action_chip_background"
+        android:clickable="true"
+        android:clipToPadding="false"
+        android:contentDescription="@string/accessibility_quick_settings_edit"
+        android:focusable="true"
+        android:padding="@dimen/qs_footer_icon_padding"
+        android:src="@*android:drawable/ic_mode_edit"
+        android:tint="?android:attr/textColorPrimary" />
+
+    <com.android.systemui.statusbar.phone.MultiUserSwitch
+        android:id="@+id/multi_user_switch"
+        android:layout_width="0dp"
+        android:layout_height="@dimen/qs_footer_action_button_size"
+        android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
+        android:layout_weight="1"
+        android:background="@drawable/qs_footer_action_chip_background"
+        android:focusable="true">
+
+        <ImageView
+            android:id="@+id/multi_user_avatar"
+            android:layout_width="@dimen/multi_user_avatar_expanded_size"
+            android:layout_height="@dimen/multi_user_avatar_expanded_size"
+            android:layout_gravity="center"
+            android:scaleType="centerInside" />
+    </com.android.systemui.statusbar.phone.MultiUserSwitch>
+
+    <com.android.systemui.statusbar.AlphaOptimizedImageView
+        android:id="@+id/pm_lite"
+        android:layout_width="0dp"
+        android:layout_height="@dimen/qs_footer_action_button_size"
+        android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
+        android:layout_weight="1"
+        android:background="@drawable/qs_footer_action_chip_background"
+        android:clickable="true"
+        android:clipToPadding="false"
+        android:focusable="true"
+        android:padding="@dimen/qs_footer_icon_padding"
+        android:src="@*android:drawable/ic_lock_power_off"
+        android:contentDescription="@string/accessibility_quick_settings_power_menu"
+        android:tint="?android:attr/textColorPrimary" />
+
+    <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
+        android:id="@+id/settings_button_container"
+        android:layout_width="0dp"
+        android:layout_height="@dimen/qs_footer_action_button_size"
+        android:background="@drawable/qs_footer_action_chip_background"
+        android:layout_weight="1"
+        android:clipChildren="false"
+        android:clipToPadding="false">
+
+        <com.android.systemui.statusbar.phone.SettingsButton
+            android:id="@+id/settings_button"
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/qs_footer_action_button_size"
+            android:layout_gravity="center"
+            android:contentDescription="@string/accessibility_quick_settings_settings"
+            android:background="@drawable/qs_footer_action_chip_background_borderless"
+            android:padding="@dimen/qs_footer_icon_padding"
+            android:scaleType="centerInside"
+            android:src="@drawable/ic_settings"
+            android:tint="?android:attr/textColorPrimary" />
+
+        <com.android.systemui.statusbar.AlphaOptimizedImageView
+            android:id="@+id/tuner_icon"
+            android:layout_width="8dp"
+            android:layout_height="8dp"
+            android:layout_gravity="center_horizontal|bottom"
+            android:layout_marginBottom="@dimen/qs_footer_icon_padding"
+            android:src="@drawable/tuner"
+            android:tint="?android:attr/textColorTertiary"
+            android:visibility="invisible" />
+
+    </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
+
+</com.android.systemui.qs.FooterActionsView>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
index ce63082..f613a19 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml
@@ -27,49 +27,44 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:clipChildren="false"
-    android:clipToPadding="false"
     androidprv:layout_maxWidth="@dimen/keyguard_security_width"
-    androidprv:layout_maxHeight="@dimen/keyguard_security_height"
-    android:gravity="center_horizontal">
+    android:clipChildren="false"
+    android:clipToPadding="false">
 
-    <FrameLayout
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/pattern_container"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:clipChildren="false"
-        android:clipToPadding="false">
-
-        <LinearLayout
-            android:id="@+id/pattern_container"
-            android:layout_height="wrap_content"
+        android:layout_height="0dp"
+        android:layout_marginBottom="8dp"
+        android:layout_weight="1"
+        android:layoutDirection="ltr">
+        <androidx.constraintlayout.widget.Guideline
+            android:id="@+id/pattern_top_guideline"
             android:layout_width="wrap_content"
-            android:orientation="vertical"
-            android:layout_gravity="center_horizontal|bottom"
-            android:clipChildren="false"
-            android:clipToPadding="false">
+            android:layout_height="wrap_content"
+            androidprv:layout_constraintGuide_percent="0"
+            android:orientation="horizontal" />
 
-            <com.android.internal.widget.LockPatternView
-                android:id="@+id/lockPatternView"
-                android:layout_width="match_parent"
-                android:layout_height="0dp"
-                android:layout_weight="1"
-                android:layout_marginEnd="8dip"
-                android:layout_marginBottom="4dip"
-                android:layout_marginStart="8dip"
-                android:layout_gravity="center_horizontal"
-                android:gravity="center"
-                android:clipChildren="false"
-                android:clipToPadding="false" />
+        <com.android.internal.widget.LockPatternView
+            android:id="@+id/lockPatternView"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            androidprv:layout_constraintTop_toBottomOf="@id/pattern_top_guideline"
+            androidprv:layout_constraintBottom_toBottomOf="parent"
+            androidprv:layout_constraintLeft_toLeftOf="parent"
+            androidprv:layout_constraintRight_toRightOf="parent"
+            androidprv:layout_constraintDimensionRatio="1.0"
+            androidprv:layout_constraintVertical_bias="1.0"
+            />
+    </androidx.constraintlayout.widget.ConstraintLayout>
 
-          <include layout="@layout/keyguard_eca"
-              android:id="@+id/keyguard_selector_fade_container"
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content"
-              android:orientation="vertical"
-              android:layout_gravity="bottom|center_horizontal"
-              android:layout_marginTop="@dimen/keyguard_eca_top_margin"
-              android:gravity="center_horizontal" />
-        </LinearLayout>
-    </FrameLayout>
+    <include layout="@layout/keyguard_eca"
+        android:id="@+id/keyguard_selector_fade_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:layout_gravity="bottom|center_horizontal"
+        android:layout_marginTop="@dimen/keyguard_eca_top_margin"
+        android:gravity="center_horizontal" />
 
 </com.android.keyguard.KeyguardPatternView>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
index a22a56f..a946318 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -20,8 +20,8 @@
 <com.android.keyguard.KeyguardPINView
         xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:androidprv="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:id="@+id/keyguard_pin_view"
+        xmlns:tools="http://schemas.android.com/tools"
+        android:id="@+id/keyguard_pin_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         androidprv:layout_maxWidth="@dimen/keyguard_security_width"
@@ -47,12 +47,12 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:paddingBottom="@dimen/num_pad_entry_row_margin_bottom"
-            androidprv:layout_constraintBottom_toTopOf="@id/key1"
             androidprv:layout_constraintEnd_toEndOf="parent"
             androidprv:layout_constraintStart_toStartOf="parent"
 
             androidprv:layout_constraintTop_toTopOf="parent"
-            androidprv:layout_constraintVertical_bias="1.0">
+            androidprv:layout_constraintBottom_toTopOf="@id/key1"
+            androidprv:layout_constraintVertical_bias="0.0">
 
             <com.android.keyguard.PasswordTextView
                 android:id="@+id/pinEntry"
@@ -65,6 +65,15 @@
                 androidprv:scaledTextSize="@integer/scaled_password_text_size" />
         </com.android.keyguard.AlphaOptimizedRelativeLayout>
 
+        <!-- Guideline used to place the top row of keys relative to the screen height. This will be
+             updated in KeyguardPINView to reduce the height of the PIN pad. -->
+        <androidx.constraintlayout.widget.Guideline
+            android:id="@+id/pin_pad_top_guideline"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            androidprv:layout_constraintGuide_percent="0"
+            android:orientation="horizontal" />
+
         <androidx.constraintlayout.helper.widget.Flow
             android:id="@+id/flow1"
             android:layout_width="0dp"
@@ -78,16 +87,15 @@
             androidprv:flow_horizontalStyle="packed"
             androidprv:flow_maxElementsWrap="3"
 
+            androidprv:flow_verticalBias="1.0"
             androidprv:flow_verticalGap="@dimen/num_pad_entry_row_margin_bottom"
             androidprv:flow_verticalStyle="packed"
-            androidprv:flow_verticalBias="1.0"
 
             androidprv:flow_wrapMode="aligned"
-
-            androidprv:layout_constraintTop_toTopOf="parent"
             androidprv:layout_constraintBottom_toBottomOf="parent"
             androidprv:layout_constraintEnd_toEndOf="parent"
-            androidprv:layout_constraintStart_toStartOf="parent" />
+            androidprv:layout_constraintStart_toStartOf="parent"
+            androidprv:layout_constraintTop_toBottomOf="@id/pin_pad_top_guideline" />
 
         <com.android.keyguard.NumPadKey
             android:id="@+id/key1"
diff --git a/packages/SystemUI/res-keyguard/values-bn/strings.xml b/packages/SystemUI/res-keyguard/values-bn/strings.xml
index 967d5cb..2908bae 100644
--- a/packages/SystemUI/res-keyguard/values-bn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bn/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • চার্জ হচ্ছে"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • দ্রুত চার্জ হচ্ছে"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ধীরে চার্জ হচ্ছে"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • চার্জ সাময়িকভাবে বন্ধ করা আছে"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • চার্জ করা সাময়িকভাবে বন্ধ রাখা হয়েছে"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"আপনার চার্জার সংযুক্ত করুন।"</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"আনলক করতে মেনুতে টিপুন।"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"নেটওয়ার্ক লক করা আছে"</string>
diff --git a/packages/SystemUI/res-keyguard/values-de/strings.xml b/packages/SystemUI/res-keyguard/values-de/strings.xml
index ac5a58e..5be92983 100644
--- a/packages/SystemUI/res-keyguard/values-de/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-de/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wird geladen"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wird schnell geladen"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wird langsam geladen"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Aufladen vorübergehend eingeschränkt"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Laden vorübergehend eingeschränkt"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Ladegerät anschließen."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Zum Entsperren die Menütaste drücken."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Netzwerk gesperrt"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
index 38a7139..f8c8cad 100644
--- a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"En recharge : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"En recharge rapide : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"En recharge lente : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • recharge temporairement limitée"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Recharge temporairement limitée"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Branchez votre chargeur."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Appuyez sur la touche Menu pour déverrouiller l\'appareil."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Réseau verrouillé"</string>
diff --git a/packages/SystemUI/res-keyguard/values-gu/strings.xml b/packages/SystemUI/res-keyguard/values-gu/strings.xml
index a41cce7..e87ee92 100644
--- a/packages/SystemUI/res-keyguard/values-gu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gu/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ચાર્જિંગ"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ઝડપથી ચાર્જિંગ"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ધીમેથી ચાર્જિંગ"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ચાર્જિંગ હંગામીરૂપે પ્રતિબંધિત કરવામાં આવ્યું છે"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ચાર્જ કરવાનું થોડા સમય માટે મર્યાદિત કરવામાં આવ્યું છે"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"તમારું ચાર્જર કનેક્ટ કરો."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"અનલૉક કરવા માટે મેનૂ દબાવો."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"નેટવર્ક લૉક થયું"</string>
diff --git a/packages/SystemUI/res-keyguard/values-in/strings.xml b/packages/SystemUI/res-keyguard/values-in/strings.xml
index 6237075..edf8ab2 100644
--- a/packages/SystemUI/res-keyguard/values-in/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-in/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengisi daya"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengisi daya dengan cepat"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengisi daya dengan lambat"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Pengisian daya dibatasi untuk sementara"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Pengisian daya dibatasi sementara"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Hubungkan pengisi daya."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Tekan Menu untuk membuka kunci."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Jaringan terkunci"</string>
diff --git a/packages/SystemUI/res-keyguard/values-iw/strings.xml b/packages/SystemUI/res-keyguard/values-iw/strings.xml
index 4e7ac49..bbc5aa0 100644
--- a/packages/SystemUI/res-keyguard/values-iw/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-iw/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • בטעינה"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • בטעינה מהירה"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • בטעינה איטית"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • הטעינה מוגבלת זמנית"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • הטעינה מוגבלת באופן זמני"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"כדאי לחבר את המטען."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"יש ללחוץ על \'תפריט\' כדי לבטל את הנעילה."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"הרשת נעולה"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ja/strings.xml b/packages/SystemUI/res-keyguard/values-ja/strings.xml
index a1d58b7..60b52ea 100644
--- a/packages/SystemUI/res-keyguard/values-ja/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ja/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 充電中"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 急速充電中"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 低速充電中"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 充電は一時的に制限されています"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 充電を一時的に制限しています"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"充電してください。"</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"メニューからロックを解除できます。"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"ネットワークがロックされました"</string>
diff --git a/packages/SystemUI/res-keyguard/values-km/strings.xml b/packages/SystemUI/res-keyguard/values-km/strings.xml
index a0ae88a..eca4957 100644
--- a/packages/SystemUI/res-keyguard/values-km/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-km/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • កំពុង​សាកថ្ម"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • កំពុង​សាកថ្មយ៉ាង​ឆាប់រហ័ស"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • កំពុង​សាកថ្មយឺត"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • បានដាក់កម្រិត​ការសាកថ្ម​ជាបណ្ដោះអាសន្ន"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • បានដាក់កំហិតលើ​ការសាកថ្មជា​បណ្ដោះអាសន្ន"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"សូមសាក​ថ្ម​របស់​អ្នក។"</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ចុចម៉ឺនុយ ​ដើម្បី​ដោះ​សោ។"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"បណ្ដាញ​ជាប់​សោ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-kn/strings.xml b/packages/SystemUI/res-keyguard/values-kn/strings.xml
index 98b1af6..5cbe02e 100644
--- a/packages/SystemUI/res-keyguard/values-kn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kn/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ಚಾರ್ಜ್‌ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ವೇಗವಾಗಿ ಚಾರ್ಜ್‌ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ನಿಧಾನವಾಗಿ ಚಾರ್ಜ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ಚಾರ್ಜಿಂಗ್ ತಾತ್ಕಾಲಿಕವಾಗಿ ಸೀಮಿತವಾಗಿದೆ"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ಚಾರ್ಜಿಂಗ್ ಅನ್ನು ತಾತ್ಕಾಲಿಕವಾಗಿ ಸೀಮಿತಗೊಳಿಸಲಾಗಿದೆ"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"ನಿಮ್ಮ ಚಾರ್ಜರ್ ಸಂಪರ್ಕಗೊಳಿಸಿ."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ಅನ್‌ಲಾಕ್ ಮಾಡಲು ಮೆನು ಒತ್ತಿರಿ."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"ನೆಟ್‌ವರ್ಕ್ ಲಾಕ್ ಆಗಿದೆ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-lo/strings.xml b/packages/SystemUI/res-keyguard/values-lo/strings.xml
index 65b0409..b6fd676 100644
--- a/packages/SystemUI/res-keyguard/values-lo/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-lo/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ກຳລັງສາກ"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ກຳລັງສາກແບບດ່ວນ"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ກຳລັງສາກແບບຊ້າ"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ຈຳກັດການສາກໄຟຊົ່ວຄາວ"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ຈຳກັດການສາກໄຟຊົ່ວຄາວແລ້ວ"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"ເຊື່ອມຕໍ່ສາຍສາກຂອງທ່ານ."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ກົດ \"ເມນູ\" ເພື່ອປົດລັອກ."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"ເຄືອຂ່າຍຖືກລັອກ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-mn/strings.xml b/packages/SystemUI/res-keyguard/values-mn/strings.xml
index 110b711..b4f709d 100644
--- a/packages/SystemUI/res-keyguard/values-mn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mn/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Цэнэглэж байна"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Хурдан цэнэглэж байна"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Удаан цэнэглэж байна"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Цэнэглэхийг түр зуур хязгаарласан"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Цэнэглэхийг түр хязгаарласан"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Цэнэглэгчээ холбоно уу."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Түгжээг тайлах бол цэсийг дарна уу."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Сүлжээ түгжигдсэн"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ms/strings.xml b/packages/SystemUI/res-keyguard/values-ms/strings.xml
index 82579d4..6092cc7 100644
--- a/packages/SystemUI/res-keyguard/values-ms/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ms/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengecas"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengecas dengan cepat"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mengecas dengan perlahan"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Pengecasan terhad buat sementara waktu"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Pengecasan terhad sementara"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Sambungkan pengecas anda."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Tekan Menu untuk membuka kunci."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rangkaian dikunci"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sl/strings.xml b/packages/SystemUI/res-keyguard/values-sl/strings.xml
index 371d993..bb388ea 100644
--- a/packages/SystemUI/res-keyguard/values-sl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sl/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • polnjenje"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • hitro polnjenje"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • počasno polnjenje"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Začasno omejeno polnjenje"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Polnjenje začasno omejeno"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Priključite napajalnik."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Če želite odkleniti, pritisnite meni."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Omrežje je zaklenjeno"</string>
diff --git a/packages/SystemUI/res/values-h560dp-xhdpi/config.xml b/packages/SystemUI/res-keyguard/values-sw600dp-land/donottranslate.xml
similarity index 66%
rename from packages/SystemUI/res/values-h560dp-xhdpi/config.xml
rename to packages/SystemUI/res-keyguard/values-sw600dp-land/donottranslate.xml
index cf2017f..1a52e93 100644
--- a/packages/SystemUI/res/values-h560dp-xhdpi/config.xml
+++ b/packages/SystemUI/res-keyguard/values-sw600dp-land/donottranslate.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-
 <!--
-  ~ Copyright (C) 2014 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.
@@ -13,11 +12,10 @@
   ~ distributed under the License is distributed on an "AS IS" BASIS,
   ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
+  ~ limitations under the License.
   -->
-<resources>
-    <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
-     card. -->
-    <integer name="keyguard_max_notification_count">3</integer>
-</resources>
 
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Don't use the smaller PIN pad keys if we have the screen space to support it. -->
+    <string name="num_pad_key_ratio">1.0</string>
+</resources>
diff --git a/packages/SystemUI/res-keyguard/values-ta/strings.xml b/packages/SystemUI/res-keyguard/values-ta/strings.xml
index adec7fe..ef36fdd 100644
--- a/packages/SystemUI/res-keyguard/values-ta/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ta/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • சார்ஜாகிறது"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • வேகமாகச் சார்ஜாகிறது"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • மெதுவாகச் சார்ஜாகிறது"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • சார்ஜாவது தற்காலிகமாக வரம்பிடப்பட்டுள்ளது"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • சார்ஜிங் தற்காலிகமாக வரம்பிடப்பட்டுள்ளது"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"சார்ஜரை இணைக்கவும்."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"அன்லாக் செய்ய மெனுவை அழுத்தவும்."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"நெட்வொர்க் பூட்டப்பட்டது"</string>
diff --git a/packages/SystemUI/res-keyguard/values-te/strings.xml b/packages/SystemUI/res-keyguard/values-te/strings.xml
index 03bb5cc..f9544af 100644
--- a/packages/SystemUI/res-keyguard/values-te/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-te/strings.xml
@@ -40,7 +40,7 @@
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • నెమ్మదిగా ఛార్జ్ అవుతోంది"</string>
     <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ఛార్జింగ్ తాత్కాలికంగా పరిమితం చేయబడింది"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"మీ ఛార్జర్‌ను కనెక్ట్ చేయండి."</string>
-    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"అన్‌లాక్ చేయడానికి మెనుని నొక్కండి."</string>
+    <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"అన్‌లాక్ చేయడానికి మెనూను నొక్కండి."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"నెట్‌వర్క్ లాక్ చేయబడింది"</string>
     <string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"SIM కార్డ్ లేదు"</string>
     <string name="keyguard_missing_sim_instructions" msgid="1162120926141335918">"SIM కార్డ్‌ని చొప్పించండి."</string>
@@ -83,8 +83,8 @@
     <string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"PUK కోడ్ అనేది 8 లేదా అంతకంటే ఎక్కువ సంఖ్యలు ఉండాలి."</string>
     <string name="kg_invalid_puk" msgid="1774337070084931186">"సరైన PUK కోడ్‌ను మళ్లీ నమోదు చేయండి. ఎక్కువసార్లు ప్రయత్నించడం వలన SIM శాశ్వతంగా నిలిపివేయబడుతుంది."</string>
     <string name="kg_login_too_many_attempts" msgid="4519957179182578690">"నమూనాని చాలా ఎక్కువసార్లు గీసారు"</string>
-    <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"మీరు మీ పిన్‌ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేసారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
-    <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"మీరు మీ పాస్‌వర్డ్‌ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేసారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
+    <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"మీరు మీ పిన్‌ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేశారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
+    <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"మీరు మీ పాస్‌వర్డ్‌ను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా టైప్ చేశారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
     <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"మీరు మీ అన్‌లాక్ నమూనాను <xliff:g id="NUMBER_0">%1$d</xliff:g> సార్లు తప్పుగా గీసారు. \n\n<xliff:g id="NUMBER_1">%2$d</xliff:g> సెకన్లలో మళ్లీ ప్రయత్నించండి."</string>
     <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"SIM పిన్ కోడ్ తప్పు, ఇప్పుడు మీ డివైజ్‌ను అన్‌లాక్ చేయాలంటే, మీరు తప్పనిసరిగా మీ క్యారియర్‌ను సంప్రదించాలి."</string>
     <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
diff --git a/packages/SystemUI/res-keyguard/values-vi/strings.xml b/packages/SystemUI/res-keyguard/values-vi/strings.xml
index 3c3972c..be10a50 100644
--- a/packages/SystemUI/res-keyguard/values-vi/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-vi/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đang sạc"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đang sạc nhanh"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Đang sạc chậm"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Khả năng sạc tạm thời bị hạn chế"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Chức năng sạc tạm thời bị hạn chế"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Kết nối bộ sạc của bạn."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Nhấn vào Menu để mở khóa."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mạng đã bị khóa"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
index d950d40..621fe49 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 充電中"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 快速充電中"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 慢速充電中"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 充電功能暫時受到限制"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 已暫時限制充電"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"請連接充電器。"</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"按選單鍵解鎖。"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"網路已鎖定"</string>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 7e3c87b..a2ae5023 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -98,4 +98,10 @@
     <dimen name="below_clock_padding_start">32dp</dimen>
     <dimen name="below_clock_padding_end">16dp</dimen>
     <dimen name="below_clock_padding_start_icons">28dp</dimen>
+
+    <!-- Proportion of the screen height to use to set the maximum height of the bouncer to when
+         the device is in the DEVICE_POSTURE_HALF_OPENED posture, for the PIN/pattern entry. 0 will
+         allow it to use the whole screen space, 0.6 will allow it to use just under half of the
+         screen. -->
+    <item name="half_opened_bouncer_height_ratio" type="dimen" format="float">0.0</item>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values/donottranslate.xml b/packages/SystemUI/res-keyguard/values/donottranslate.xml
index 878c017..052d329 100644
--- a/packages/SystemUI/res-keyguard/values/donottranslate.xml
+++ b/packages/SystemUI/res-keyguard/values/donottranslate.xml
@@ -21,6 +21,9 @@
     <!-- Skeleton string format for displaying the date when an alarm is set. -->
     <string name="abbrev_wday_month_day_no_year_alarm">EEEMMMd</string>
 
+    <!-- Skeleton string format for displaying the date shorter. -->
+    <string name="abbrev_month_day_no_year">MMMd</string>
+
     <!-- Skeleton string format for displaying the time in 12-hour format. -->
     <string name="clock_12hr_format">hm</string>
 
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index ea769c6..871b1c4 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -62,8 +62,8 @@
         <item name="android:src">@drawable/ic_backspace_24dp</item>
     </style>
     <style name="NumPadKey.Enter">
-      <item name="android:colorControlNormal">?android:attr/textColorSecondary</item>
-      <item name="android:src">@drawable/ic_keyboard_tab_36dp</item>
+        <item name="android:colorControlNormal">?android:attr/textColorSecondary</item>
+        <item name="android:src">@drawable/ic_keyboard_tab_36dp</item>
     </style>
     <style name="Widget.TextView.NumPadKey.Klondike"
            parent="@android:style/Widget.DeviceDefault.TextView">
diff --git a/packages/SystemUI/res/anim/fp_to_unlock.xml b/packages/SystemUI/res/anim/fp_to_unlock.xml
index a348208..a5f75b6 100644
--- a/packages/SystemUI/res/anim/fp_to_unlock.xml
+++ b/packages/SystemUI/res/anim/fp_to_unlock.xml
@@ -19,10 +19,10 @@
       <group android:name="_R_G">
         <group android:name="_R_G_L_1_G_N_7_T_0" android:translateX="-27" android:translateY="-17.5">
           <group android:name="_R_G_L_1_G" android:translateX="30.75" android:translateY="25.75">
-            <path android:name="_R_G_L_1_G_D_0_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2.5" android:strokeAlpha="1" android:pathData=" M27.52 38.98 C26.49,39.95 25.29,40.73 23.98,41.29 C23.17,41.65 22.31,41.91 21.41,42.07 C20.74,42.19 20.05,42.25 19.34,42.25 C18.44,42.25 17.56,42.15 16.72,41.96 C15.93,41.77 15.16,41.51 14.43,41.18 C13.23,40.63 12.13,39.88 11.16,38.98 " />
-            <path android:name="_R_G_L_1_G_D_1_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2.5" android:strokeAlpha="1" android:pathData=" M8.64 34.07 C7.89,31.97 7.89,29.85 7.89,29.85 C7.89,24.05 12.81,19.34 19.34,19.34 C25.87,19.34 30.8,24.05 30.8,29.85 C30.8,29.85 30.8,30.16 30.8,30.16 C30.8,32.32 29.04,34.07 26.89,34.07 C25.28,34.07 23.86,33.1 23.27,31.61 C23.27,31.61 21.96,28.34 21.96,28.34 C21.37,26.85 19.93,25.89 18.34,25.89 C16.18,25.89 14.43,27.64 14.43,29.8 C14.43,31.42 14.87,32.99 15.68,34.36 C16.22,35.26 16.93,36.08 17.77,36.75 C17.77,36.75 18.52,37.34 18.52,37.34 " />
-            <path android:name="_R_G_L_1_G_D_2_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2.5" android:strokeAlpha="1" android:pathData=" M6.25 19.34 C7.48,17.3 9.46,15.58 11.9,14.42 C12.93,13.94 14.03,13.55 15.2,13.27 C16.51,12.96 17.9,12.8 19.34,12.8 C20.77,12.8 22.14,12.96 23.45,13.26 C24.9,13.6 26.26,14.12 27.48,14.78 C29.6,15.92 31.32,17.5 32.43,19.34 " />
-            <path android:name="_R_G_L_1_G_D_3_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2.5" android:strokeAlpha="1" android:pathData=" M9.52 8.7 C10.98,7.91 12.58,7.28 14.28,6.86 C15.89,6.46 17.58,6.25 19.34,6.25 C21.06,6.25 22.72,6.45 24.3,6.83 C26.04,7.25 27.67,7.89 29.16,8.7 " />
+            <path android:name="_R_G_L_1_G_D_0_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M27.52 38.98 C26.49,39.95 25.29,40.73 23.98,41.29 C23.17,41.65 22.31,41.91 21.41,42.07 C20.74,42.19 20.05,42.25 19.34,42.25 C18.44,42.25 17.56,42.15 16.72,41.96 C15.93,41.77 15.16,41.51 14.43,41.18 C13.23,40.63 12.13,39.88 11.16,38.98 " />
+            <path android:name="_R_G_L_1_G_D_1_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M8.64 34.07 C7.89,31.97 7.89,29.85 7.89,29.85 C7.89,24.05 12.81,19.34 19.34,19.34 C25.87,19.34 30.8,24.05 30.8,29.85 C30.8,29.85 30.8,30.16 30.8,30.16 C30.8,32.32 29.04,34.07 26.89,34.07 C25.28,34.07 23.86,33.1 23.27,31.61 C23.27,31.61 21.96,28.34 21.96,28.34 C21.37,26.85 19.93,25.89 18.34,25.89 C16.18,25.89 14.43,27.64 14.43,29.8 C14.43,31.42 14.87,32.99 15.68,34.36 C16.22,35.26 16.93,36.08 17.77,36.75 C17.77,36.75 18.52,37.34 18.52,37.34 " />
+            <path android:name="_R_G_L_1_G_D_2_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M6.25 19.34 C7.48,17.3 9.46,15.58 11.9,14.42 C12.93,13.94 14.03,13.55 15.2,13.27 C16.51,12.96 17.9,12.8 19.34,12.8 C20.77,12.8 22.14,12.96 23.45,13.26 C24.9,13.6 26.26,14.12 27.48,14.78 C29.6,15.92 31.32,17.5 32.43,19.34 " />
+            <path android:name="_R_G_L_1_G_D_3_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M9.52 8.7 C10.98,7.91 12.58,7.28 14.28,6.86 C15.89,6.46 17.58,6.25 19.34,6.25 C21.06,6.25 22.72,6.45 24.3,6.83 C26.04,7.25 27.67,7.89 29.16,8.7 " />
           </group>
         </group>
         <group android:name="_R_G_L_0_G_N_7_T_0" android:translateX="-27" android:translateY="-17.5">
diff --git a/packages/SystemUI/res/anim/lock_to_unlock.xml b/packages/SystemUI/res/anim/lock_to_unlock.xml
index ec51c01..76f7a05 100644
--- a/packages/SystemUI/res/anim/lock_to_unlock.xml
+++ b/packages/SystemUI/res/anim/lock_to_unlock.xml
@@ -21,7 +21,7 @@
           <group android:name="_R_G_L_2_G_N_10_T_1" android:translateX="50.25" android:translateY="61">
             <group android:name="_R_G_L_2_G_N_10_T_0" android:translateX="-13.75" android:translateY="-7.5">
               <group android:name="_R_G_L_2_G" android:translateX="-0.375" android:translateY="-22.375">
-                <path android:name="_R_G_L_2_G_D_0_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2.5" android:strokeAlpha="1" android:pathData=" M4.75 15 C4.75,15 23.25,15 23.25,15 C24.35,15 25.25,15.9 25.25,17 C25.25,17 25.25,33 25.25,33 C25.25,34.1 24.35,35 23.25,35 C23.25,35 4.75,35 4.75,35 C3.65,35 2.75,34.1 2.75,33 C2.75,33 2.75,17 2.75,17 C2.75,15.9 3.65,15 4.75,15c " />
+                <path android:name="_R_G_L_2_G_D_0_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M4.75 15 C4.75,15 23.25,15 23.25,15 C24.35,15 25.25,15.9 25.25,17 C25.25,17 25.25,33 25.25,33 C25.25,34.1 24.35,35 23.25,35 C23.25,35 4.75,35 4.75,35 C3.65,35 2.75,34.1 2.75,33 C2.75,33 2.75,17 2.75,17 C2.75,15.9 3.65,15 4.75,15c " />
               </group>
             </group>
           </group>
@@ -30,7 +30,7 @@
           <group android:name="_R_G_L_1_G_N_10_T_1" android:translateX="50.25" android:translateY="61">
             <group android:name="_R_G_L_1_G_N_10_T_0" android:translateX="-13.75" android:translateY="-7.5">
               <group android:name="_R_G_L_1_G" android:translateX="5" android:translateY="-22.5">
-                <path android:name="_R_G_L_1_G_D_0_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2.5" android:strokeAlpha="1" android:pathData=" M2.5 15 C2.5,15 2.5,8.61 2.5,8.61 C2.5,5.24 5.3,2.5 8.75,2.5 C12.2,2.5 15,5.24 15,8.61 C15,8.61 15,15 15,15 " />
+                <path android:name="_R_G_L_1_G_D_0_P_0" android:strokeColor="#b7f29f" android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="2" android:strokeAlpha="1" android:pathData=" M2.5 15 C2.5,15 2.5,8.61 2.5,8.61 C2.5,5.24 5.3,2.5 8.75,2.5 C12.2,2.5 15,5.24 15,8.61 C15,8.61 15,15 15,15 " />
               </group>
             </group>
           </group>
diff --git a/packages/SystemUI/res/anim/progress_indeterminate_horizontal_rect.xml b/packages/SystemUI/res/anim/progress_indeterminate_horizontal_rect.xml
new file mode 100644
index 0000000..13133cb
--- /dev/null
+++ b/packages/SystemUI/res/anim/progress_indeterminate_horizontal_rect.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<!-- Copy of progress_indeterminate_horizontal_rect2 in frameworks/base/core/res -->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="2000"
+        android:propertyXName="translateX"
+        android:pathData="M -197.60001,0 c 14.28182,0 85.07782,0 135.54689,0 c 54.26191,0 90.42461,0 168.24331,0 c 144.72154,0 316.40982,0 316.40982,0 "
+        android:interpolator="@interpolator/progress_indeterminate_horizontal_rect2_translatex_copy"
+        android:repeatCount="infinite" />
+</set>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/docked_divider_background.xml b/packages/SystemUI/res/color/docked_divider_background.xml
new file mode 100644
index 0000000..85ede9a
--- /dev/null
+++ b/packages/SystemUI/res/color/docked_divider_background.xml
@@ -0,0 +1,18 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+  <item android:color="@android:color/system_neutral2_500" android:lStar="35" />
+</selector>
diff --git a/packages/SystemUI/res/color/settingslib_state_off.xml b/packages/SystemUI/res/color/settingslib_state_off.xml
new file mode 100644
index 0000000..e821825
--- /dev/null
+++ b/packages/SystemUI/res/color/settingslib_state_off.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <item android:color="?androidprv:attr/colorAccentSecondaryVariant"/>
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/settingslib_state_on.xml b/packages/SystemUI/res/color/settingslib_state_on.xml
new file mode 100644
index 0000000..6d2133c
--- /dev/null
+++ b/packages/SystemUI/res/color/settingslib_state_on.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <item android:color="?androidprv:attr/colorAccentPrimary"/>
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/settingslib_track_off.xml b/packages/SystemUI/res/color/settingslib_track_off.xml
new file mode 100644
index 0000000..21d1dcc
--- /dev/null
+++ b/packages/SystemUI/res/color/settingslib_track_off.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <item android:color="?androidprv:attr/colorAccentSecondary"/>
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/settingslib_track_on.xml b/packages/SystemUI/res/color/settingslib_track_on.xml
new file mode 100644
index 0000000..ba7848a
--- /dev/null
+++ b/packages/SystemUI/res/color/settingslib_track_on.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <item android:color="?androidprv:attr/colorAccentPrimaryVariant"/>
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/fingerprint_bg.xml b/packages/SystemUI/res/drawable/fingerprint_bg.xml
index 2b0ab6f..558ec08 100644
--- a/packages/SystemUI/res/drawable/fingerprint_bg.xml
+++ b/packages/SystemUI/res/drawable/fingerprint_bg.xml
@@ -14,10 +14,11 @@
 -->
 <shape
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:shape="oval">
 
     <solid
-      android:color="?android:attr/colorBackground"/>
+      android:color="?androidprv:attr/colorSurface"/>
 
     <size
         android:width="64dp"
diff --git a/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml b/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml
index 33263a9..0ae5dc7 100644
--- a/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml
+++ b/packages/SystemUI/res/drawable/fingerprint_dialog_error_to_fp.xml
@@ -1,201 +1,142 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright (C) 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
+<?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.
+-->
 <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:aapt="http://schemas.android.com/aapt">
+                 xmlns:aapt="http://schemas.android.com/aapt">
     <aapt:attr name="android:drawable">
-        <vector
-            android:width="60dp"
-            android:height="60dp"
-            android:viewportHeight="60"
-            android:viewportWidth="60">
+        <vector android:height="60dp" android:width="60dp" android:viewportHeight="60"
+                android:viewportWidth="60">
             <group android:name="_R_G">
-                <group
-                    android:name="_R_G_L_1_G_N_4_T_0"
-                    android:translateX="30"
-                    android:translateY="30">
-                    <group
-                        android:name="_R_G_L_1_G"
-                        android:pivotX="114"
-                        android:pivotY="114"
-                        android:scaleX="0.42200000000000004"
-                        android:scaleY="0.42200000000000004"
-                        android:translateX="-114"
-                        android:translateY="-114">
-                        <path
-                            android:name="_R_G_L_1_G_D_0_P_0"
-                            android:pathData=" M79.63 67.24 C79.63,67.24 111.5,47.42 147.83,67.24 "
-                            android:strokeAlpha="1"
-                            android:strokeColor="@color/biometric_dialog_accent"
-                            android:strokeLineCap="round"
-                            android:strokeLineJoin="round"
-                            android:strokeWidth="5.5"
-                            android:trimPathEnd="0"
-                            android:trimPathOffset="0"
-                            android:trimPathStart="0" />
-                        <path
-                            android:name="_R_G_L_1_G_D_1_P_0"
-                            android:pathData=" M64.27 98.07 C64.27,98.07 80.13,73.02 113.98,73.02 C147.83,73.02 163.56,97.26 163.56,97.26 "
-                            android:strokeAlpha="1"
-                            android:strokeColor="@color/biometric_dialog_accent"
-                            android:strokeLineCap="round"
-                            android:strokeLineJoin="round"
-                            android:strokeWidth="5.5"
-                            android:trimPathEnd="0"
-                            android:trimPathOffset="0"
-                            android:trimPathStart="0" />
-                        <path
-                            android:name="_R_G_L_1_G_D_2_P_0"
-                            android:pathData=" M72.53 151.07 C72.53,151.07 62.46,122.89 76.16,105.55 C89.86,88.21 106.72,86.73 113.98,86.73 C121.08,86.73 153.51,90.62 158.7,125.87 C159.14,128.82 158.8,132.88 157.18,136.09 C154.88,140.63 150.62,143.63 145.85,143.97 C133.78,144.85 129.76,137.92 129.26,128.49 C128.88,121.19 122.49,115.35 113.15,115.35 C102.91,115.35 95.97,126.69 99.77,139.74 C103.57,152.78 111.33,163.85 130.32,169.13 "
-                            android:strokeAlpha="1"
-                            android:strokeColor="@color/biometric_dialog_accent"
-                            android:strokeLineCap="round"
-                            android:strokeLineJoin="round"
-                            android:strokeWidth="5.5"
-                            android:trimPathEnd="0"
-                            android:trimPathOffset="0"
-                            android:trimPathStart="0" />
-                        <path
-                            android:name="_R_G_L_1_G_D_3_P_0"
-                            android:pathData=" M100.6 167.84 C100.6,167.84 82.76,152.1 83.75,130.31 C84.75,108.53 102.58,100.7 113.73,100.7 C124.87,100.7 144.19,108.56 144.19,130.01 "
-                            android:strokeAlpha="1"
-                            android:strokeColor="@color/biometric_dialog_accent"
-                            android:strokeLineCap="round"
-                            android:strokeLineJoin="round"
-                            android:strokeWidth="5.5"
-                            android:trimPathEnd="0"
-                            android:trimPathOffset="0"
-                            android:trimPathStart="0" />
-                        <path
-                            android:name="_R_G_L_1_G_D_4_P_0"
-                            android:pathData=" M113.73 129.17 C113.73,129.17 113.15,161.33 149.15,156.58 "
-                            android:strokeAlpha="1"
-                            android:strokeColor="@color/biometric_dialog_accent"
-                            android:strokeLineCap="round"
-                            android:strokeLineJoin="round"
-                            android:strokeWidth="5.5"
-                            android:trimPathEnd="0"
-                            android:trimPathOffset="0"
-                            android:trimPathStart="0" />
+                <group android:name="_R_G_L_1_G" android:translateX="-0.05000000000000071">
+                    <group android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0" android:translateX="30"
+                           android:translateY="38.75" android:scaleX="1" android:scaleY="1">
+                        <path android:name="_R_G_L_1_G_D_0_P_0"
+                              android:fillColor="@color/biometric_dialog_error"
+                              android:fillAlpha="1" android:fillType="nonZero"
+                              android:pathData=" M-1.2 -1.25 C-1.2,-1.25 1.2,-1.25 1.2,-1.25 C1.2,-1.25 1.2,1.25 1.2,1.25 C1.2,1.25 -1.2,1.25 -1.2,1.25 C-1.2,1.25 -1.2,-1.25 -1.2,-1.25c "/>
                     </group>
+                    <group android:name="_R_G_L_1_G_D_1_P_0_G_0_T_0" android:translateX="30"
+                           android:translateY="25" android:pivotX="0.002" android:pivotY="7.488"
+                           android:scaleX="1" android:scaleY="1">
+                        <path android:name="_R_G_L_1_G_D_1_P_0"
+                              android:fillColor="@color/biometric_dialog_error"
+                              android:fillAlpha="1" android:fillType="nonZero"
+                              android:pathData=" M-1.2 -7.5 C-1.2,-7.5 1.2,-7.5 1.2,-7.5 C1.2,-7.5 1.2,7.5 1.2,7.5 C1.2,7.5 -1.2,7.5 -1.2,7.5 C-1.2,7.5 -1.2,-7.5 -1.2,-7.5c "/>
+                    </group>
+                    <path android:name="_R_G_L_1_G_D_2_P_0"
+                          android:strokeColor="@color/biometric_dialog_error"
+                          android:strokeLineCap="round" android:strokeLineJoin="round"
+                          android:strokeWidth="2.5" android:strokeAlpha="1"
+                          android:trimPathStart="0" android:trimPathEnd="1"
+                          android:trimPathOffset="0"
+                          android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "/>
                 </group>
-                <group
-                    android:name="_R_G_L_0_G_N_4_T_0"
-                    android:translateX="30"
-                    android:translateY="30">
-                    <group
-                        android:name="_R_G_L_0_G"
-                        android:translateX="-30.05"
-                        android:translateY="-30">
-                        <group
-                            android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"
-                            android:scaleX="1"
-                            android:scaleY="1"
-                            android:translateX="30"
-                            android:translateY="38.75">
-                            <path
-                                android:name="_R_G_L_0_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="@color/biometric_dialog_error"
-                                android:fillType="nonZero"
-                                android:pathData=" M-1.2 -1.25 C-1.2,-1.25 1.2,-1.25 1.2,-1.25 C1.2,-1.25 1.2,1.25 1.2,1.25 C1.2,1.25 -1.2,1.25 -1.2,1.25 C-1.2,1.25 -1.2,-1.25 -1.2,-1.25c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0"
-                            android:pivotX="0.002"
-                            android:pivotY="7.488"
-                            android:scaleX="1"
-                            android:scaleY="1"
-                            android:translateX="30"
-                            android:translateY="25">
-                            <path
-                                android:name="_R_G_L_0_G_D_1_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="@color/biometric_dialog_error"
-                                android:fillType="nonZero"
-                                android:pathData=" M-1.2 -7.5 C-1.2,-7.5 1.2,-7.5 1.2,-7.5 C1.2,-7.5 1.2,7.5 1.2,7.5 C1.2,7.5 -1.2,7.5 -1.2,7.5 C-1.2,7.5 -1.2,-7.5 -1.2,-7.5c " />
-                        </group>
-                        <path
-                            android:name="_R_G_L_0_G_D_2_P_0"
-                            android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
-                            android:strokeAlpha="1"
-                            android:strokeColor="@color/biometric_dialog_error"
-                            android:strokeLineCap="round"
-                            android:strokeLineJoin="round"
-                            android:strokeWidth="2.5"
-                            android:trimPathEnd="1"
-                            android:trimPathOffset="0"
-                            android:trimPathStart="0" />
-                    </group>
+                <group android:name="_R_G_L_0_G" android:translateX="-10.325"
+                       android:translateY="-10.25">
+                    <path android:name="_R_G_L_0_G_D_0_P_0"
+                          android:strokeColor="@color/biometric_dialog_accent"
+                          android:strokeLineCap="round" android:strokeLineJoin="round"
+                          android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0"
+                          android:trimPathEnd="0" android:trimPathOffset="0"
+                          android:pathData=" M31.41 48.43 C30.78,46.69 30.78,44.91 30.78,44.91 C30.78,40.09 34.88,36.16 40.32,36.16 C45.77,36.16 49.87,40.09 49.87,44.91 C49.87,44.91 49.87,45.17 49.87,45.17 C49.87,46.97 48.41,48.43 46.61,48.43 C45.28,48.43 44.09,47.63 43.6,46.39 C43.6,46.39 42.51,43.66 42.51,43.66 C42.02,42.42 40.82,41.61 39.49,41.61 C37.69,41.61 36.23,43.07 36.23,44.87 C36.23,47.12 37.26,49.26 39.02,50.67 C39.02,50.67 39.64,51.16 39.64,51.16 "/>
+                    <path android:name="_R_G_L_0_G_D_1_P_0"
+                          android:strokeColor="@color/biometric_dialog_accent"
+                          android:strokeLineCap="round" android:strokeLineJoin="round"
+                          android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0"
+                          android:trimPathEnd="0" android:trimPathOffset="0"
+                          android:pathData=" M32.14 27.3 C34.5,26 37.31,25.25 40.33,25.25 C43.34,25.25 46.15,26 48.51,27.3 "/>
+                    <path android:name="_R_G_L_0_G_D_2_P_0"
+                          android:strokeColor="@color/biometric_dialog_accent"
+                          android:strokeLineCap="round" android:strokeLineJoin="round"
+                          android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0"
+                          android:trimPathEnd="0" android:trimPathOffset="0"
+                          android:pathData=" M29.42 36.16 C31.35,32.94 35.51,30.71 40.33,30.71 C45.14,30.71 49.3,32.94 51.23,36.16 "/>
+                    <path android:name="_R_G_L_0_G_D_3_P_0"
+                          android:strokeColor="@color/biometric_dialog_accent"
+                          android:strokeLineCap="round" android:strokeLineJoin="round"
+                          android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0"
+                          android:trimPathEnd="0" android:trimPathOffset="0"
+                          android:pathData=" M47.14 52.52 C45.33,54.21 42.94,55.25 40.33,55.25 C37.71,55.25 35.32,54.21 33.51,52.52 "/>
                 </group>
             </group>
-            <group android:name="time_group" />
+            <group android:name="time_group"/>
         </vector>
     </aapt:attr>
-    <target android:name="_R_G_L_1_G_D_0_P_0">
+    <target android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0">
         <aapt:attr name="android:animation">
             <set android:ordering="together">
-                <objectAnimator
-                    android:duration="83"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0"
-                    android:valueType="floatType">
+                <objectAnimator android:propertyName="scaleX" android:duration="67"
+                                android:startOffset="0" android:valueFrom="1" android:valueTo="1.1"
+                                android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.853,0 0.6,1 1.0,1.0"/>
                     </aapt:attr>
                 </objectAnimator>
-                <objectAnimator
-                    android:duration="250"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="83"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
+                <objectAnimator android:propertyName="scaleY" android:duration="67"
+                                android:startOffset="0" android:valueFrom="1" android:valueTo="1.1"
+                                android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.853,0 0.6,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="scaleX" android:duration="100"
+                                android:startOffset="67" android:valueFrom="1.1" android:valueTo="0"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.06 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="scaleY" android:duration="100"
+                                android:startOffset="67" android:valueFrom="1.1" android:valueTo="0"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.06 1.0,1.0"/>
                     </aapt:attr>
                 </objectAnimator>
             </set>
         </aapt:attr>
     </target>
-    <target android:name="_R_G_L_1_G_D_1_P_0">
+    <target android:name="_R_G_L_1_G_D_1_P_0_G_0_T_0">
         <aapt:attr name="android:animation">
             <set android:ordering="together">
-                <objectAnimator
-                    android:duration="83"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0"
-                    android:valueType="floatType">
+                <objectAnimator android:propertyName="scaleX" android:duration="67"
+                                android:startOffset="0" android:valueFrom="1" android:valueTo="1"
+                                android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.659,1 1.0,1.0"/>
                     </aapt:attr>
                 </objectAnimator>
-                <objectAnimator
-                    android:duration="250"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="83"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
+                <objectAnimator android:propertyName="scaleY" android:duration="67"
+                                android:startOffset="0" android:valueFrom="1" android:valueTo="1.1"
+                                android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.6,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="scaleX" android:duration="100"
+                                android:startOffset="67" android:valueFrom="1" android:valueTo="1"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.6,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="scaleY" android:duration="100"
+                                android:startOffset="67" android:valueFrom="1.1" android:valueTo="0"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.096 1.0,1.0"/>
                     </aapt:attr>
                 </objectAnimator>
             </set>
@@ -204,182 +145,58 @@
     <target android:name="_R_G_L_1_G_D_2_P_0">
         <aapt:attr name="android:animation">
             <set android:ordering="together">
-                <objectAnimator
-                    android:duration="83"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0"
-                    android:valueType="floatType">
+                <objectAnimator android:propertyName="trimPathEnd" android:duration="67"
+                                android:startOffset="0" android:valueFrom="1" android:valueTo="1"
+                                android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
                     </aapt:attr>
                 </objectAnimator>
-                <objectAnimator
-                    android:duration="250"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="83"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
+                <objectAnimator android:propertyName="trimPathEnd" android:duration="133"
+                                android:startOffset="67" android:valueFrom="1" android:valueTo="0"
+                                android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
                     </aapt:attr>
                 </objectAnimator>
             </set>
         </aapt:attr>
     </target>
-    <target android:name="_R_G_L_1_G_D_3_P_0">
+    <target android:name="_R_G_L_0_G_D_0_P_0">
         <aapt:attr name="android:animation">
             <set android:ordering="together">
-                <objectAnimator
-                    android:duration="83"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0"
-                    android:valueType="floatType">
+                <objectAnimator android:propertyName="trimPathEnd" android:duration="83"
+                                android:startOffset="0" android:valueFrom="0" android:valueTo="0"
+                                android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
                     </aapt:attr>
                 </objectAnimator>
-                <objectAnimator
-                    android:duration="250"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="83"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
+                <objectAnimator android:propertyName="trimPathEnd" android:duration="250"
+                                android:startOffset="83" android:valueFrom="0" android:valueTo="1"
+                                android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
                     </aapt:attr>
                 </objectAnimator>
             </set>
         </aapt:attr>
     </target>
-    <target android:name="_R_G_L_1_G_D_4_P_0">
+    <target android:name="_R_G_L_0_G_D_1_P_0">
         <aapt:attr name="android:animation">
             <set android:ordering="together">
-                <objectAnimator
-                    android:duration="83"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0"
-                    android:valueType="floatType">
+                <objectAnimator android:propertyName="trimPathEnd" android:duration="83"
+                                android:startOffset="0" android:valueFrom="0" android:valueTo="0"
+                                android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
                     </aapt:attr>
                 </objectAnimator>
-                <objectAnimator
-                    android:duration="250"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="83"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
+                <objectAnimator android:propertyName="trimPathEnd" android:duration="250"
+                                android:startOffset="83" android:valueFrom="0" android:valueTo="1"
+                                android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="67"
-                    android:propertyName="scaleX"
-                    android:startOffset="0"
-                    android:valueFrom="1"
-                    android:valueTo="1.1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.853,0 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="67"
-                    android:propertyName="scaleY"
-                    android:startOffset="0"
-                    android:valueFrom="1"
-                    android:valueTo="1.1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.853,0 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="scaleX"
-                    android:startOffset="67"
-                    android:valueFrom="1.1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.06 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="scaleY"
-                    android:startOffset="67"
-                    android:valueFrom="1.1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.06 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="67"
-                    android:propertyName="scaleX"
-                    android:startOffset="0"
-                    android:valueFrom="1"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.659,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="67"
-                    android:propertyName="scaleY"
-                    android:startOffset="0"
-                    android:valueFrom="1"
-                    android:valueTo="1.1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="scaleX"
-                    android:startOffset="67"
-                    android:valueFrom="1"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="scaleY"
-                    android:startOffset="67"
-                    android:valueFrom="1.1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.8,0 0.92,1.096 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
                     </aapt:attr>
                 </objectAnimator>
             </set>
@@ -388,26 +205,38 @@
     <target android:name="_R_G_L_0_G_D_2_P_0">
         <aapt:attr name="android:animation">
             <set android:ordering="together">
-                <objectAnimator
-                    android:duration="67"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="0"
-                    android:valueFrom="1"
-                    android:valueTo="1"
-                    android:valueType="floatType">
+                <objectAnimator android:propertyName="trimPathEnd" android:duration="83"
+                                android:startOffset="0" android:valueFrom="0" android:valueTo="0"
+                                android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
                     </aapt:attr>
                 </objectAnimator>
-                <objectAnimator
-                    android:duration="133"
-                    android:propertyName="trimPathEnd"
-                    android:startOffset="67"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
+                <objectAnimator android:propertyName="trimPathEnd" android:duration="250"
+                                android:startOffset="83" android:valueFrom="0" android:valueTo="1"
+                                android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_3_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="trimPathEnd" android:duration="83"
+                                android:startOffset="0" android:valueFrom="0" android:valueTo="0"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="trimPathEnd" android:duration="250"
+                                android:startOffset="83" android:valueFrom="0" android:valueTo="1"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
                     </aapt:attr>
                 </objectAnimator>
             </set>
@@ -416,14 +245,10 @@
     <target android:name="time_group">
         <aapt:attr name="android:animation">
             <set android:ordering="together">
-                <objectAnimator
-                    android:duration="350"
-                    android:propertyName="translateX"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
+                <objectAnimator android:propertyName="translateX" android:duration="417"
+                                android:startOffset="0" android:valueFrom="0" android:valueTo="1"
+                                android:valueType="floatType"/>
             </set>
         </aapt:attr>
     </target>
-</animated-vector>
\ No newline at end of file
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml b/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml
index b899828..fc2c7d0 100644
--- a/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml
+++ b/packages/SystemUI/res/drawable/fingerprint_dialog_fp_to_error.xml
@@ -1,179 +1,170 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright (C) 2018 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
+<?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.
+-->
 <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:aapt="http://schemas.android.com/aapt">
+                 xmlns:aapt="http://schemas.android.com/aapt">
     <aapt:attr name="android:drawable">
-        <vector
-            android:width="60dp"
-            android:height="60dp"
-            android:viewportHeight="60"
-            android:viewportWidth="60">
+        <vector android:height="60dp" android:width="60dp" android:viewportHeight="60"
+                android:viewportWidth="60">
             <group android:name="_R_G">
-                <group
-                    android:name="_R_G_L_1_G_N_4_T_0"
-                    android:translateX="30"
-                    android:translateY="30">
-                    <group
-                        android:name="_R_G_L_1_G"
-                        android:pivotX="114"
-                        android:pivotY="114"
-                        android:scaleX="0.42244"
-                        android:scaleY="0.42244"
-                        android:translateX="-114"
-                        android:translateY="-114">
-                        <path
-                            android:name="_R_G_L_1_G_D_0_P_0"
-                            android:pathData=" M79.63 67.24 C79.63,67.24 111.5,47.42 147.83,67.24 "
-                            android:strokeAlpha="1"
-                            android:strokeColor="@color/biometric_dialog_accent"
-                            android:strokeLineCap="round"
-                            android:strokeLineJoin="round"
-                            android:strokeWidth="5.5"
-                            android:trimPathEnd="1"
-                            android:trimPathOffset="0"
-                            android:trimPathStart="0" />
-                        <path
-                            android:name="_R_G_L_1_G_D_1_P_0"
-                            android:pathData=" M64.27 98.07 C64.27,98.07 80.13,73.02 113.98,73.02 C147.83,73.02 163.56,97.26 163.56,97.26 "
-                            android:strokeAlpha="1"
-                            android:strokeColor="@color/biometric_dialog_accent"
-                            android:strokeLineCap="round"
-                            android:strokeLineJoin="round"
-                            android:strokeWidth="5.5"
-                            android:trimPathEnd="1"
-                            android:trimPathOffset="0"
-                            android:trimPathStart="0" />
-                        <path
-                            android:name="_R_G_L_1_G_D_2_P_0"
-                            android:pathData=" M72.53 151.07 C72.53,151.07 62.46,122.89 76.16,105.55 C89.86,88.21 106.72,86.73 113.98,86.73 C121.08,86.73 153.51,90.62 158.7,125.87 C159.14,128.82 158.8,132.88 157.18,136.09 C154.88,140.63 150.62,143.63 145.85,143.97 C133.78,144.85 129.76,137.92 129.26,128.49 C128.88,121.19 122.49,115.35 113.15,115.35 C102.91,115.35 95.97,126.69 99.77,139.74 C103.57,152.78 111.33,163.85 130.32,169.13 "
-                            android:strokeAlpha="1"
-                            android:strokeColor="@color/biometric_dialog_accent"
-                            android:strokeLineCap="round"
-                            android:strokeLineJoin="round"
-                            android:strokeWidth="5.5"
-                            android:trimPathEnd="1"
-                            android:trimPathOffset="0"
-                            android:trimPathStart="0" />
-                        <path
-                            android:name="_R_G_L_1_G_D_3_P_0"
-                            android:pathData=" M100.6 167.84 C100.6,167.84 82.76,152.1 83.75,130.31 C84.75,108.53 102.58,100.7 113.73,100.7 C124.87,100.7 144.19,108.56 144.19,130.01 "
-                            android:strokeAlpha="1"
-                            android:strokeColor="@color/biometric_dialog_accent"
-                            android:strokeLineCap="round"
-                            android:strokeLineJoin="round"
-                            android:strokeWidth="5.5"
-                            android:trimPathEnd="1"
-                            android:trimPathOffset="0"
-                            android:trimPathStart="0" />
-                        <path
-                            android:name="_R_G_L_1_G_D_4_P_0"
-                            android:pathData=" M113.73 129.17 C113.73,129.17 113.15,161.33 149.15,156.58 "
-                            android:strokeAlpha="1"
-                            android:strokeColor="@color/biometric_dialog_accent"
-                            android:strokeLineCap="round"
-                            android:strokeLineJoin="round"
-                            android:strokeWidth="5.5"
-                            android:trimPathEnd="1"
-                            android:trimPathOffset="0"
-                            android:trimPathStart="0" />
+                <group android:name="_R_G_L_1_G" android:translateX="-0.05000000000000071">
+                    <group android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0" android:translateX="30"
+                           android:translateY="38.75" android:scaleX="0" android:scaleY="0">
+                        <path android:name="_R_G_L_1_G_D_0_P_0"
+                              android:fillColor="@color/biometric_dialog_error"
+                              android:fillAlpha="1" android:fillType="nonZero"
+                              android:pathData=" M-1.2 -1.25 C-1.2,-1.25 1.2,-1.25 1.2,-1.25 C1.2,-1.25 1.2,1.25 1.2,1.25 C1.2,1.25 -1.2,1.25 -1.2,1.25 C-1.2,1.25 -1.2,-1.25 -1.2,-1.25c "/>
                     </group>
+                    <group android:name="_R_G_L_1_G_D_1_P_0_G_0_T_0" android:translateX="30"
+                           android:translateY="25" android:pivotX="0.002" android:pivotY="7.488"
+                           android:scaleX="1" android:scaleY="0">
+                        <path android:name="_R_G_L_1_G_D_1_P_0"
+                              android:fillColor="@color/biometric_dialog_error"
+                              android:fillAlpha="1" android:fillType="nonZero"
+                              android:pathData=" M-1.2 -7.5 C-1.2,-7.5 1.2,-7.5 1.2,-7.5 C1.2,-7.5 1.2,7.5 1.2,7.5 C1.2,7.5 -1.2,7.5 -1.2,7.5 C-1.2,7.5 -1.2,-7.5 -1.2,-7.5c "/>
+                    </group>
+                    <path android:name="_R_G_L_1_G_D_2_P_0"
+                          android:strokeColor="@color/biometric_dialog_error"
+                          android:strokeLineCap="round" android:strokeLineJoin="round"
+                          android:strokeWidth="2.5" android:strokeAlpha="1"
+                          android:trimPathStart="1" android:trimPathEnd="1"
+                          android:trimPathOffset="0"
+                          android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "/>
                 </group>
-                <group
-                    android:name="_R_G_L_0_G_N_4_T_0"
-                    android:translateX="30"
-                    android:translateY="30">
-                    <group
-                        android:name="_R_G_L_0_G"
-                        android:translateX="-30.05"
-                        android:translateY="-30">
-                        <group
-                            android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"
-                            android:scaleX="0"
-                            android:scaleY="0"
-                            android:translateX="30"
-                            android:translateY="38.75">
-                            <path
-                                android:name="_R_G_L_0_G_D_0_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="@color/biometric_dialog_error"
-                                android:fillType="nonZero"
-                                android:pathData=" M-1.2 -1.25 C-1.2,-1.25 1.2,-1.25 1.2,-1.25 C1.2,-1.25 1.2,1.25 1.2,1.25 C1.2,1.25 -1.2,1.25 -1.2,1.25 C-1.2,1.25 -1.2,-1.25 -1.2,-1.25c " />
-                        </group>
-                        <group
-                            android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0"
-                            android:pivotX="0.002"
-                            android:pivotY="7.488"
-                            android:scaleX="1"
-                            android:scaleY="0"
-                            android:translateX="30"
-                            android:translateY="25">
-                            <path
-                                android:name="_R_G_L_0_G_D_1_P_0"
-                                android:fillAlpha="1"
-                                android:fillColor="@color/biometric_dialog_error"
-                                android:fillType="nonZero"
-                                android:pathData=" M-1.2 -7.5 C-1.2,-7.5 1.2,-7.5 1.2,-7.5 C1.2,-7.5 1.2,7.5 1.2,7.5 C1.2,7.5 -1.2,7.5 -1.2,7.5 C-1.2,7.5 -1.2,-7.5 -1.2,-7.5c " />
-                        </group>
-                        <path
-                            android:name="_R_G_L_0_G_D_2_P_0"
-                            android:pathData=" M30 6.2 C16.9,6.2 6.3,16.8 6.3,30 C6.3,43.2 16.9,53.8 30,53.8 C43.1,53.8 53.8,43.2 53.8,30 C53.8,16.8 43.1,6.2 30,6.2c "
-                            android:strokeAlpha="1"
-                            android:strokeColor="@color/biometric_dialog_error"
-                            android:strokeLineCap="round"
-                            android:strokeLineJoin="round"
-                            android:strokeWidth="2.5"
-                            android:trimPathEnd="1"
-                            android:trimPathOffset="0"
-                            android:trimPathStart="1" />
-                    </group>
+                <group android:name="_R_G_L_0_G" android:translateX="-10.325"
+                       android:translateY="-10.25">
+                    <path android:name="_R_G_L_0_G_D_0_P_0"
+                          android:strokeColor="@color/biometric_dialog_accent"
+                          android:strokeLineCap="round" android:strokeLineJoin="round"
+                          android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0"
+                          android:trimPathEnd="1" android:trimPathOffset="0"
+                          android:pathData=" M31.41 48.43 C30.78,46.69 30.78,44.91 30.78,44.91 C30.78,40.09 34.88,36.16 40.32,36.16 C45.77,36.16 49.87,40.09 49.87,44.91 C49.87,44.91 49.87,45.17 49.87,45.17 C49.87,46.97 48.41,48.43 46.61,48.43 C45.28,48.43 44.09,47.63 43.6,46.39 C43.6,46.39 42.51,43.66 42.51,43.66 C42.02,42.42 40.82,41.61 39.49,41.61 C37.69,41.61 36.23,43.07 36.23,44.87 C36.23,47.12 37.26,49.26 39.02,50.67 C39.02,50.67 39.64,51.16 39.64,51.16 "/>
+                    <path android:name="_R_G_L_0_G_D_1_P_0"
+                          android:strokeColor="@color/biometric_dialog_accent"
+                          android:strokeLineCap="round" android:strokeLineJoin="round"
+                          android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0"
+                          android:trimPathEnd="1" android:trimPathOffset="0"
+                          android:pathData=" M32.14 27.3 C34.5,26 37.31,25.25 40.33,25.25 C43.34,25.25 46.15,26 48.51,27.3 "/>
+                    <path android:name="_R_G_L_0_G_D_2_P_0"
+                          android:strokeColor="@color/biometric_dialog_accent"
+                          android:strokeLineCap="round" android:strokeLineJoin="round"
+                          android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0"
+                          android:trimPathEnd="1" android:trimPathOffset="0"
+                          android:pathData=" M29.42 36.16 C31.35,32.94 35.51,30.71 40.33,30.71 C45.14,30.71 49.3,32.94 51.23,36.16 "/>
+                    <path android:name="_R_G_L_0_G_D_3_P_0"
+                          android:strokeColor="@color/biometric_dialog_accent"
+                          android:strokeLineCap="round" android:strokeLineJoin="round"
+                          android:strokeWidth="2" android:strokeAlpha="1" android:trimPathStart="0"
+                          android:trimPathEnd="1" android:trimPathOffset="0"
+                          android:pathData=" M47.14 52.52 C45.33,54.21 42.94,55.25 40.33,55.25 C37.71,55.25 35.32,54.21 33.51,52.52 "/>
                 </group>
             </group>
-            <group android:name="time_group" />
+            <group android:name="time_group"/>
         </vector>
     </aapt:attr>
-    <target android:name="_R_G_L_1_G_D_0_P_0">
+    <target android:name="_R_G_L_1_G_D_0_P_0_G_0_T_0">
         <aapt:attr name="android:animation">
             <set android:ordering="together">
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
+                <objectAnimator android:propertyName="scaleX" android:duration="167"
+                                android:startOffset="0" android:valueFrom="0" android:valueTo="0"
+                                android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="scaleY" android:duration="167"
+                                android:startOffset="0" android:valueFrom="0" android:valueTo="0"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="scaleX" android:duration="100"
+                                android:startOffset="167" android:valueFrom="0"
+                                android:valueTo="1.1" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="scaleY" android:duration="100"
+                                android:startOffset="167" android:valueFrom="0"
+                                android:valueTo="1.1" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="scaleX" android:duration="67"
+                                android:startOffset="267" android:valueFrom="1.1"
+                                android:valueTo="1" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.147,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="scaleY" android:duration="67"
+                                android:startOffset="267" android:valueFrom="1.1"
+                                android:valueTo="1" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.147,1 1.0,1.0"/>
                     </aapt:attr>
                 </objectAnimator>
             </set>
         </aapt:attr>
     </target>
-    <target android:name="_R_G_L_1_G_D_1_P_0">
+    <target android:name="_R_G_L_1_G_D_1_P_0_G_0_T_0">
         <aapt:attr name="android:animation">
             <set android:ordering="together">
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
+                <objectAnimator android:propertyName="scaleX" android:duration="167"
+                                android:startOffset="0" android:valueFrom="1" android:valueTo="1"
+                                android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="scaleY" android:duration="167"
+                                android:startOffset="0" android:valueFrom="0" android:valueTo="0"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.096 0.2,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="scaleX" android:duration="100"
+                                android:startOffset="167" android:valueFrom="1" android:valueTo="1"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="scaleY" android:duration="100"
+                                android:startOffset="167" android:valueFrom="0"
+                                android:valueTo="1.1" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.096 0.2,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="scaleX" android:duration="67"
+                                android:startOffset="267" android:valueFrom="1" android:valueTo="1"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.341,0 0.2,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator android:propertyName="scaleY" android:duration="67"
+                                android:startOffset="267" android:valueFrom="1.1"
+                                android:valueTo="1" android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
                     </aapt:attr>
                 </objectAnimator>
             </set>
@@ -182,193 +173,37 @@
     <target android:name="_R_G_L_1_G_D_2_P_0">
         <aapt:attr name="android:animation">
             <set android:ordering="together">
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
+                <objectAnimator android:propertyName="trimPathStart" android:duration="267"
+                                android:startOffset="0" android:valueFrom="1" android:valueTo="0"
+                                android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
                     </aapt:attr>
                 </objectAnimator>
             </set>
         </aapt:attr>
     </target>
-    <target android:name="_R_G_L_1_G_D_3_P_0">
+    <target android:name="_R_G_L_0_G_D_0_P_0">
         <aapt:attr name="android:animation">
             <set android:ordering="together">
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
+                <objectAnimator android:propertyName="trimPathStart" android:duration="167"
+                                android:startOffset="0" android:valueFrom="0" android:valueTo="1"
+                                android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
                     </aapt:attr>
                 </objectAnimator>
             </set>
         </aapt:attr>
     </target>
-    <target android:name="_R_G_L_1_G_D_4_P_0">
+    <target android:name="_R_G_L_0_G_D_1_P_0">
         <aapt:attr name="android:animation">
             <set android:ordering="together">
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType">
+                <objectAnimator android:propertyName="trimPathStart" android:duration="167"
+                                android:startOffset="0" android:valueFrom="0" android:valueTo="1"
+                                android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.6,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="scaleX"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="scaleY"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="scaleX"
-                    android:startOffset="167"
-                    android:valueFrom="0"
-                    android:valueTo="1.1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="scaleY"
-                    android:startOffset="167"
-                    android:valueFrom="0"
-                    android:valueTo="1.1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.06 0.2,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="67"
-                    android:propertyName="scaleX"
-                    android:startOffset="267"
-                    android:valueFrom="1.1"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.147,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="67"
-                    android:propertyName="scaleY"
-                    android:startOffset="267"
-                    android:valueFrom="1.1"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.147,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
-    <target android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="scaleX"
-                    android:startOffset="0"
-                    android:valueFrom="1"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="167"
-                    android:propertyName="scaleY"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="0"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.096 0.2,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="scaleX"
-                    android:startOffset="167"
-                    android:valueFrom="1"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="100"
-                    android:propertyName="scaleY"
-                    android:startOffset="167"
-                    android:valueFrom="0"
-                    android:valueTo="1.1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.08,0.096 0.2,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="67"
-                    android:propertyName="scaleX"
-                    android:startOffset="267"
-                    android:valueFrom="1"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.341,0 0.2,1 1.0,1.0" />
-                    </aapt:attr>
-                </objectAnimator>
-                <objectAnimator
-                    android:duration="67"
-                    android:propertyName="scaleY"
-                    android:startOffset="267"
-                    android:valueFrom="1.1"
-                    android:valueTo="1"
-                    android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
                     </aapt:attr>
                 </objectAnimator>
             </set>
@@ -377,15 +212,24 @@
     <target android:name="_R_G_L_0_G_D_2_P_0">
         <aapt:attr name="android:animation">
             <set android:ordering="together">
-                <objectAnimator
-                    android:duration="267"
-                    android:propertyName="trimPathStart"
-                    android:startOffset="0"
-                    android:valueFrom="1"
-                    android:valueTo="0"
-                    android:valueType="floatType">
+                <objectAnimator android:propertyName="trimPathStart" android:duration="167"
+                                android:startOffset="0" android:valueFrom="0" android:valueTo="1"
+                                android:valueType="floatType">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0" />
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_3_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator android:propertyName="trimPathStart" android:duration="167"
+                                android:startOffset="0" android:valueFrom="0" android:valueTo="1"
+                                android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
                     </aapt:attr>
                 </objectAnimator>
             </set>
@@ -394,14 +238,10 @@
     <target android:name="time_group">
         <aapt:attr name="android:animation">
             <set android:ordering="together">
-                <objectAnimator
-                    android:duration="350"
-                    android:propertyName="translateX"
-                    android:startOffset="0"
-                    android:valueFrom="0"
-                    android:valueTo="1"
-                    android:valueType="floatType" />
+                <objectAnimator android:propertyName="translateX" android:duration="350"
+                                android:startOffset="0" android:valueFrom="0" android:valueTo="1"
+                                android:valueType="floatType"/>
             </set>
         </aapt:attr>
     </target>
-</animated-vector>
\ No newline at end of file
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_arrow_forward.xml b/packages/SystemUI/res/drawable/ic_arrow_forward.xml
new file mode 100644
index 0000000..438e4c7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_arrow_forward.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ 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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:autoMirrored="true"
+        android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/black"
+        android:pathData="M6.23,20.23l1.77,1.77l10,-10l-10,-10l-1.77,1.77l8.23,8.23z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_friction_lock_closed.xml b/packages/SystemUI/res/drawable/ic_friction_lock_closed.xml
new file mode 100644
index 0000000..2c34060
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_friction_lock_closed.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2017 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:tint="?android:attr/colorControlNormal"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M18,8h-1V6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2H6c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V10C20,8.9 19.1,8 18,8zM9,6c0,-1.66 1.34,-3 3,-3s3,1.34 3,3v2H9V6zM18,20H6V10h12V20zM12,17c1.1,0 2,-0.9 2,-2c0,-1.1 -0.9,-2 -2,-2c-1.1,0 -2,0.9 -2,2C10,16.1 10.9,17 12,17z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_settings_24dp.xml b/packages/SystemUI/res/drawable/ic_settings_24dp.xml
new file mode 100644
index 0000000..ac4c43b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_settings_24dp.xml
@@ -0,0 +1,29 @@
+<!--
+     Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M13.85,22.25h-3.7c-0.74,0 -1.36,-0.54 -1.45,-1.27l-0.27,-1.89c-0.27,-0.14 -0.53,-0.29 -0.79,-0.46l-1.8,0.72c-0.7,0.26 -1.47,-0.03 -1.81,-0.65L2.2,15.53c-0.35,-0.66 -0.2,-1.44 0.36,-1.88l1.53,-1.19c-0.01,-0.15 -0.02,-0.3 -0.02,-0.46c0,-0.15 0.01,-0.31 0.02,-0.46l-1.52,-1.19C1.98,9.9 1.83,9.09 2.2,8.47l1.85,-3.19c0.34,-0.62 1.11,-0.9 1.79,-0.63l1.81,0.73c0.26,-0.17 0.52,-0.32 0.78,-0.46l0.27,-1.91c0.09,-0.7 0.71,-1.25 1.44,-1.25h3.7c0.74,0 1.36,0.54 1.45,1.27l0.27,1.89c0.27,0.14 0.53,0.29 0.79,0.46l1.8,-0.72c0.71,-0.26 1.48,0.03 1.82,0.65l1.84,3.18c0.36,0.66 0.2,1.44 -0.36,1.88l-1.52,1.19c0.01,0.15 0.02,0.3 0.02,0.46s-0.01,0.31 -0.02,0.46l1.52,1.19c0.56,0.45 0.72,1.23 0.37,1.86l-1.86,3.22c-0.34,0.62 -1.11,0.9 -1.8,0.63l-1.8,-0.72c-0.26,0.17 -0.52,0.32 -0.78,0.46l-0.27,1.91C15.21,21.71 14.59,22.25 13.85,22.25zM13.32,20.72c0,0.01 0,0.01 0,0.02L13.32,20.72zM10.68,20.7l0,0.02C10.69,20.72 10.69,20.71 10.68,20.7zM10.62,20.25h2.76l0.37,-2.55l0.53,-0.22c0.44,-0.18 0.88,-0.44 1.34,-0.78l0.45,-0.34l2.38,0.96l1.38,-2.4l-2.03,-1.58l0.07,-0.56c0.03,-0.26 0.06,-0.51 0.06,-0.78c0,-0.27 -0.03,-0.53 -0.06,-0.78l-0.07,-0.56l2.03,-1.58l-1.39,-2.4l-2.39,0.96l-0.45,-0.35c-0.42,-0.32 -0.87,-0.58 -1.33,-0.77L13.75,6.3l-0.37,-2.55h-2.76L10.25,6.3L9.72,6.51C9.28,6.7 8.84,6.95 8.38,7.3L7.93,7.63L5.55,6.68L4.16,9.07l2.03,1.58l-0.07,0.56C6.09,11.47 6.06,11.74 6.06,12c0,0.26 0.02,0.53 0.06,0.78l0.07,0.56l-2.03,1.58l1.38,2.4l2.39,-0.96l0.45,0.35c0.43,0.33 0.86,0.58 1.33,0.77l0.53,0.22L10.62,20.25zM18.22,17.72c0,0.01 -0.01,0.02 -0.01,0.03L18.22,17.72zM5.77,17.71l0.01,0.02C5.78,17.72 5.77,17.71 5.77,17.71zM3.93,9.47L3.93,9.47C3.93,9.47 3.93,9.47 3.93,9.47zM18.22,6.27c0,0.01 0.01,0.02 0.01,0.02L18.22,6.27zM5.79,6.25L5.78,6.27C5.78,6.27 5.79,6.26 5.79,6.25zM13.31,3.28c0,0.01 0,0.01 0,0.02L13.31,3.28zM10.69,3.26l0,0.02C10.69,3.27 10.69,3.27 10.69,3.26z"/>
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M12,12m-3.5,0a3.5,3.5 0,1 1,7 0a3.5,3.5 0,1 1,-7 0"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_signal_strength_zero_bar_no_internet.xml b/packages/SystemUI/res/drawable/ic_signal_strength_zero_bar_no_internet.xml
new file mode 100644
index 0000000..f38a368
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_signal_strength_zero_bar_no_internet.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0"
+    android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M2,22l16,0l0,-2l-11,0l13,-13l0,1l2,0l0,-6z"
+        android:strokeAlpha="0.3"
+        android:fillAlpha="0.3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,10h2v8h-2z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,20h2v2h-2z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_unlock.xml b/packages/SystemUI/res/drawable/ic_unlock.xml
new file mode 100644
index 0000000..46023e6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_unlock.xml
@@ -0,0 +1,42 @@
+<!--
+     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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="65dp" android:width="46dp" android:viewportHeight="65" android:viewportWidth="46">
+    <group android:translateX="8.625" android:translateY="13.625">
+        <path
+            android:strokeColor="#FF000000"
+            android:strokeLineCap="round"
+            android:strokeLineJoin="round"
+            android:strokeWidth="2"
+            android:pathData="M4.75 15 C4.75,15 23.25,15 23.25,15 C24.35,15 25.25,15.9 25.25,17 C25.25,17 25.25,33 25.25,33 C25.25,34.1 24.35,35 23.25,35 C23.25,35 4.75,35 4.75,35 C3.65,35 2.75,34.1 2.75,33 C2.75,33 2.75,17 2.75,17 C2.75,15.9 3.65,15 4.75,15c " />
+    </group>
+    <group android:translateX="14" android:translateY="13.5">
+        <path
+            android:strokeColor="#FF000000"
+            android:strokeLineCap="round"
+            android:strokeLineJoin="round"
+            android:strokeWidth="2"
+            android:pathData="M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " />
+    </group>
+    <group android:translateX="20" android:translateY="35.75">
+        <path
+            android:fillColor="#FF000000"
+            android:fillAlpha="1"
+            android:fillType="nonZero"
+            android:pathData=" M2.75 5.25 C4.13,5.25 5.25,4.13 5.25,2.75 C5.25,1.37 4.13,0.25 2.75,0.25 C1.37,0.25 0.25,1.37 0.25,2.75 C0.25,4.13 1.37,5.25 2.75,5.25c " />
+    </group>
+</vector>
diff --git a/packages/SystemUI/res/values-h560dp-xhdpi/config.xml b/packages/SystemUI/res/drawable/internet_dialog_background.xml
similarity index 64%
copy from packages/SystemUI/res/values-h560dp-xhdpi/config.xml
copy to packages/SystemUI/res/drawable/internet_dialog_background.xml
index cf2017f..3ceb0f6 100644
--- a/packages/SystemUI/res/values-h560dp-xhdpi/config.xml
+++ b/packages/SystemUI/res/drawable/internet_dialog_background.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-
 <!--
-  ~ Copyright (C) 2014 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.
@@ -13,11 +12,12 @@
   ~ distributed under the License is distributed on an "AS IS" BASIS,
   ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
+  ~ limitations under the License.
   -->
-<resources>
-    <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
-     card. -->
-    <integer name="keyguard_max_notification_count">3</integer>
-</resources>
 
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+    <shape android:shape="rectangle">
+        <corners android:radius="8dp" />
+        <solid android:color="?android:attr/colorBackground" />
+    </shape>
+</inset>
diff --git a/packages/SystemUI/res/drawable/internet_dialog_footer_background.xml b/packages/SystemUI/res/drawable/internet_dialog_footer_background.xml
new file mode 100644
index 0000000..50267fd
--- /dev/null
+++ b/packages/SystemUI/res/drawable/internet_dialog_footer_background.xml
@@ -0,0 +1,30 @@
+<?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
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+       android:shape="rectangle">
+    <stroke
+        android:color="?androidprv:attr/colorAccentPrimaryVariant"
+        android:width="1dp"/>
+    <corners android:radius="20dp"/>
+    <padding
+        android:left="8dp"
+        android:right="8dp"
+        android:top="4dp"
+        android:bottom="4dp" />
+    <solid android:color="@android:color/transparent" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/internet_dialog_rounded_top_corner_background.xml b/packages/SystemUI/res/drawable/internet_dialog_rounded_top_corner_background.xml
new file mode 100644
index 0000000..14672ef
--- /dev/null
+++ b/packages/SystemUI/res/drawable/internet_dialog_rounded_top_corner_background.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+    <shape android:shape="rectangle">
+        <corners
+            android:topLeftRadius="@dimen/internet_dialog_corner_radius"
+            android:topRightRadius="@dimen/internet_dialog_corner_radius"
+            android:bottomLeftRadius="@dimen/internet_dialog_corner_radius"
+            android:bottomRightRadius="@dimen/internet_dialog_corner_radius"/>
+        <solid android:color="?android:attr/colorBackground" />
+    </shape>
+</inset>
diff --git a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
index 1535e72..3a08a71 100644
--- a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
+++ b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
@@ -22,11 +22,12 @@
     android:color="?android:attr/textColorPrimary">
   <item>
     <shape
-        android:shape="oval">
+        android:shape="rectangle">
       <solid android:color="?androidprv:attr/colorSurface"/>
       <size
           android:width="@dimen/keyguard_affordance_width"
           android:height="@dimen/keyguard_affordance_height"/>
+      <corners android:radius="@dimen/keyguard_affordance_fixed_radius"/>
     </shape>
   </item>
 </ripple>
diff --git a/packages/SystemUI/res/drawable/progress_indeterminate_horizontal_material_trimmed.xml b/packages/SystemUI/res/drawable/progress_indeterminate_horizontal_material_trimmed.xml
new file mode 100644
index 0000000..95209f8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/progress_indeterminate_horizontal_material_trimmed.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+
+<!-- Copy of progress_indeterminate_horizontal_material_trimmed in frameworks/base/core/res -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+                 android:drawable="@drawable/vector_drawable_progress_indeterminate_horizontal_trimmed" >
+    <target
+        android:name="rect_grp"
+        android:animation="@anim/progress_indeterminate_horizontal_rect" />
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_switch_bar_bg_disabled.xml b/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_disabled.xml
similarity index 100%
copy from packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_switch_bar_bg_disabled.xml
copy to packages/SystemUI/res/drawable/settingslib_switch_bar_bg_disabled.xml
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_switch_bar_bg_on.xml b/packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml
similarity index 100%
copy from packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_switch_bar_bg_on.xml
copy to packages/SystemUI/res/drawable/settingslib_switch_bar_bg_on.xml
diff --git a/packages/SystemUI/res/drawable/settingslib_thumb_disabled.xml b/packages/SystemUI/res/drawable/settingslib_thumb_disabled.xml
new file mode 100644
index 0000000..b41762f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_thumb_disabled.xml
@@ -0,0 +1,33 @@
+<?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.
+  -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:top="@dimen/settingslib_switch_thumb_margin"
+        android:left="@dimen/settingslib_switch_thumb_margin"
+        android:right="@dimen/settingslib_switch_thumb_margin"
+        android:bottom="@dimen/settingslib_switch_thumb_margin">
+        <shape android:shape="oval">
+            <size
+                android:height="@dimen/settingslib_switch_thumb_size"
+                android:width="@dimen/settingslib_switch_thumb_size"/>
+            <solid
+                android:color="@color/settingslib_thumb_off_color"
+                android:alpha="?android:attr/disabledAlpha"/>
+        </shape>
+    </item>
+</layer-list>
diff --git a/packages/SystemUI/res/drawable/settingslib_thumb_off.xml b/packages/SystemUI/res/drawable/settingslib_thumb_off.xml
new file mode 100644
index 0000000..87d4aea
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_thumb_off.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:top="@dimen/settingslib_switch_thumb_margin"
+        android:bottom="@dimen/settingslib_switch_thumb_margin">
+        <shape android:shape="oval">
+            <size
+                android:height="@dimen/settingslib_switch_thumb_size"
+                android:width="@dimen/settingslib_switch_thumb_size"/>
+            <solid android:color="@color/settingslib_thumb_off_color"/>
+        </shape>
+    </item>
+</layer-list>
diff --git a/packages/SystemUI/res/drawable/settingslib_thumb_on.xml b/packages/SystemUI/res/drawable/settingslib_thumb_on.xml
new file mode 100644
index 0000000..5566ea3
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_thumb_on.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:top="@dimen/settingslib_switch_thumb_margin"
+        android:bottom="@dimen/settingslib_switch_thumb_margin">
+        <shape android:shape="oval">
+            <size
+                android:height="@dimen/settingslib_switch_thumb_size"
+                android:width="@dimen/settingslib_switch_thumb_size"/>
+            <solid android:color="@color/settingslib_state_on_color"/>
+        </shape>
+    </item>
+</layer-list>
diff --git a/packages/SystemUI/res/drawable/settingslib_thumb_selector.xml b/packages/SystemUI/res/drawable/settingslib_thumb_selector.xml
new file mode 100644
index 0000000..06bb779
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_thumb_selector.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/settingslib_thumb_on" android:state_checked="true"/>
+    <item android:drawable="@drawable/settingslib_thumb_off" android:state_checked="false"/>
+    <item android:drawable="@drawable/settingslib_thumb_disabled" android:state_enabled="false"/>
+</selector>
diff --git a/packages/SystemUI/res/drawable/settingslib_track_disabled_background.xml b/packages/SystemUI/res/drawable/settingslib_track_disabled_background.xml
new file mode 100644
index 0000000..15dfcb7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_track_disabled_background.xml
@@ -0,0 +1,26 @@
+<?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.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle"
+    android:width="@dimen/settingslib_switch_track_width"
+    android:height="@dimen/settingslib_switch_track_height">
+    <solid
+        android:color="@color/settingslib_track_off_color"
+        android:alpha="?android:attr/disabledAlpha"/>
+    <corners android:radius="@dimen/settingslib_switch_track_radius"/>
+</shape>
diff --git a/packages/SystemUI/res/drawable/settingslib_track_off_background.xml b/packages/SystemUI/res/drawable/settingslib_track_off_background.xml
new file mode 100644
index 0000000..3a09284
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_track_off_background.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle"
+    android:width="@dimen/settingslib_switch_track_width"
+    android:height="@dimen/settingslib_switch_track_height">
+    <padding android:left="@dimen/settingslib_switch_thumb_margin"
+             android:right="@dimen/settingslib_switch_thumb_margin"/>
+    <solid android:color="@color/settingslib_track_off_color"/>
+    <corners android:radius="@dimen/settingslib_switch_track_radius"/>
+</shape>
diff --git a/packages/SystemUI/res/drawable/settingslib_track_on_background.xml b/packages/SystemUI/res/drawable/settingslib_track_on_background.xml
new file mode 100644
index 0000000..1d9dacd
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_track_on_background.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle"
+    android:width="@dimen/settingslib_switch_track_width"
+    android:height="@dimen/settingslib_switch_track_height">
+    <padding android:left="@dimen/settingslib_switch_thumb_margin"
+             android:right="@dimen/settingslib_switch_thumb_margin"/>
+    <solid android:color="@color/settingslib_track_on_color"/>
+    <corners android:radius="@dimen/settingslib_switch_track_radius"/>
+</shape>
diff --git a/packages/SystemUI/res/drawable/settingslib_track_selector.xml b/packages/SystemUI/res/drawable/settingslib_track_selector.xml
new file mode 100644
index 0000000..a38c3b4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/settingslib_track_selector.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/settingslib_track_on_background" android:state_checked="true"/>
+    <item android:drawable="@drawable/settingslib_track_off_background" android:state_checked="false"/>
+    <item android:drawable="@drawable/settingslib_track_disabled_background" android:state_enabled="false"/>
+</selector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml b/packages/SystemUI/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml
new file mode 100644
index 0000000..aec204f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml
@@ -0,0 +1,44 @@
+<?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.
+-->
+
+<!-- Variant of vector_drawable_progress_indeterminate_horizontal in frameworks/base/core/res, which
+     draws the whole height of the progress bar instead having blank space above and below the
+     bar. -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+        android:height="10dp"
+        android:width="340dp"
+        android:viewportHeight="10"
+        android:viewportWidth="340" >
+    <group
+        android:name="progress_group"
+        android:translateX="180"
+        android:translateY="5" >
+        <path
+            android:name="background_track"
+            android:pathData="M -180.0,-5.0 l 360.0,0 l 0,10.0 l -360.0,0 Z"
+            android:fillColor="?androidprv:attr/colorSurfaceVariant"/>
+        <group
+            android:name="rect_grp"
+            android:translateX="-197.60001"
+            android:scaleX="0.5" >
+            <path
+                android:name="rect"
+                android:pathData="M -144.0,-5.0 l 288.0,0 l 0,10.0 l -288.0,0 Z"
+                android:fillColor="?androidprv:attr/colorAccentPrimaryVariant" />
+        </group>
+    </group>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/global_actions_grid_lite.xml b/packages/SystemUI/res/layout/global_actions_grid_lite.xml
index 2430eec..5588fd3 100644
--- a/packages/SystemUI/res/layout/global_actions_grid_lite.xml
+++ b/packages/SystemUI/res/layout/global_actions_grid_lite.xml
@@ -13,13 +13,12 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<LinearLayout
+<androidx.constraintlayout.widget.ConstraintLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/global_actions_container"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="vertical"
     android:gravity="center"
     android:layout_gravity="center">
   <com.android.systemui.globalactions.GlobalActionsLayoutLite
@@ -29,8 +28,11 @@
       android:orientation="vertical"
       android:clipChildren="false"
       android:clipToPadding="false"
-      android:background="@drawable/global_actions_lite_background"
-      android:padding="@dimen/global_actions_lite_padding">
+      app:layout_constraintBottom_toBottomOf="parent"
+      app:layout_constraintTop_toTopOf="parent"
+      app:layout_constraintStart_toStartOf="parent"
+      app:layout_constraintEnd_toEndOf="parent"
+      android:layout_weight="1">
     <androidx.constraintlayout.widget.ConstraintLayout
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
@@ -38,6 +40,8 @@
         android:gravity="center"
         android:translationZ="@dimen/global_actions_translate"
         android:orientation="horizontal"
+        android:background="@drawable/global_actions_lite_background"
+        android:padding="@dimen/global_actions_lite_padding"
         android:layoutDirection="ltr">
       <androidx.constraintlayout.helper.widget.Flow
           android:id="@+id/list_flow"
@@ -53,4 +57,4 @@
           app:flow_horizontalStyle="packed"/>
     </androidx.constraintlayout.widget.ConstraintLayout>
   </com.android.systemui.globalactions.GlobalActionsLayoutLite>
-</LinearLayout>
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/global_actions_toast.xml b/packages/SystemUI/res/layout/global_actions_toast.xml
new file mode 100644
index 0000000..1f08996
--- /dev/null
+++ b/packages/SystemUI/res/layout/global_actions_toast.xml
@@ -0,0 +1,44 @@
+<?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.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center|bottom"
+    android:gravity="center"
+    android:layout_marginBottom="@dimen/global_actions_info_margin"
+    app:layout_constraintBottom_toBottomOf="parent"
+    app:layout_constraintStart_toStartOf="parent"
+    app:layout_constraintEnd_toEndOf="parent"
+    app:layout_constraintWidth_max="382dp"
+    android:layout_weight="0"
+    android:background="@drawable/global_actions_lite_background"
+    android:theme="@style/Theme.SystemUI.QuickSettings"
+    android:paddingTop="14dp"
+    android:paddingBottom="14dp"
+    android:paddingStart="20dp"
+    android:paddingEnd="20dp"
+    android:orientation="horizontal">
+    <TextView
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:textSize="14sp"
+        android:textColor="?android:attr/textColorSecondary"
+        android:text="@string/global_action_smart_lock_disabled" />
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/global_screenshot_static.xml b/packages/SystemUI/res/layout/global_screenshot_static.xml
index e4a9694..6a9254c 100644
--- a/packages/SystemUI/res/layout/global_screenshot_static.xml
+++ b/packages/SystemUI/res/layout/global_screenshot_static.xml
@@ -36,7 +36,6 @@
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_marginEnd="@dimen/screenshot_action_container_margin_horizontal"
-        android:layout_marginBottom="@dimen/screenshot_action_container_offset_y"
         android:paddingEnd="@dimen/screenshot_action_container_padding_right"
         android:paddingVertical="@dimen/screenshot_action_container_padding_vertical"
         android:elevation="1dp"
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
new file mode 100644
index 0000000..b841419
--- /dev/null
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -0,0 +1,373 @@
+<?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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:id="@+id/internet_connectivity_dialog"
+    android:layout_width="@dimen/internet_dialog_list_max_width"
+    android:layout_height="@dimen/internet_dialog_list_max_height"
+    android:background="@drawable/internet_dialog_rounded_top_corner_background"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        style="@style/Widget.SliceView.Panel"
+        android:gravity="center_vertical|center_horizontal"
+        android:layout_marginTop="24dp"
+        android:layout_marginBottom="@dimen/internet_dialog_network_layout_margin"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/internet_dialog_title"
+            android:gravity="center_vertical|center_horizontal"
+            android:layout_width="wrap_content"
+            android:layout_height="32dp"
+            android:textColor="?android:attr/textColorPrimary"
+            android:fontFamily="google-sans"
+            android:textSize="24sp"/>
+
+        <TextView
+            android:id="@+id/internet_dialog_subtitle"
+            android:gravity="center_vertical|center_horizontal"
+            android:layout_width="wrap_content"
+            android:layout_height="20dp"
+            android:layout_marginTop="4dp"
+            android:ellipsize="end"
+            android:maxLines="1"
+            android:fontFamily="google-sans"
+            android:textSize="14sp"/>
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="@dimen/internet_dialog_network_layout_margin"
+        android:orientation="vertical">
+
+        <View
+            android:id="@+id/divider"
+            android:layout_gravity="center_vertical|center_horizontal"
+            android:layout_width="340dp"
+            android:layout_height="4dp"
+            android:background="?androidprv:attr/colorSurfaceVariant"/>
+
+        <ProgressBar
+            android:id="@+id/wifi_searching_progress"
+            android:indeterminate="true"
+            android:layout_width="340dp"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:visibility="gone"
+            style="@style/TrimmedHorizontalProgressBar"/>
+    </LinearLayout>
+
+    <androidx.core.widget.NestedScrollView
+        android:id="@+id/scroll_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+        <LinearLayout
+            android:id="@+id/scroll_layout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+            <LinearLayout
+                android:id="@+id/internet_list"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical">
+
+                <LinearLayout
+                    android:id="@+id/mobile_network_layout"
+                    android:layout_width="match_parent"
+                    android:layout_height="88dp"
+                    android:clickable="true"
+                    android:focusable="true"
+                    android:background="?android:attr/selectableItemBackground"
+                    android:layout_gravity="center_vertical|start"
+                    android:orientation="horizontal"
+                    android:layout_marginEnd="@dimen/internet_dialog_network_layout_margin"
+                    android:layout_marginStart="@dimen/internet_dialog_network_layout_margin"
+                    android:paddingStart="22dp"
+                    android:paddingEnd="22dp">
+
+                    <FrameLayout
+                        android:layout_width="24dp"
+                        android:layout_height="24dp"
+                        android:clickable="false"
+                        android:layout_gravity="center_vertical|start">
+                        <ImageView
+                            android:id="@+id/signal_icon"
+                            android:autoMirrored="true"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_gravity="center"/>
+                    </FrameLayout>
+
+                    <LinearLayout
+                        android:layout_weight="1"
+                        android:id="@+id/mobile_network_list"
+                        android:orientation="vertical"
+                        android:clickable="false"
+                        android:layout_width="wrap_content"
+                        android:layout_height="match_parent"
+                        android:gravity="start|center_vertical">
+                        <TextView
+                            android:id="@+id/mobile_title"
+                            android:textDirection="locale"
+                            android:layout_marginStart="@dimen/internet_dialog_network_layout_margin"
+                            android:layout_marginEnd="7dp"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_gravity="center_vertical|start"
+                            android:ellipsize="end"
+                            android:textColor="?android:attr/textColorPrimary"
+                            android:textSize="16sp"
+                            android:fontFamily="google-sans"/>
+                        <TextView
+                            android:id="@+id/mobile_summary"
+                            android:textDirection="locale"
+                            android:layout_marginStart="@dimen/internet_dialog_network_layout_margin"
+                            android:layout_marginEnd="34dp"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_gravity="center_vertical|start"
+                            android:ellipsize="end"
+                            android:textColor="?android:attr/textColorTertiary"
+                            android:textSize="14sp"
+                            android:fontFamily="google-sans"/>
+                    </LinearLayout>
+
+                    <FrameLayout
+                        android:layout_width="@dimen/settingslib_switch_track_width"
+                        android:layout_height="48dp"
+                        android:layout_gravity="end|center_vertical">
+                        <Switch
+                            android:id="@+id/mobile_toggle"
+                            android:switchMinWidth="@dimen/settingslib_switch_track_width"
+                            android:layout_gravity="center"
+                            android:layout_width="@dimen/settingslib_switch_track_width"
+                            android:layout_height="@dimen/settingslib_switch_track_height"
+                            android:track="@drawable/settingslib_track_selector"
+                            android:thumb="@drawable/settingslib_thumb_selector"
+                            android:theme="@style/MainSwitch.Settingslib"/>
+                    </FrameLayout>
+
+                </LinearLayout>
+
+                <LinearLayout
+                    android:id="@+id/turn_on_wifi_layout"
+                    android:layout_width="match_parent"
+                    android:layout_height="72dp"
+                    android:clickable="true"
+                    android:focusable="true"
+                    android:background="?android:attr/selectableItemBackground"
+                    android:gravity="center"
+                    android:orientation="horizontal"
+                    android:layout_marginEnd="@dimen/internet_dialog_network_layout_margin"
+                    android:layout_marginStart="@dimen/internet_dialog_network_layout_margin"
+                    android:paddingStart="22dp"
+                    android:paddingEnd="22dp">
+
+                    <FrameLayout
+                        android:layout_weight="1"
+                        android:orientation="vertical"
+                        android:clickable="false"
+                        android:layout_width="wrap_content"
+                        android:layout_height="match_parent">
+                        <TextView
+                            android:id="@+id/wifi_toggle_title"
+                            android:text="@string/turn_on_wifi"
+                            android:textDirection="locale"
+                            android:layout_width="wrap_content"
+                            android:layout_height="match_parent"
+                            android:gravity="start|center_vertical"
+                            android:textColor="?android:attr/textColorPrimary"
+                            android:textSize="16sp"
+                            android:fontFamily="google-sans"/>
+                    </FrameLayout>
+
+                    <FrameLayout
+                        android:layout_width="@dimen/settingslib_switch_track_width"
+                        android:layout_height="48dp"
+                        android:layout_marginTop="10dp"
+                        android:layout_marginBottom="10dp">
+                        <Switch
+                            android:id="@+id/wifi_toggle"
+                            android:switchMinWidth="@dimen/settingslib_switch_track_width"
+                            android:layout_gravity="center"
+                            android:layout_width="@dimen/settingslib_switch_track_width"
+                            android:layout_height="@dimen/settingslib_switch_track_height"
+                            android:track="@drawable/settingslib_track_selector"
+                            android:thumb="@drawable/settingslib_thumb_selector"
+                            android:theme="@style/MainSwitch.Settingslib"/>
+                    </FrameLayout>
+
+                </LinearLayout>
+
+                <LinearLayout
+                    android:id="@+id/wifi_connected_layout"
+                    android:layout_width="match_parent"
+                    android:layout_height="72dp"
+                    android:layout_gravity="center_vertical|start"
+                    android:clickable="true"
+                    android:focusable="true"
+                    android:visibility="gone"
+                    android:background="?android:attr/selectableItemBackground"
+                    android:orientation="horizontal"
+                    android:layout_marginStart="@dimen/internet_dialog_network_layout_margin"
+                    android:layout_marginEnd="@dimen/internet_dialog_network_layout_margin"
+                    android:paddingStart="20dp"
+                    android:paddingEnd="24dp">
+
+                    <FrameLayout
+                        android:layout_width="24dp"
+                        android:layout_height="24dp"
+                        android:clickable="false"
+                        android:layout_gravity="center_vertical|start">
+                        <ImageView
+                            android:id="@+id/wifi_connected_icon"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_gravity="center"/>
+                    </FrameLayout>
+
+                    <LinearLayout
+                        android:id="@+id/wifi_connected_list"
+                        android:orientation="vertical"
+                        android:clickable="false"
+                        android:layout_width="wrap_content"
+                        android:layout_height="72dp"
+                        android:layout_marginEnd="30dp"
+                        android:layout_weight="1"
+                        android:gravity="start|center_vertical">
+                        <TextView
+                            android:id="@+id/wifi_connected_title"
+                            android:textDirection="locale"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_gravity="center_vertical|start"
+                            android:layout_marginStart="@dimen/internet_dialog_network_layout_margin"
+                            android:ellipsize="end"
+                            android:textColor="?android:attr/textColorPrimary"
+                            android:textSize="14sp"
+                            android:fontFamily="google-sans"/>
+                        <TextView
+                            android:id="@+id/wifi_connected_summary"
+                            android:textDirection="locale"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_gravity="center_vertical|start"
+                            android:layout_marginStart="@dimen/internet_dialog_network_layout_margin"
+                            android:ellipsize="end"
+                            android:textColor="?android:attr/textColorTertiary"
+                            android:textSize="14sp"
+                            android:fontFamily="google-sans"/>
+                    </LinearLayout>
+
+                    <FrameLayout
+                        android:layout_width="24dp"
+                        android:layout_height="match_parent"
+                        android:clickable="false"
+                        android:layout_gravity="end|center_vertical"
+                        android:gravity="center">
+                        <ImageView
+                            android:id="@+id/wifi_settings_icon"
+                            android:src="@drawable/ic_settings_24dp"
+                            android:layout_width="24dp"
+                            android:layout_gravity="end|center_vertical"
+                            android:layout_height="wrap_content"/>
+                    </FrameLayout>
+
+                </LinearLayout>
+
+                <androidx.recyclerview.widget.RecyclerView
+                    android:id="@+id/wifi_list_layout"
+                    android:scrollbars="vertical"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:overScrollMode="never"
+                    android:nestedScrollingEnabled="false"/>
+
+            </LinearLayout>
+
+            <LinearLayout
+                android:id="@+id/see_all_layout"
+                android:layout_width="match_parent"
+                android:layout_height="64dp"
+                android:clickable="true"
+                android:focusable="true"
+                android:background="?android:attr/selectableItemBackground"
+                android:gravity="center_vertical|center_horizontal"
+                android:orientation="horizontal"
+                android:paddingStart="22dp"
+                android:paddingEnd="22dp">
+
+                <FrameLayout
+                    android:layout_width="24dp"
+                    android:layout_height="24dp"
+                    android:clickable="false"
+                    android:layout_gravity="center_vertical|start"
+                    android:layout_marginStart="@dimen/internet_dialog_network_layout_margin">
+                    <ImageView
+                        android:id="@+id/arrow_forward"
+                        android:src="@drawable/ic_arrow_forward"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_gravity="center"/>
+                </FrameLayout>
+
+                <FrameLayout
+                    android:orientation="vertical"
+                    android:clickable="false"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:layout_marginStart="@dimen/internet_dialog_network_layout_margin">
+                    <TextView
+                        android:text="@string/see_all_networks"
+                        android:textDirection="locale"
+                        android:layout_width="wrap_content"
+                        android:layout_height="match_parent"
+                        android:gravity="start|center_vertical"
+                        android:textColor="?android:attr/textColorPrimary"
+                        android:textSize="14sp"
+                        android:fontFamily="google-sans"/>
+                </FrameLayout>
+
+            </LinearLayout>
+
+            <FrameLayout
+                android:layout_width="match_parent"
+                android:layout_height="48dp"
+                android:layout_marginBottom="40dp">
+                <Button
+                    style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+                    android:id="@+id/done"
+                    android:layout_width="67dp"
+                    android:layout_height="36dp"
+                    android:layout_marginEnd="24dp"
+                    android:layout_gravity="end|center_vertical"
+                    android:background="@drawable/internet_dialog_footer_background"
+                    android:textColor="?android:attr/textColorPrimary"
+                    android:text="@string/inline_done_button"
+                    android:textSize="14sp"
+                    android:fontFamily="google-sans"/>
+            </FrameLayout>
+        </LinearLayout>
+    </androidx.core.widget.NestedScrollView>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/internet_list_item.xml b/packages/SystemUI/res/layout/internet_list_item.xml
new file mode 100644
index 0000000..b52933d
--- /dev/null
+++ b/packages/SystemUI/res/layout/internet_list_item.xml
@@ -0,0 +1,95 @@
+<?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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/internet_container"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:id="@+id/wifi_list"
+        android:layout_width="match_parent"
+        android:layout_height="72dp"
+        android:layout_gravity="center_vertical|start"
+        android:clickable="true"
+        android:focusable="true"
+        android:background="?android:attr/selectableItemBackground"
+        android:orientation="horizontal"
+        android:paddingStart="20dp"
+        android:paddingEnd="40dp">
+        <FrameLayout
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:clickable="false"
+            android:layout_gravity="center_vertical|start"
+            android:layout_marginStart="@dimen/internet_dialog_network_layout_margin">
+            <ImageView
+                android:id="@+id/wifi_icon"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"/>
+        </FrameLayout>
+
+        <LinearLayout
+            android:id="@+id/wifi_network_layout"
+            android:orientation="vertical"
+            android:clickable="false"
+            android:layout_width="wrap_content"
+            android:layout_height="72dp"
+            android:layout_weight="1"
+            android:gravity="start|center_vertical"
+            android:layout_marginStart="@dimen/internet_dialog_network_layout_margin">
+            <TextView
+                android:id="@+id/wifi_title"
+                android:textDirection="locale"
+                android:layout_width="wrap_content"
+                android:layout_height="20dp"
+                android:gravity="start|center_vertical"
+                android:ellipsize="end"
+                android:textColor="?android:attr/textColorPrimary"
+                android:textSize="14sp"
+                android:fontFamily="google-sans"
+                android:layout_marginEnd="18dp"/>
+            <TextView
+                android:id="@+id/wifi_summary"
+                android:textDirection="locale"
+                android:layout_width="wrap_content"
+                android:layout_height="20dp"
+                android:gravity="start|center_vertical"
+                android:ellipsize="end"
+                android:textColor="?android:attr/textColorSecondary"
+                android:textSize="14sp"
+                android:fontFamily="google-sans"
+                android:layout_marginEnd="18dp"/>
+        </LinearLayout>
+
+        <FrameLayout
+            android:layout_width="24dp"
+            android:layout_height="match_parent"
+            android:clickable="false"
+            android:layout_gravity="end|center_vertical">
+            <ImageView
+                android:id="@+id/wifi_locked_icon"
+                android:layout_gravity="end|center_vertical"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"/>
+        </FrameLayout>
+
+    </LinearLayout>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/keyguard_status_bar.xml b/packages/SystemUI/res/layout/keyguard_status_bar.xml
index 2789ed1..eb76382 100644
--- a/packages/SystemUI/res/layout/keyguard_status_bar.xml
+++ b/packages/SystemUI/res/layout/keyguard_status_bar.xml
@@ -30,6 +30,7 @@
         android:id="@+id/status_icon_area"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
+        android:paddingEnd="@dimen/system_icons_keyguard_padding_end"
         android:paddingTop="@dimen/status_bar_padding_top"
         android:layout_alignParentEnd="true"
         android:gravity="center_vertical|end" >
@@ -38,12 +39,10 @@
             android:layout_height="match_parent"
             android:layout_weight="1"
             android:layout_marginStart="@dimen/system_icons_super_container_margin_start"
-            android:gravity="center_vertical|end"
-            android:paddingEnd="@dimen/system_icons_keyguard_padding_end" >
+            android:gravity="center_vertical|end">
             <include layout="@layout/system_icons" />
         </FrameLayout>
 
-
         <ImageView android:id="@+id/multi_user_avatar"
             android:layout_width="@dimen/multi_user_avatar_keyguard_size"
             android:layout_height="@dimen/multi_user_avatar_keyguard_size"
diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml
index c0d353b..075473a 100644
--- a/packages/SystemUI/res/layout/media_view.xml
+++ b/packages/SystemUI/res/layout/media_view.xml
@@ -185,7 +185,7 @@
         android:maxHeight="@dimen/qs_media_enabled_seekbar_height"
         android:paddingTop="@dimen/qs_media_enabled_seekbar_vertical_padding"
         android:layout_marginTop="-22dp"
-        android:paddingBottom="0dp"
+        android:paddingBottom="2dp"
         android:splitTrack="false" />
 
     <!-- Song name -->
diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
index 59e1a75..78655c0 100644
--- a/packages/SystemUI/res/layout/qs_detail.xml
+++ b/packages/SystemUI/res/layout/qs_detail.xml
@@ -20,9 +20,9 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:background="@drawable/qs_detail_background"
+    android:layout_marginTop="@dimen/qs_detail_margin_top"
     android:clickable="true"
     android:orientation="vertical"
-    android:layout_marginTop="@*android:dimen/quick_qs_offset_height"
     android:paddingBottom="8dp"
     android:visibility="invisible"
     android:elevation="4dp"
diff --git a/packages/SystemUI/res/layout/qs_detail_header.xml b/packages/SystemUI/res/layout/qs_detail_header.xml
index da80633..d1ab054 100644
--- a/packages/SystemUI/res/layout/qs_detail_header.xml
+++ b/packages/SystemUI/res/layout/qs_detail_header.xml
@@ -28,7 +28,7 @@
 
     <com.android.systemui.ResizingSpace
         android:layout_width="match_parent"
-        android:layout_height="@dimen/qs_detail_margin_top" />
+        android:layout_height="@dimen/qs_detail_header_margin_top" />
 
     <LinearLayout
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 317dbc0..e70084b 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -68,93 +68,9 @@
 
         </LinearLayout>
 
-        <LinearLayout
-            android:id="@+id/qs_footer_actions_container"
-            android:layout_width="match_parent"
-            android:layout_height="48dp"
-            android:gravity="center_vertical">
+        <include layout="@layout/footer_actions"
+            android:id="@+id/qs_footer_actions"/>
 
-            <com.android.systemui.statusbar.AlphaOptimizedImageView
-                android:id="@android:id/edit"
-                android:layout_width="0dp"
-                android:layout_height="@dimen/qs_footer_action_button_size"
-                android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
-                android:layout_weight="1"
-                android:background="@drawable/qs_footer_action_chip_background"
-                android:clickable="true"
-                android:clipToPadding="false"
-                android:contentDescription="@string/accessibility_quick_settings_edit"
-                android:focusable="true"
-                android:padding="@dimen/qs_footer_icon_padding"
-                android:src="@*android:drawable/ic_mode_edit"
-                android:tint="?android:attr/textColorPrimary" />
-
-            <com.android.systemui.statusbar.phone.MultiUserSwitch
-                android:id="@+id/multi_user_switch"
-                android:layout_width="0dp"
-                android:layout_height="@dimen/qs_footer_action_button_size"
-                android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
-                android:layout_weight="1"
-                android:background="@drawable/qs_footer_action_chip_background"
-                android:focusable="true">
-
-                <ImageView
-                    android:id="@+id/multi_user_avatar"
-                    android:layout_width="@dimen/multi_user_avatar_expanded_size"
-                    android:layout_height="@dimen/multi_user_avatar_expanded_size"
-                    android:layout_gravity="center"
-                    android:scaleType="centerInside" />
-            </com.android.systemui.statusbar.phone.MultiUserSwitch>
-
-            <com.android.systemui.statusbar.AlphaOptimizedImageView
-                android:id="@+id/pm_lite"
-                android:layout_width="0dp"
-                android:layout_height="@dimen/qs_footer_action_button_size"
-                android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
-                android:layout_weight="1"
-                android:background="@drawable/qs_footer_action_chip_background"
-                android:clickable="true"
-                android:clipToPadding="false"
-                android:focusable="true"
-                android:padding="@dimen/qs_footer_icon_padding"
-                android:src="@*android:drawable/ic_lock_power_off"
-                android:contentDescription="@string/accessibility_quick_settings_power_menu"
-                android:tint="?android:attr/textColorPrimary" />
-
-            <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
-                android:id="@+id/settings_button_container"
-                android:layout_width="0dp"
-                android:layout_height="@dimen/qs_footer_action_button_size"
-                android:background="@drawable/qs_footer_action_chip_background"
-                android:layout_weight="1"
-                android:clipChildren="false"
-                android:clipToPadding="false">
-
-                <com.android.systemui.statusbar.phone.SettingsButton
-                    android:id="@+id/settings_button"
-                    android:layout_width="match_parent"
-                    android:layout_height="@dimen/qs_footer_action_button_size"
-                    android:layout_gravity="center"
-                    android:contentDescription="@string/accessibility_quick_settings_settings"
-                    android:background="@drawable/qs_footer_action_chip_background_borderless"
-                    android:padding="@dimen/qs_footer_icon_padding"
-                    android:scaleType="centerInside"
-                    android:src="@drawable/ic_settings"
-                    android:tint="?android:attr/textColorPrimary" />
-
-                <com.android.systemui.statusbar.AlphaOptimizedImageView
-                    android:id="@+id/tuner_icon"
-                    android:layout_width="8dp"
-                    android:layout_height="8dp"
-                    android:layout_gravity="center_horizontal|bottom"
-                    android:layout_marginBottom="@dimen/qs_footer_icon_padding"
-                    android:src="@drawable/tuner"
-                    android:tint="?android:attr/textColorTertiary"
-                    android:visibility="invisible" />
-
-            </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
-
-        </LinearLayout>
     </LinearLayout>
 
 </com.android.systemui.qs.QSFooterView>
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index 74c39a3..fc0d60a 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -26,17 +26,38 @@
     android:focusable="true"
     android:theme="@style/Theme.SystemUI.QuickSettings.Header">
 
-    <com.android.systemui.statusbar.policy.Clock
-        android:id="@+id/clock"
+    <LinearLayout
+        android:id="@+id/clock_container"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
-        android:minWidth="48dp"
-        android:minHeight="@dimen/qs_header_row_min_height"
+        android:orientation="horizontal"
+        android:layout_gravity="center_vertical|start"
         android:gravity="center_vertical|start"
-        android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
-        android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
-        android:singleLine="true"
-        android:textAppearance="@style/TextAppearance.QS.Status" />
+        >
+
+        <com.android.systemui.statusbar.policy.Clock
+            android:id="@+id/clock"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:minWidth="48dp"
+            android:minHeight="@dimen/qs_header_row_min_height"
+            android:gravity="center_vertical|start"
+            android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
+            android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
+            android:singleLine="true"
+            android:textAppearance="@style/TextAppearance.QS.Status" />
+
+        <com.android.systemui.statusbar.policy.VariableDateView
+            android:id="@+id/date_clock"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:gravity="center_vertical|start"
+            android:singleLine="true"
+            android:textAppearance="@style/TextAppearance.QS.Status"
+            systemui:longDatePattern="@string/abbrev_wday_month_day_no_year_alarm"
+            systemui:shortDatePattern="@string/abbrev_month_day_no_year"
+        />
+    </LinearLayout>
 
     <include layout="@layout/qs_carrier_group"
         android:id="@+id/carrier_group"
@@ -76,7 +97,7 @@
             android:layout_height="match_parent"
             android:paddingEnd="@dimen/signal_cluster_battery_padding" />
 
-        <com.android.systemui.BatteryMeterView
+        <com.android.systemui.battery.BatteryMeterView
             android:id="@+id/batteryRemainingIcon"
             android:layout_height="match_parent"
             android:layout_width="0dp"
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index df02730..6b14c96 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -56,7 +56,18 @@
             android:clipToPadding="false"
             android:focusable="true"
             android:paddingBottom="24dp"
-            android:importantForAccessibility="yes" />
+            android:importantForAccessibility="yes">
+
+            <include
+                layout="@layout/footer_actions"
+                android:id="@+id/qqs_footer_actions"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="16dp"
+                android:layout_marginStart="@dimen/qs_footer_margin"
+                android:layout_marginEnd="@dimen/qs_footer_margin"
+                />
+        </com.android.systemui.qs.QuickQSPanel>
     </RelativeLayout>
 
 </com.android.systemui.qs.QuickStatusBarHeader>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml b/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
index bff93a9..cc44b5e 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
@@ -36,7 +36,7 @@
         android:layout_weight="1"
         android:gravity="center_vertical|start" >
 
-        <com.android.systemui.statusbar.policy.DateView
+        <com.android.systemui.statusbar.policy.VariableDateView
             android:id="@+id/date"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
@@ -44,7 +44,9 @@
             android:gravity="center_vertical"
             android:singleLine="true"
             android:textAppearance="@style/TextAppearance.QS.Status"
-            systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" />
+            systemui:longDatePattern="@string/abbrev_wday_month_day_no_year_alarm"
+            systemui:shortDatePattern="@string/abbrev_month_day_no_year"
+        />
     </FrameLayout>
 
     <android.widget.Space
diff --git a/packages/SystemUI/res/layout/split_shade_header.xml b/packages/SystemUI/res/layout/split_shade_header.xml
index 401dc19..f2c5b7b 100644
--- a/packages/SystemUI/res/layout/split_shade_header.xml
+++ b/packages/SystemUI/res/layout/split_shade_header.xml
@@ -78,7 +78,7 @@
                 android:layout_height="match_parent"
                 android:paddingEnd="@dimen/signal_cluster_battery_padding" />
 
-            <com.android.systemui.BatteryMeterView
+            <com.android.systemui.battery.BatteryMeterView
                 android:id="@+id/batteryRemainingIcon"
                 android:layout_width="wrap_content"
                 android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index cf91a2b..d31d1df 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -54,10 +54,23 @@
     <com.android.keyguard.LockIconView
         android:id="@+id/lock_icon_view"
         android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:padding="48px"
-        android:layout_gravity="center"
-        android:scaleType="centerCrop"/>
+        android:layout_height="wrap_content">
+        <!-- Background protection -->
+        <ImageView
+            android:id="@+id/lock_icon_bg"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:background="@drawable/fingerprint_bg"
+            android:visibility="invisible"/>
+
+        <ImageView
+            android:id="@+id/lock_icon"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:padding="@dimen/lock_icon_padding"
+            android:layout_gravity="center"
+            android:scaleType="centerCrop"/>
+    </com.android.keyguard.LockIconView>
 
     <com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer
         android:layout_width="match_parent"
@@ -73,7 +86,7 @@
             layout="@layout/keyguard_status_view"
             android:visibility="gone"/>
 
-        <include layout="@layout/dock_info_overlay" />
+        <include layout="@layout/dock_info_overlay"/>
 
         <FrameLayout
             android:id="@+id/qs_frame"
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index bea50e8..d9a5670 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -62,8 +62,7 @@
     <com.android.systemui.statusbar.LightRevealScrim
             android:id="@+id/light_reveal_scrim"
             android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:visibility="gone" />
+            android:layout_height="match_parent" />
 
     <include layout="@layout/status_bar_expanded"
         android:layout_width="match_parent"
@@ -81,9 +80,10 @@
     />
 
     <!-- Keyguard messages -->
-    <FrameLayout
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
         android:layout_marginTop="@dimen/status_bar_height"
         android:layout_gravity="top|center_horizontal"
         android:gravity="center_horizontal">
@@ -97,7 +97,11 @@
             android:singleLine="true"
             android:ellipsize="marquee"
             android:focusable="true" />
-    </FrameLayout>
+        <FrameLayout android:id="@+id/keyboard_bouncer_container"
+                     android:layout_height="0dp"
+                     android:layout_width="match_parent"
+                     android:layout_weight="1" />
+    </LinearLayout>
 
     <com.android.systemui.biometrics.AuthRippleView
         android:id="@+id/auth_ripple"
diff --git a/packages/SystemUI/res/layout/system_icons.xml b/packages/SystemUI/res/layout/system_icons.xml
index 818d1d7..6d5c7d4 100644
--- a/packages/SystemUI/res/layout/system_icons.xml
+++ b/packages/SystemUI/res/layout/system_icons.xml
@@ -29,7 +29,7 @@
         android:gravity="center_vertical"
         android:orientation="horizontal"/>
 
-    <com.android.systemui.BatteryMeterView android:id="@+id/battery"
+    <com.android.systemui.battery.BatteryMeterView android:id="@+id/battery"
         android:layout_height="match_parent"
         android:layout_width="wrap_content"
         android:clipToPadding="false"
diff --git a/packages/SystemUI/res/layout/udfps_keyguard_view.xml b/packages/SystemUI/res/layout/udfps_keyguard_view.xml
index 8834ac0..a9eb27a 100644
--- a/packages/SystemUI/res/layout/udfps_keyguard_view.xml
+++ b/packages/SystemUI/res/layout/udfps_keyguard_view.xml
@@ -26,8 +26,7 @@
         android:id="@+id/udfps_keyguard_fp_bg"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:background="@drawable/fingerprint_bg"
-        android:visibility="gone"/>
+        android:src="@drawable/fingerprint_bg"/>
 
     <!-- Fingerprint -->
     <!-- AOD dashed fingerprint icon with moving dashes -->
@@ -35,7 +34,7 @@
         android:id="@+id/udfps_aod_fp"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:padding="48px"
+        android:padding="@dimen/lock_icon_padding"
         android:layout_gravity="center"
         android:scaleType="centerCrop"
         app:lottie_autoPlay="false"
@@ -47,7 +46,7 @@
         android:id="@+id/udfps_lockscreen_fp"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:padding="48px"
+        android:padding="@dimen/lock_icon_padding"
         android:layout_gravity="center"
         android:scaleType="centerCrop"
         app:lottie_autoPlay="false"
diff --git a/packages/SystemUI/res/raw/udfps_aod_fp.json b/packages/SystemUI/res/raw/udfps_aod_fp.json
index 3247fe7..51d9058 100644
--- a/packages/SystemUI/res/raw/udfps_aod_fp.json
+++ b/packages/SystemUI/res/raw/udfps_aod_fp.json
@@ -5,7 +5,7 @@
   "op":361,
   "w":46,
   "h":65,
-  "nm":"fingerprint_burn_in_Loop_02",
+  "nm":"fingerprint_burn_in_loop",
   "ddd":0,
   "assets":[
 
@@ -15,368 +15,6 @@
       "ddd":0,
       "ind":2,
       "ty":4,
-      "nm":"Fingerprint_20210701 Outlines 9",
-      "sr":1,
-      "ks":{
-        "o":{
-          "a":0,
-          "k":100,
-          "ix":11
-        },
-        "r":{
-          "a":0,
-          "k":0,
-          "ix":10
-        },
-        "p":{
-          "a":0,
-          "k":[
-            23.091,
-            32.5,
-            0
-          ],
-          "ix":2,
-          "l":2
-        },
-        "a":{
-          "a":0,
-          "k":[
-            19.341,
-            24.25,
-            0
-          ],
-          "ix":1,
-          "l":2
-        },
-        "s":{
-          "a":0,
-          "k":[
-            100,
-            100,
-            100
-          ],
-          "ix":6,
-          "l":2
-        }
-      },
-      "ao":0,
-      "shapes":[
-        {
-          "ty":"gr",
-          "it":[
-            {
-              "ind":0,
-              "ty":"sh",
-              "ix":1,
-              "ks":{
-                "a":0,
-                "k":{
-                  "i":[
-                    [
-                      0,
-                      0
-                    ],
-                    [
-                      -1.701,
-                      0.42
-                    ],
-                    [
-                      -1.757,
-                      0
-                    ],
-                    [
-                      -1.577,
-                      -0.381
-                    ],
-                    [
-                      -1.485,
-                      -0.816
-                    ]
-                  ],
-                  "o":[
-                    [
-                      1.455,
-                      -0.799
-                    ],
-                    [
-                      1.608,
-                      -0.397
-                    ],
-                    [
-                      1.719,
-                      0
-                    ],
-                    [
-                      1.739,
-                      0.42
-                    ],
-                    [
-                      0,
-                      0
-                    ]
-                  ],
-                  "v":[
-                    [
-                      -9.818,
-                      1.227
-                    ],
-                    [
-                      -5.064,
-                      -0.618
-                    ],
-                    [
-                      0,
-                      -1.227
-                    ],
-                    [
-                      4.96,
-                      -0.643
-                    ],
-                    [
-                      9.818,
-                      1.227
-                    ]
-                  ],
-                  "c":false
-                },
-                "ix":2
-              },
-              "nm":"Path 1",
-              "mn":"ADBE Vector Shape - Group",
-              "hd":false
-            },
-            {
-              "ty":"st",
-              "c":{
-                "a":0,
-                "k":[
-                  1,
-                  1,
-                  1,
-                  1
-                ],
-                "ix":3
-              },
-              "o":{
-                "a":0,
-                "k":100,
-                "ix":4
-              },
-              "w":{
-                "a":0,
-                "k":1,
-                "ix":5
-              },
-              "lc":2,
-              "lj":1,
-              "ml":10,
-              "bm":0,
-              "nm":"Stroke 1",
-              "mn":"ADBE Vector Graphic - Stroke",
-              "hd":false
-            },
-            {
-              "ty":"tr",
-              "p":{
-                "a":0,
-                "k":[
-                  19.341,
-                  7.477
-                ],
-                "ix":2
-              },
-              "a":{
-                "a":0,
-                "k":[
-                  0,
-                  0
-                ],
-                "ix":1
-              },
-              "s":{
-                "a":0,
-                "k":[
-                  100,
-                  100
-                ],
-                "ix":3
-              },
-              "r":{
-                "a":0,
-                "k":0,
-                "ix":6
-              },
-              "o":{
-                "a":0,
-                "k":100,
-                "ix":7
-              },
-              "sk":{
-                "a":0,
-                "k":0,
-                "ix":4
-              },
-              "sa":{
-                "a":0,
-                "k":0,
-                "ix":5
-              },
-              "nm":"Transform"
-            }
-          ],
-          "nm":"Top",
-          "np":2,
-          "cix":2,
-          "bm":0,
-          "ix":1,
-          "mn":"ADBE Vector Group",
-          "hd":false
-        },
-        {
-          "ty":"tm",
-          "s":{
-            "a":0,
-            "k":0,
-            "ix":1
-          },
-          "e":{
-            "a":0,
-            "k":17,
-            "ix":2
-          },
-          "o":{
-            "a":1,
-            "k":[
-              {
-                "i":{
-                  "x":[
-                    0.833
-                  ],
-                  "y":[
-                    0.833
-                  ]
-                },
-                "o":{
-                  "x":[
-                    0.167
-                  ],
-                  "y":[
-                    0.167
-                  ]
-                },
-                "t":0,
-                "s":[
-                  246
-                ]
-              },
-              {
-                "t":360,
-                "s":[
-                  1326
-                ]
-              }
-            ],
-            "ix":3
-          },
-          "m":1,
-          "ix":2,
-          "nm":"Trim Paths 1",
-          "mn":"ADBE Vector Filter - Trim",
-          "hd":false
-        },
-        {
-          "ty":"gr",
-          "it":[
-            {
-              "ty":"tm",
-              "s":{
-                "a":0,
-                "k":0,
-                "ix":1
-              },
-              "e":{
-                "a":0,
-                "k":17,
-                "ix":2
-              },
-              "o":{
-                "a":0,
-                "k":0,
-                "ix":3
-              },
-              "m":1,
-              "ix":1,
-              "nm":"Trim Paths 1",
-              "mn":"ADBE Vector Filter - Trim",
-              "hd":false
-            },
-            {
-              "ty":"tr",
-              "p":{
-                "a":0,
-                "k":[
-                  0,
-                  0
-                ],
-                "ix":2
-              },
-              "a":{
-                "a":0,
-                "k":[
-                  0,
-                  0
-                ],
-                "ix":1
-              },
-              "s":{
-                "a":0,
-                "k":[
-                  100,
-                  100
-                ],
-                "ix":3
-              },
-              "r":{
-                "a":0,
-                "k":0,
-                "ix":6
-              },
-              "o":{
-                "a":0,
-                "k":100,
-                "ix":7
-              },
-              "sk":{
-                "a":0,
-                "k":0,
-                "ix":4
-              },
-              "sa":{
-                "a":0,
-                "k":0,
-                "ix":5
-              },
-              "nm":"Transform"
-            }
-          ],
-          "nm":"Group 1",
-          "np":1,
-          "cix":2,
-          "bm":0,
-          "ix":3,
-          "mn":"ADBE Vector Group",
-          "hd":false
-        }
-      ],
-      "ip":0,
-      "op":600,
-      "st":0,
-      "bm":0
-    },
-    {
-      "ddd":0,
-      "ind":3,
-      "ty":4,
       "nm":"Fingerprint_20210701 Outlines 8",
       "sr":1,
       "ks":{
@@ -526,13 +164,62 @@
               },
               "w":{
                 "a":0,
-                "k":1,
+                "k":1.3,
                 "ix":5
               },
               "lc":2,
               "lj":1,
               "ml":10,
               "bm":0,
+              "d":[
+                {
+                  "n":"d",
+                  "nm":"dash",
+                  "v":{
+                    "a":0,
+                    "k":3,
+                    "ix":1
+                  }
+                },
+                {
+                  "n":"o",
+                  "nm":"offset",
+                  "v":{
+                    "a":1,
+                    "k":[
+                      {
+                        "i":{
+                          "x":[
+                            0.833
+                          ],
+                          "y":[
+                            0.833
+                          ]
+                        },
+                        "o":{
+                          "x":[
+                            0.167
+                          ],
+                          "y":[
+                            0.167
+                          ]
+                        },
+                        "t":0,
+                        "s":[
+                          0
+                        ]
+                      },
+                      {
+                        "t":360,
+                        "s":[
+                          -6
+                        ]
+                      }
+                    ],
+                    "ix":7
+                  }
+                }
+              ],
               "nm":"Stroke 1",
               "mn":"ADBE Vector Graphic - Stroke",
               "hd":false
@@ -603,41 +290,12 @@
           },
           "e":{
             "a":0,
-            "k":54,
+            "k":100,
             "ix":2
           },
           "o":{
-            "a":1,
-            "k":[
-              {
-                "i":{
-                  "x":[
-                    0.833
-                  ],
-                  "y":[
-                    0.833
-                  ]
-                },
-                "o":{
-                  "x":[
-                    0.167
-                  ],
-                  "y":[
-                    0.167
-                  ]
-                },
-                "t":0,
-                "s":[
-                  0
-                ]
-              },
-              {
-                "t":360,
-                "s":[
-                  1080
-                ]
-              }
-            ],
+            "a":0,
+            "k":0,
             "ix":3
           },
           "m":1,
@@ -645,257 +303,6 @@
           "nm":"Trim Paths 1",
           "mn":"ADBE Vector Filter - Trim",
           "hd":false
-        }
-      ],
-      "ip":0,
-      "op":600,
-      "st":0,
-      "bm":0
-    },
-    {
-      "ddd":0,
-      "ind":4,
-      "ty":4,
-      "nm":"Fingerprint_20210701 Outlines 7",
-      "sr":1,
-      "ks":{
-        "o":{
-          "a":0,
-          "k":100,
-          "ix":11
-        },
-        "r":{
-          "a":0,
-          "k":0,
-          "ix":10
-        },
-        "p":{
-          "a":0,
-          "k":[
-            23.091,
-            32.5,
-            0
-          ],
-          "ix":2,
-          "l":2
-        },
-        "a":{
-          "a":0,
-          "k":[
-            19.341,
-            24.25,
-            0
-          ],
-          "ix":1,
-          "l":2
-        },
-        "s":{
-          "a":0,
-          "k":[
-            100,
-            100,
-            100
-          ],
-          "ix":6,
-          "l":2
-        }
-      },
-      "ao":0,
-      "shapes":[
-        {
-          "ty":"gr",
-          "it":[
-            {
-              "ind":0,
-              "ty":"sh",
-              "ix":1,
-              "ks":{
-                "a":0,
-                "k":{
-                  "i":[
-                    [
-                      0,
-                      0
-                    ],
-                    [
-                      -2.446,
-                      1.161
-                    ],
-                    [
-                      -1.168,
-                      0.275
-                    ],
-                    [
-                      -1.439,
-                      0
-                    ],
-                    [
-                      -1.301,
-                      -0.304
-                    ],
-                    [
-                      -1.225,
-                      -0.66
-                    ],
-                    [
-                      -1.11,
-                      -1.844
-                    ]
-                  ],
-                  "o":[
-                    [
-                      1.23,
-                      -2.044
-                    ],
-                    [
-                      1.024,
-                      -0.486
-                    ],
-                    [
-                      1.312,
-                      -0.31
-                    ],
-                    [
-                      1.425,
-                      0
-                    ],
-                    [
-                      1.454,
-                      0.34
-                    ],
-                    [
-                      2.122,
-                      1.143
-                    ],
-                    [
-                      0,
-                      0
-                    ]
-                  ],
-                  "v":[
-                    [
-                      -13.091,
-                      3.273
-                    ],
-                    [
-                      -7.438,
-                      -1.646
-                    ],
-                    [
-                      -4.14,
-                      -2.797
-                    ],
-                    [
-                      0,
-                      -3.273
-                    ],
-                    [
-                      4.104,
-                      -2.805
-                    ],
-                    [
-                      8.141,
-                      -1.29
-                    ],
-                    [
-                      13.091,
-                      3.273
-                    ]
-                  ],
-                  "c":false
-                },
-                "ix":2
-              },
-              "nm":"Path 1",
-              "mn":"ADBE Vector Shape - Group",
-              "hd":false
-            },
-            {
-              "ty":"st",
-              "c":{
-                "a":0,
-                "k":[
-                  1,
-                  1,
-                  1,
-                  1
-                ],
-                "ix":3
-              },
-              "o":{
-                "a":0,
-                "k":100,
-                "ix":4
-              },
-              "w":{
-                "a":0,
-                "k":1,
-                "ix":5
-              },
-              "lc":2,
-              "lj":1,
-              "ml":10,
-              "bm":0,
-              "nm":"Stroke 1",
-              "mn":"ADBE Vector Graphic - Stroke",
-              "hd":false
-            },
-            {
-              "ty":"tr",
-              "p":{
-                "a":0,
-                "k":[
-                  19.341,
-                  16.069
-                ],
-                "ix":2
-              },
-              "a":{
-                "a":0,
-                "k":[
-                  0,
-                  0
-                ],
-                "ix":1
-              },
-              "s":{
-                "a":0,
-                "k":[
-                  100,
-                  100
-                ],
-                "ix":3
-              },
-              "r":{
-                "a":0,
-                "k":0,
-                "ix":6
-              },
-              "o":{
-                "a":0,
-                "k":100,
-                "ix":7
-              },
-              "sk":{
-                "a":0,
-                "k":0,
-                "ix":4
-              },
-              "sa":{
-                "a":0,
-                "k":0,
-                "ix":5
-              },
-              "nm":"Transform"
-            }
-          ],
-          "nm":"Mid Top",
-          "np":2,
-          "cix":2,
-          "bm":0,
-          "ix":1,
-          "mn":"ADBE Vector Group",
-          "hd":false
         },
         {
           "ty":"tm",
@@ -906,46 +313,17 @@
           },
           "e":{
             "a":0,
-            "k":38.2,
+            "k":100,
             "ix":2
           },
           "o":{
-            "a":1,
-            "k":[
-              {
-                "i":{
-                  "x":[
-                    0.833
-                  ],
-                  "y":[
-                    0.833
-                  ]
-                },
-                "o":{
-                  "x":[
-                    0.167
-                  ],
-                  "y":[
-                    0.167
-                  ]
-                },
-                "t":0,
-                "s":[
-                  170
-                ]
-              },
-              {
-                "t":360,
-                "s":[
-                  890
-                ]
-              }
-            ],
+            "a":0,
+            "k":0,
             "ix":3
           },
           "m":1,
-          "ix":2,
-          "nm":"Trim Paths 1",
+          "ix":3,
+          "nm":"Trim Paths 2",
           "mn":"ADBE Vector Filter - Trim",
           "hd":false
         }
@@ -957,7 +335,7 @@
     },
     {
       "ddd":0,
-      "ind":5,
+      "ind":3,
       "ty":4,
       "nm":"Fingerprint_20210701 Outlines 6",
       "sr":1,
@@ -1132,13 +510,62 @@
               },
               "w":{
                 "a":0,
-                "k":1,
+                "k":1.3,
                 "ix":5
               },
               "lc":2,
               "lj":1,
               "ml":10,
               "bm":0,
+              "d":[
+                {
+                  "n":"d",
+                  "nm":"dash",
+                  "v":{
+                    "a":0,
+                    "k":3,
+                    "ix":1
+                  }
+                },
+                {
+                  "n":"o",
+                  "nm":"offset",
+                  "v":{
+                    "a":1,
+                    "k":[
+                      {
+                        "i":{
+                          "x":[
+                            0.833
+                          ],
+                          "y":[
+                            0.833
+                          ]
+                        },
+                        "o":{
+                          "x":[
+                            0.167
+                          ],
+                          "y":[
+                            0.167
+                          ]
+                        },
+                        "t":0,
+                        "s":[
+                          0
+                        ]
+                      },
+                      {
+                        "t":360,
+                        "s":[
+                          -6
+                        ]
+                      }
+                    ],
+                    "ix":7
+                  }
+                }
+              ],
               "nm":"Stroke 1",
               "mn":"ADBE Vector Graphic - Stroke",
               "hd":false
@@ -1209,41 +636,12 @@
           },
           "e":{
             "a":0,
-            "k":34.2,
+            "k":100,
             "ix":2
           },
           "o":{
-            "a":1,
-            "k":[
-              {
-                "i":{
-                  "x":[
-                    0.833
-                  ],
-                  "y":[
-                    0.833
-                  ]
-                },
-                "o":{
-                  "x":[
-                    0.167
-                  ],
-                  "y":[
-                    0.167
-                  ]
-                },
-                "t":0,
-                "s":[
-                  0
-                ]
-              },
-              {
-                "t":360,
-                "s":[
-                  720
-                ]
-              }
-            ],
+            "a":0,
+            "k":0,
             "ix":3
           },
           "m":1,
@@ -1260,7 +658,7 @@
     },
     {
       "ddd":0,
-      "ind":6,
+      "ind":4,
       "ty":4,
       "nm":"Fingerprint_20210701 Outlines 5",
       "sr":1,
@@ -1507,13 +905,62 @@
               },
               "w":{
                 "a":0,
-                "k":1,
+                "k":1.3,
                 "ix":5
               },
               "lc":2,
               "lj":1,
               "ml":10,
               "bm":0,
+              "d":[
+                {
+                  "n":"d",
+                  "nm":"dash",
+                  "v":{
+                    "a":0,
+                    "k":3,
+                    "ix":1
+                  }
+                },
+                {
+                  "n":"o",
+                  "nm":"offset",
+                  "v":{
+                    "a":1,
+                    "k":[
+                      {
+                        "i":{
+                          "x":[
+                            0.833
+                          ],
+                          "y":[
+                            0.833
+                          ]
+                        },
+                        "o":{
+                          "x":[
+                            0.167
+                          ],
+                          "y":[
+                            0.167
+                          ]
+                        },
+                        "t":0,
+                        "s":[
+                          0
+                        ]
+                      },
+                      {
+                        "t":360,
+                        "s":[
+                          -6
+                        ]
+                      }
+                    ],
+                    "ix":7
+                  }
+                }
+              ],
               "nm":"Stroke 1",
               "mn":"ADBE Vector Graphic - Stroke",
               "hd":false
@@ -1584,416 +1031,12 @@
           },
           "e":{
             "a":0,
-            "k":35,
+            "k":100,
             "ix":2
           },
           "o":{
-            "a":1,
-            "k":[
-              {
-                "i":{
-                  "x":[
-                    0.833
-                  ],
-                  "y":[
-                    0.833
-                  ]
-                },
-                "o":{
-                  "x":[
-                    0.167
-                  ],
-                  "y":[
-                    0.167
-                  ]
-                },
-                "t":0,
-                "s":[
-                  -159
-                ]
-              },
-              {
-                "t":360,
-                "s":[
-                  201
-                ]
-              }
-            ],
-            "ix":3
-          },
-          "m":1,
-          "ix":2,
-          "nm":"Trim Paths 1",
-          "mn":"ADBE Vector Filter - Trim",
-          "hd":false
-        }
-      ],
-      "ip":0,
-      "op":600,
-      "st":0,
-      "bm":0
-    },
-    {
-      "ddd":0,
-      "ind":7,
-      "ty":4,
-      "nm":"Fingerprint_20210701 Outlines 4",
-      "sr":1,
-      "ks":{
-        "o":{
-          "a":0,
-          "k":100,
-          "ix":11
-        },
-        "r":{
-          "a":0,
-          "k":0,
-          "ix":10
-        },
-        "p":{
-          "a":0,
-          "k":[
-            23.091,
-            32.5,
-            0
-          ],
-          "ix":2,
-          "l":2
-        },
-        "a":{
-          "a":0,
-          "k":[
-            19.341,
-            24.25,
-            0
-          ],
-          "ix":1,
-          "l":2
-        },
-        "s":{
-          "a":0,
-          "k":[
-            100,
-            100,
-            100
-          ],
-          "ix":6,
-          "l":2
-        }
-      },
-      "ao":0,
-      "shapes":[
-        {
-          "ty":"gr",
-          "it":[
-            {
-              "ind":0,
-              "ty":"sh",
-              "ix":1,
-              "ks":{
-                "a":0,
-                "k":{
-                  "i":[
-                    [
-                      0,
-                      0
-                    ],
-                    [
-                      0,
-                      0
-                    ],
-                    [
-                      -6.53,
-                      0
-                    ],
-                    [
-                      0,
-                      -5.793
-                    ],
-                    [
-                      0,
-                      0
-                    ],
-                    [
-                      2.159,
-                      0
-                    ],
-                    [
-                      0.59,
-                      1.489
-                    ],
-                    [
-                      0,
-                      0
-                    ],
-                    [
-                      1.587,
-                      0
-                    ],
-                    [
-                      0,
-                      -2.16
-                    ],
-                    [
-                      -0.81,
-                      -1.363
-                    ],
-                    [
-                      -0.844,
-                      -0.674
-                    ],
-                    [
-                      0,
-                      0
-                    ]
-                  ],
-                  "o":[
-                    [
-                      -0.753,
-                      -2.095
-                    ],
-                    [
-                      0,
-                      -5.793
-                    ],
-                    [
-                      6.529,
-                      0
-                    ],
-                    [
-                      0,
-                      0
-                    ],
-                    [
-                      0,
-                      2.16
-                    ],
-                    [
-                      -1.604,
-                      0
-                    ],
-                    [
-                      0,
-                      0
-                    ],
-                    [
-                      -0.589,
-                      -1.489
-                    ],
-                    [
-                      -2.161,
-                      0
-                    ],
-                    [
-                      0,
-                      1.62
-                    ],
-                    [
-                      0.54,
-                      0.909
-                    ],
-                    [
-                      0,
-                      0
-                    ],
-                    [
-                      0,
-                      0
-                    ]
-                  ],
-                  "v":[
-                    [
-                      -10.702,
-                      5.728
-                    ],
-                    [
-                      -11.454,
-                      1.506
-                    ],
-                    [
-                      0.001,
-                      -9
-                    ],
-                    [
-                      11.454,
-                      1.506
-                    ],
-                    [
-                      11.454,
-                      1.817
-                    ],
-                    [
-                      7.544,
-                      5.728
-                    ],
-                    [
-                      3.926,
-                      3.273
-                    ],
-                    [
-                      2.618,
-                      0
-                    ],
-                    [
-                      -0.997,
-                      -2.454
-                    ],
-                    [
-                      -4.91,
-                      1.457
-                    ],
-                    [
-                      -3.657,
-                      6.014
-                    ],
-                    [
-                      -1.57,
-                      8.412
-                    ],
-                    [
-                      -0.818,
-                      9
-                    ]
-                  ],
-                  "c":false
-                },
-                "ix":2
-              },
-              "nm":"Path 1",
-              "mn":"ADBE Vector Shape - Group",
-              "hd":false
-            },
-            {
-              "ty":"st",
-              "c":{
-                "a":0,
-                "k":[
-                  1,
-                  1,
-                  1,
-                  1
-                ],
-                "ix":3
-              },
-              "o":{
-                "a":0,
-                "k":100,
-                "ix":4
-              },
-              "w":{
-                "a":0,
-                "k":1,
-                "ix":5
-              },
-              "lc":2,
-              "lj":1,
-              "ml":10,
-              "bm":0,
-              "nm":"Stroke 1",
-              "mn":"ADBE Vector Graphic - Stroke",
-              "hd":false
-            },
-            {
-              "ty":"tr",
-              "p":{
-                "a":0,
-                "k":[
-                  19.341,
-                  28.341
-                ],
-                "ix":2
-              },
-              "a":{
-                "a":0,
-                "k":[
-                  0,
-                  0
-                ],
-                "ix":1
-              },
-              "s":{
-                "a":0,
-                "k":[
-                  100,
-                  100
-                ],
-                "ix":3
-              },
-              "r":{
-                "a":0,
-                "k":0,
-                "ix":6
-              },
-              "o":{
-                "a":0,
-                "k":100,
-                "ix":7
-              },
-              "sk":{
-                "a":0,
-                "k":0,
-                "ix":4
-              },
-              "sa":{
-                "a":0,
-                "k":0,
-                "ix":5
-              },
-              "nm":"Transform"
-            }
-          ],
-          "nm":"Inside to dot ",
-          "np":2,
-          "cix":2,
-          "bm":0,
-          "ix":1,
-          "mn":"ADBE Vector Group",
-          "hd":false
-        },
-        {
-          "ty":"tm",
-          "s":{
             "a":0,
             "k":0,
-            "ix":1
-          },
-          "e":{
-            "a":0,
-            "k":9,
-            "ix":2
-          },
-          "o":{
-            "a":1,
-            "k":[
-              {
-                "i":{
-                  "x":[
-                    0.833
-                  ],
-                  "y":[
-                    0.833
-                  ]
-                },
-                "o":{
-                  "x":[
-                    0.167
-                  ],
-                  "y":[
-                    0.167
-                  ]
-                },
-                "t":0,
-                "s":[
-                  135
-                ]
-              },
-              {
-                "t":360,
-                "s":[
-                  495
-                ]
-              }
-            ],
             "ix":3
           },
           "m":1,
@@ -2010,382 +1053,7 @@
     },
     {
       "ddd":0,
-      "ind":8,
-      "ty":4,
-      "nm":"Fingerprint_20210701 Outlines 3",
-      "sr":1,
-      "ks":{
-        "o":{
-          "a":0,
-          "k":100,
-          "ix":11
-        },
-        "r":{
-          "a":0,
-          "k":0,
-          "ix":10
-        },
-        "p":{
-          "a":0,
-          "k":[
-            23.091,
-            32.5,
-            0
-          ],
-          "ix":2,
-          "l":2
-        },
-        "a":{
-          "a":0,
-          "k":[
-            19.341,
-            24.25,
-            0
-          ],
-          "ix":1,
-          "l":2
-        },
-        "s":{
-          "a":0,
-          "k":[
-            100,
-            100,
-            100
-          ],
-          "ix":6,
-          "l":2
-        }
-      },
-      "ao":0,
-      "shapes":[
-        {
-          "ty":"gr",
-          "it":[
-            {
-              "ind":0,
-              "ty":"sh",
-              "ix":1,
-              "ks":{
-                "a":0,
-                "k":{
-                  "i":[
-                    [
-                      0,
-                      0
-                    ],
-                    [
-                      0,
-                      0
-                    ],
-                    [
-                      -6.53,
-                      0
-                    ],
-                    [
-                      0,
-                      -5.793
-                    ],
-                    [
-                      0,
-                      0
-                    ],
-                    [
-                      2.159,
-                      0
-                    ],
-                    [
-                      0.59,
-                      1.489
-                    ],
-                    [
-                      0,
-                      0
-                    ],
-                    [
-                      1.587,
-                      0
-                    ],
-                    [
-                      0,
-                      -2.16
-                    ],
-                    [
-                      -0.81,
-                      -1.363
-                    ],
-                    [
-                      -0.844,
-                      -0.674
-                    ],
-                    [
-                      0,
-                      0
-                    ]
-                  ],
-                  "o":[
-                    [
-                      -0.753,
-                      -2.095
-                    ],
-                    [
-                      0,
-                      -5.793
-                    ],
-                    [
-                      6.529,
-                      0
-                    ],
-                    [
-                      0,
-                      0
-                    ],
-                    [
-                      0,
-                      2.16
-                    ],
-                    [
-                      -1.604,
-                      0
-                    ],
-                    [
-                      0,
-                      0
-                    ],
-                    [
-                      -0.589,
-                      -1.489
-                    ],
-                    [
-                      -2.161,
-                      0
-                    ],
-                    [
-                      0,
-                      1.62
-                    ],
-                    [
-                      0.54,
-                      0.909
-                    ],
-                    [
-                      0,
-                      0
-                    ],
-                    [
-                      0,
-                      0
-                    ]
-                  ],
-                  "v":[
-                    [
-                      -10.702,
-                      5.728
-                    ],
-                    [
-                      -11.454,
-                      1.506
-                    ],
-                    [
-                      0.001,
-                      -9
-                    ],
-                    [
-                      11.454,
-                      1.506
-                    ],
-                    [
-                      11.454,
-                      1.817
-                    ],
-                    [
-                      7.544,
-                      5.728
-                    ],
-                    [
-                      3.926,
-                      3.273
-                    ],
-                    [
-                      2.618,
-                      0
-                    ],
-                    [
-                      -0.997,
-                      -2.454
-                    ],
-                    [
-                      -4.91,
-                      1.457
-                    ],
-                    [
-                      -3.657,
-                      6.014
-                    ],
-                    [
-                      -1.57,
-                      8.412
-                    ],
-                    [
-                      -0.818,
-                      9
-                    ]
-                  ],
-                  "c":false
-                },
-                "ix":2
-              },
-              "nm":"Path 1",
-              "mn":"ADBE Vector Shape - Group",
-              "hd":false
-            },
-            {
-              "ty":"st",
-              "c":{
-                "a":0,
-                "k":[
-                  1,
-                  1,
-                  1,
-                  1
-                ],
-                "ix":3
-              },
-              "o":{
-                "a":0,
-                "k":100,
-                "ix":4
-              },
-              "w":{
-                "a":0,
-                "k":1,
-                "ix":5
-              },
-              "lc":2,
-              "lj":1,
-              "ml":10,
-              "bm":0,
-              "nm":"Stroke 1",
-              "mn":"ADBE Vector Graphic - Stroke",
-              "hd":false
-            },
-            {
-              "ty":"tr",
-              "p":{
-                "a":0,
-                "k":[
-                  19.341,
-                  28.341
-                ],
-                "ix":2
-              },
-              "a":{
-                "a":0,
-                "k":[
-                  0,
-                  0
-                ],
-                "ix":1
-              },
-              "s":{
-                "a":0,
-                "k":[
-                  100,
-                  100
-                ],
-                "ix":3
-              },
-              "r":{
-                "a":0,
-                "k":0,
-                "ix":6
-              },
-              "o":{
-                "a":0,
-                "k":100,
-                "ix":7
-              },
-              "sk":{
-                "a":0,
-                "k":0,
-                "ix":4
-              },
-              "sa":{
-                "a":0,
-                "k":0,
-                "ix":5
-              },
-              "nm":"Transform"
-            }
-          ],
-          "nm":"Inside to dot ",
-          "np":2,
-          "cix":2,
-          "bm":0,
-          "ix":1,
-          "mn":"ADBE Vector Group",
-          "hd":false
-        },
-        {
-          "ty":"tm",
-          "s":{
-            "a":0,
-            "k":0,
-            "ix":1
-          },
-          "e":{
-            "a":0,
-            "k":30,
-            "ix":2
-          },
-          "o":{
-            "a":1,
-            "k":[
-              {
-                "i":{
-                  "x":[
-                    0.833
-                  ],
-                  "y":[
-                    0.833
-                  ]
-                },
-                "o":{
-                  "x":[
-                    0.167
-                  ],
-                  "y":[
-                    0.167
-                  ]
-                },
-                "t":0,
-                "s":[
-                  0
-                ]
-              },
-              {
-                "t":360,
-                "s":[
-                  360
-                ]
-              }
-            ],
-            "ix":3
-          },
-          "m":1,
-          "ix":2,
-          "nm":"Trim Paths 1",
-          "mn":"ADBE Vector Filter - Trim",
-          "hd":false
-        }
-      ],
-      "ip":0,
-      "op":600,
-      "st":0,
-      "bm":0
-    },
-    {
-      "ddd":0,
-      "ind":9,
+      "ind":5,
       "ty":4,
       "nm":"Fingerprint_20210701 Outlines",
       "sr":1,
@@ -2560,13 +1228,62 @@
               },
               "w":{
                 "a":0,
-                "k":1,
+                "k":1.3,
                 "ix":5
               },
               "lc":2,
               "lj":1,
               "ml":10,
               "bm":0,
+              "d":[
+                {
+                  "n":"d",
+                  "nm":"dash",
+                  "v":{
+                    "a":0,
+                    "k":2.5,
+                    "ix":1
+                  }
+                },
+                {
+                  "n":"o",
+                  "nm":"offset",
+                  "v":{
+                    "a":1,
+                    "k":[
+                      {
+                        "i":{
+                          "x":[
+                            0.833
+                          ],
+                          "y":[
+                            0.833
+                          ]
+                        },
+                        "o":{
+                          "x":[
+                            0.167
+                          ],
+                          "y":[
+                            0.167
+                          ]
+                        },
+                        "t":0,
+                        "s":[
+                          0
+                        ]
+                      },
+                      {
+                        "t":360,
+                        "s":[
+                          -5
+                        ]
+                      }
+                    ],
+                    "ix":7
+                  }
+                }
+              ],
               "nm":"Stroke 1",
               "mn":"ADBE Vector Graphic - Stroke",
               "hd":false
@@ -2637,41 +1354,12 @@
           },
           "e":{
             "a":0,
-            "k":69,
+            "k":100,
             "ix":2
           },
           "o":{
-            "a":1,
-            "k":[
-              {
-                "i":{
-                  "x":[
-                    0.833
-                  ],
-                  "y":[
-                    0.833
-                  ]
-                },
-                "o":{
-                  "x":[
-                    0.167
-                  ],
-                  "y":[
-                    0.167
-                  ]
-                },
-                "t":0,
-                "s":[
-                  0
-                ]
-              },
-              {
-                "t":360,
-                "s":[
-                  720
-                ]
-              }
-            ],
+            "a":0,
+            "k":0,
             "ix":3
           },
           "m":1,
@@ -2699,4 +1387,4 @@
       "dr":0
     }
   ]
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/res/raw/udfps_lockscreen_fp.json b/packages/SystemUI/res/raw/udfps_lockscreen_fp.json
index a25a475..a30a03a 100644
--- a/packages/SystemUI/res/raw/udfps_lockscreen_fp.json
+++ b/packages/SystemUI/res/raw/udfps_lockscreen_fp.json
@@ -1 +1 @@
-{"v":"5.7.8","fr":60,"ip":0,"op":46,"w":46,"h":65,"nm":"fingerprint_build_on","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Fingerprint_20210701 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[23.091,32.5,0],"ix":2,"l":2},"a":{"a":0,"k":[19.341,24.25,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-1.701,0.42],[-1.757,0],[-1.577,-0.381],[-1.485,-0.816]],"o":[[1.455,-0.799],[1.608,-0.397],[1.719,0],[1.739,0.42],[0,0]],"v":[[-9.818,1.227],[-5.064,-0.618],[0,-1.227],[4.96,-0.643],[9.818,1.227]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.717999985639,0.948999980852,0.62400004069,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":24,"s":[2.5]}],"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,7.477],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Top","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-2.446,1.161],[-1.168,0.275],[-1.439,0],[-1.301,-0.304],[-1.225,-0.66],[-1.11,-1.844]],"o":[[1.23,-2.044],[1.024,-0.486],[1.312,-0.31],[1.425,0],[1.454,0.34],[2.122,1.143],[0,0]],"v":[[-13.091,3.273],[-7.438,-1.646],[-4.14,-2.797],[0,-3.273],[4.104,-2.805],[8.141,-1.29],[13.091,3.273]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.717999985639,0.948999980852,0.62400004069,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":24,"s":[2.5]}],"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,16.069],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Mid Top","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-6.53,0],[0,-5.793],[0,0],[2.159,0],[0.59,1.489],[0,0],[1.587,0],[0,-2.16],[-0.81,-1.363],[-0.844,-0.674],[0,0]],"o":[[-0.753,-2.095],[0,-5.793],[6.529,0],[0,0],[0,2.16],[-1.604,0],[0,0],[-0.589,-1.489],[-2.161,0],[0,1.62],[0.54,0.909],[0,0],[0,0]],"v":[[-10.702,5.728],[-11.454,1.506],[0.001,-9],[11.454,1.506],[11.454,1.817],[7.544,5.728],[3.926,3.273],[2.618,0],[-0.997,-2.454],[-4.91,1.457],[-3.657,6.014],[-1.57,8.412],[-0.818,9]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.717999994755,0.949000000954,0.624000012875,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":24,"s":[2.5]}],"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,28.341],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Inside to dot ","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.307,-0.561],[0.894,-0.16],[0.706,0],[0.844,0.193],[0.728,0.334],[0.967,0.901]],"o":[[-1.038,0.967],[-0.817,0.351],[-0.673,0.12],[-0.9,0],[-0.794,-0.182],[-1.203,-0.551],[0,0]],"v":[[8.182,-1.636],[4.642,0.681],[2.07,1.453],[-0.001,1.636],[-2.621,1.341],[-4.909,0.563],[-8.182,-1.636]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.717999985639,0.948999980852,0.62400004069,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":24,"s":[2.5]}],"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,40.614],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Bottom","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[{"tm":210,"cm":"2","dr":0},{"tm":255,"cm":"1","dr":0}]}
\ No newline at end of file
+{"v":"5.7.8","fr":60,"ip":0,"op":46,"w":46,"h":65,"nm":"fingerprint_build_on","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Fingerprint_20210701 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[23.091,32.5,0],"ix":2,"l":2},"a":{"a":0,"k":[19.341,24.25,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-1.701,0.42],[-1.757,0],[-1.577,-0.381],[-1.485,-0.816]],"o":[[1.455,-0.799],[1.608,-0.397],[1.719,0],[1.739,0.42],[0,0]],"v":[[-9.818,1.227],[-5.064,-0.618],[0,-1.227],[4.96,-0.643],[9.818,1.227]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.717999985639,0.948999980852,0.62400004069,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":24,"s":[2]}],"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,7.477],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Top","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-2.446,1.161],[-1.168,0.275],[-1.439,0],[-1.301,-0.304],[-1.225,-0.66],[-1.11,-1.844]],"o":[[1.23,-2.044],[1.024,-0.486],[1.312,-0.31],[1.425,0],[1.454,0.34],[2.122,1.143],[0,0]],"v":[[-13.091,3.273],[-7.438,-1.646],[-4.14,-2.797],[0,-3.273],[4.104,-2.805],[8.141,-1.29],[13.091,3.273]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.717999985639,0.948999980852,0.62400004069,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":24,"s":[2]}],"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,16.069],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Mid Top","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[-6.53,0],[0,-5.793],[0,0],[2.159,0],[0.59,1.489],[0,0],[1.587,0],[0,-2.16],[-0.81,-1.363],[-0.844,-0.674],[0,0]],"o":[[-0.753,-2.095],[0,-5.793],[6.529,0],[0,0],[0,2.16],[-1.604,0],[0,0],[-0.589,-1.489],[-2.161,0],[0,1.62],[0.54,0.909],[0,0],[0,0]],"v":[[-10.702,5.728],[-11.454,1.506],[0.001,-9],[11.454,1.506],[11.454,1.817],[7.544,5.728],[3.926,3.273],[2.618,0],[-0.997,-2.454],[-4.91,1.457],[-3.657,6.014],[-1.57,8.412],[-0.818,9]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.717999994755,0.949000000954,0.624000012875,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":24,"s":[2]}],"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,28.341],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Inside to dot ","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[1.307,-0.561],[0.894,-0.16],[0.706,0],[0.844,0.193],[0.728,0.334],[0.967,0.901]],"o":[[-1.038,0.967],[-0.817,0.351],[-0.673,0.12],[-0.9,0],[-0.794,-0.182],[-1.203,-0.551],[0,0]],"v":[[8.182,-1.636],[4.642,0.681],[2.07,1.453],[-0.001,1.636],[-2.621,1.341],[-4.909,0.563],[-8.182,-1.636]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.717999985639,0.948999980852,0.62400004069,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":24,"s":[2]}],"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[19.341,40.614],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Bottom","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0}],"markers":[{"tm":210,"cm":"2","dr":0},{"tm":255,"cm":"1","dr":0}]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 3506057..b88962d 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Zoem om skerm te vul"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Strek om skerm te vul"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Skermkiekie"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock is gedeaktiveer"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"het \'n prent gestuur"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Stoor tans skermkiekie..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Stoor tans skermkiekie..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Kan nie gesig herken nie. Gebruik eerder vingerafdruk."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Gebruik jou vingerafdruk om voort te gaan"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Kan nie vingerafdruk herken nie. Gebruik eerder skermslot."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Soek tans vir jou …"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Gesig-ikoon"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Versoenbaarheid-zoem se knoppie."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Tik weer om oop te maak"</string>
     <string name="tap_again" msgid="1315420114387908655">"Tik weer"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Swiep op om oop te maak"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Druk om oop te maak"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Swiep op om weer te probeer"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Ontsluit om NFC te gebruik"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Hierdie toestel behoort aan jou organisasie"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 74009bf..1110c99 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"ማያ እንዲሞላ አጉላ"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"ማያ ለመሙለት ሳብ"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"ቅጽበታዊ ገጽ እይታ"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock ተሰናክሏል"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ምስል ተልኳል"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"ቅጽበታዊ ገጽ እይታ በማስቀመጥ ላይ..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"ቅጽበታዊ ገጽ እይታ በማስቀመጥ ላይ..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"መልክን መለየት አልተቻለም። በምትኩ የጣት አሻራ ይጠቀሙ።"</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"ለመቀጠል የእርስዎን የጣት አሻራ ይጠቀሙ"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"የጣት አሻራን መለየት አልተቻለም። በምትኩ የማያ ገጽ መቆለፊያ ይጠቀሙ።"</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"እርስዎን በመፈለግ ላይ…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"የፊት አዶ"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"የተኳኋኝአጉላ አዝራር።"</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"ለመክፈት ዳግም መታ ያድርጉ"</string>
     <string name="tap_again" msgid="1315420114387908655">"እንደገና መታ ያድርጉ"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"ለመክፈት በጣት ወደ ላይ ጠረግ ያድርጉ"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"ለመክፈት ይጫኑ"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"እንደገና ለመሞከር ወደ ላይ ይጥረጉ"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFCን ለመጠቀም ይክፈቱ"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"ይህ መሣሪያ የድርጅትዎ ነው"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 993ee39..6d66748 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"تكبير/تصغير لملء الشاشة"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"توسيع بملء الشاشة"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"لقطة شاشة"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"‏تم إيقاف Smart Lock."</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"أرسَل صورة"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"جارٍ حفظ لقطة الشاشة..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"جارٍ حفظ لقطة الشاشة..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"يتعذّر التعرّف على الوجه. استخدِم بصمة الإصبع بدلاً من ذلك."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"عليك استخدام بصمة الإصبع للمتابعة."</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"يتعذّر التعرّف على بصمة الإصبع. عليك استخدام قفل الشاشة بدلاً من ذلك."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"جارٍ البحث عن وجهك…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"رمز الوجه"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"زر تكبير/تصغير للتوافق."</string>
@@ -460,6 +463,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"انقر مرة أخرى للفتح"</string>
     <string name="tap_again" msgid="1315420114387908655">"انقر مرة أخرى"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"يمكنك الفتح بالتمرير سريعًا لأعلى."</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"اضغط لفتح الجهاز."</string>
     <string name="keyguard_retry" msgid="886802522584053523">"مرِّر سريعًا للأعلى لإعادة المحاولة."</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"‏افتح قفل الشاشة لاستخدام تقنية NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"هذا الجهاز يخص مؤسستك."</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 20023c0..ee9ae88 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"স্ক্ৰীণ পূর্ণ কৰিবলৈ জুম কৰক"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"স্ক্ৰীণ পূর্ণ কৰিবলৈ প্ৰসাৰিত কৰক"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"স্ক্ৰীনশ্বট"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock অক্ষম কৰা হৈছে"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"এখন প্ৰতিচ্ছবি পঠিয়াইছে"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"স্ক্ৰীণশ্বট ছেভ কৰি থকা হৈছে…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"স্ক্ৰীণশ্বট ছেভ কৰি থকা হৈছে…"</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"মুখাৱয়ব চিনিব নোৱাৰি। ফিংগাৰপ্ৰিণ্ট ব্যৱহাৰ কৰক।"</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"অব্যাহত ৰাখিবলৈ আপোনাৰ ফিংগাৰপ্ৰিণ্ট ব্যৱহাৰ কৰক"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"ফিংগাৰপ্ৰিণ্ট চিনাক্ত কৰিব নোৱাৰি। তাৰ সলনি স্ক্ৰীন লক ব্যৱহাৰ কৰক।"</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"আপোনাৰ মুখমণ্ডল বিচাৰি আছে…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"মুখমণ্ডলৰ আইকন"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"উপযোগিতা অনুসৰি জুম কৰা বুটাম।"</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"খুলিবলৈ পুনৰাই টিপক"</string>
     <string name="tap_again" msgid="1315420114387908655">"পুনৰ টিপক"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"খুলিবলৈ ওপৰলৈ ছোৱাইপ কৰক"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"খুলিবলৈ টিপক"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"পুনৰ চেষ্টা কৰিবলৈ ওপৰলৈ ছোৱাইপ কৰক"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC ব্যৱহাৰ কৰিবলৈ আনলক কৰক"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"এই ডিভাইচটো আপোনাৰ প্ৰতিষ্ঠানৰ"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 71f90fd5..6779834 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Ekranı doldurmaq üçün yaxınlaşdır"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Ekranı doldurmaq üçün uzat"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Skrinşot"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock deaktivdir"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"şəkil göndərdi"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Skrinşot yadda saxlanılır..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Skrinşot yadda saxlanır..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Tanımaq olmur. Barmaq izini işlədin."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Davam etmək üçün barmaq izinizi istifadə edin"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Barmaq izini tanımaq olmur. Əvəzində ekran kilidindən istifadə edin."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Siz axtarılırsınız…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Üz işarəsi"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Uyğunluq zoom düyməsi."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Açmaq üçün yenidən tıklayın"</string>
     <string name="tap_again" msgid="1315420114387908655">"Yenidən toxunun"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Açmaq üçün yuxarı sürüşdürün"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Açmaq üçün basın"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Yenidən cəhd etmək üçün yuxarı sürüşdürün"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC istifadə etmək üçün kiliddən çıxarın"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Bu cihaz təşkilatınıza məxsusdur"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 7402594..607391a 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Zumiraj na celom ekranu"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Razvuci na ceo ekran"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Snimak ekrana"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock je onemogućen"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"je poslao/la sliku"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Čuvanje snimka ekrana..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Čuvanje snimka ekrana..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Lice nije prepoznato. Koristite otisak prsta."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Nastavite pomoću otiska prsta"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Prepoznavanje otiska prsta nije uspelo. Koristite zaključavanje ekrana umesto toga."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Tražimo vas…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Ikona lica"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Dugme Zum kompatibilnosti."</string>
@@ -454,6 +457,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Dodirnite ponovo da biste otvorili"</string>
     <string name="tap_again" msgid="1315420114387908655">"Dodirnite ponovo"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Prevucite nagore da biste otvorili"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Pritisnite da biste otvorili"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Prevucite nagore da biste probali ponovo"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Otključajte da biste koristili NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Ovaj uređaj pripada organizaciji"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 7688e06..d4954c1 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Павял. на ўвесь экран"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Расцягн. на ўвесь экран"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Здымак экрана"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Функцыя \"Smart Lock\" адключана"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"адпраўлены відарыс"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Захаванне скрыншота..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Захаванне скрыншота..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Твар не распазнаны. Скарыстайце адбітак пальца."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Каб працягнуць, скарыстайце адбітак пальца"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Не ўдалося распазнаць адбітак пальца. Разблакіруйце экран іншым спосабам."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Ідзе пошук вашага твару…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Значок твару"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Кнопка сумяшчальнасці маштаба."</string>
@@ -456,6 +459,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Дакраніцеся яшчэ раз, каб адкрыць"</string>
     <string name="tap_again" msgid="1315420114387908655">"Націсніце яшчэ раз"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Каб адкрыць, прагарніце ўверх"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Націсніце, каб адкрыць"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Прагартайце ўверх, каб паўтарыць спробу"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Разблакіруйце, каб выкарыстоўваць NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Гэта прылада належыць вашай арганізацыі"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 386fbd6..b938880 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Мащаб – запълва екрана"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Разпъване – запълва екрана"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Екранна снимка"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Функцията Smart Lock е деактивирана"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"изпратено изображение"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Екранната снимка се запазва..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Екранната снимка се запазва..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Лицето не е разпознато. Използвайте отпечатък."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Използвайте отпечатъка си, за да продължите"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Отпечатъкът не може да бъде разпознат. Вместо това използвайте опция за заключване на екрана."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Търсим ви…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Икона на лице"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Бутон за промяна на мащаба с цел съвместимост."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Докоснете отново, за да отворите"</string>
     <string name="tap_again" msgid="1315420114387908655">"Докоснете отново"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Прекарайте пръст нагоре, за да отключите"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Натиснете за отваряне"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Плъзнете бързо нагоре, за да опитате отново"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Отключете, за да използвате NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Това устройство принадлежи на организацията ви"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index d269126..811b6a5 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -77,6 +77,8 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"স্ক্রীণ পূরণ করতে জুম করুন"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"ফুল স্ক্রিন করুন"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"স্ক্রিনশট নিন"</string>
+    <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
+    <skip />
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"একটি ছবি পাঠানো হয়েছে"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"স্ক্রিনশট সেভ করা হচ্ছে..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"স্ক্রিনশট সেভ করা হচ্ছে..."</string>
@@ -184,6 +186,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"মুখ শনাক্ত করতে পারছি না। পরিবর্তে আঙ্গুলের ছাপ ব্যবহার করুন।"</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"চালিয়ে যেতে আঙ্গুলের ছাপ ব্যবহার করুন"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"আঙ্গুলের ছাপ শনাক্ত করতে পারছি না। পরিবর্তে স্ক্রিন লক ব্যবহার করুন।"</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"আপনার জন্য খোঁজা হচ্ছে…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"ফেস আইকন"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"সামঞ্জস্যের জুম বোতাম৷"</string>
@@ -452,6 +456,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"খোলার জন্য আবার আলতো চাপুন"</string>
     <string name="tap_again" msgid="1315420114387908655">"আবার ট্যাপ করুন"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"খোলার জন্য উপরে সোয়াইপ করুন"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"আনলক করার জন্য প্রেস করুন"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"আবার চেষ্টা করতে উপরের দিকে সোয়াইপ করুন"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC ব্যবহার করতে আনলক করুন"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"এই ডিভাইসটি আপনার প্রতিষ্ঠানের"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 2857ecc..d326a51 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Uvećaj prikaz na ekran"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Razvuci prikaz na ekran"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Snimak ekrana"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock je onemogućen"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"je poslao/la sliku"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Spašavanje snimka ekrana..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Spašavanje snimka ekrana..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Nije moguće prepoznati lice. Koristite otisak prsta."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Nastavite pomoću otiska prsta"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Nije moguće prepoznati otisak prsta. Umjesto toga koristite zaključavanje ekrana."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Tražimo vas…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Ikona lica"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Dugme za uvećavanje u slučaju nekompatibilnosti."</string>
@@ -454,6 +457,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Dodirnite ponovo da otvorite"</string>
     <string name="tap_again" msgid="1315420114387908655">"Ponovo dodirnite"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Prevucite da otvorite"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Pritisnite da otvorite"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Prevucite prema gore da pokušate ponovo"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Otključajte da koristite NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Ovaj uređaj pripada vašoj organizaciji"</string>
@@ -992,7 +996,7 @@
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"Nećete imati pristup podacima ni internetu putem mobilnog operatera <xliff:g id="CARRIER">%s</xliff:g>. Internet će biti dostupan samo putem WiFi mreže."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"vaš operater"</string>
     <string name="touch_filtered_warning" msgid="8119511393338714836">"Postavke ne mogu potvrditi vaš odgovor jer aplikacija zaklanja zahtjev za odobrenje."</string>
-    <string name="slice_permission_title" msgid="3262615140094151017">"Dozvoliti aplikaciji <xliff:g id="APP_0">%1$s</xliff:g> prikazivanje isječaka aplikacije <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
+    <string name="slice_permission_title" msgid="3262615140094151017">"Dozvoliti aplikaciji <xliff:g id="APP_0">%1$s</xliff:g> da prikazuje isječke aplikacije <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- Može čitati informacije iz aplikacije <xliff:g id="APP">%1$s</xliff:g>"</string>
     <string name="slice_permission_text_2" msgid="6758906940360746983">"- Može poduzeti radnje u aplikaciji <xliff:g id="APP">%1$s</xliff:g>"</string>
     <string name="slice_permission_checkbox" msgid="4242888137592298523">"Dozvoli aplikaciji <xliff:g id="APP">%1$s</xliff:g> prikazivanje isječaka iz svake aplikacije"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index c25dca5..4315057 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Zoom per omplir pantalla"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Estira per omplir pant."</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Captura de pantalla"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock desactivat"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ha enviat una imatge"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"S\'està desant captura de pantalla..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"S\'està desant la captura de pantalla..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"No podem detectar la cara. Usa l\'empremta digital."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Fes servir l\'empremta digital per continuar"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"No es pot reconèixer l\'empremta digital. Utilitza el bloqueig de pantalla."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"S\'està cercant la teva cara…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Icona facial"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Botó de zoom de compatibilitat."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Torna a tocar per obrir-la."</string>
     <string name="tap_again" msgid="1315420114387908655">"Torna a tocar"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Llisca cap amunt per obrir"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Prem per obrir"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Llisca cap a dalt per tornar-ho a provar"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desbloqueja per utilitzar l\'NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Aquest dispositiu pertany a la teva organització"</string>
@@ -576,7 +580,7 @@
     <string name="monitoring_description_network_logging" msgid="577305979174002252">"L\'administrador ha activat el registre de xarxa, que supervisa el trànsit del teu dispositiu.\n\nPer obtenir més informació, contacta amb l\'administrador."</string>
     <string name="monitoring_description_vpn" msgid="1685428000684586870">"Has donat permís a una aplicació per configurar una connexió VPN.\n\nAquesta aplicació pot supervisar el dispositiu i l\'activitat a la xarxa, com ara els correus electrònics, les aplicacions i els llocs web."</string>
     <string name="monitoring_description_vpn_profile_owned" msgid="4964237035412372751">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> gestiona el teu perfil de treball.\n\nL\'administrador pot supervisar la teva activitat a la xarxa, com ara els correus electrònics, les aplicacions i els llocs web.\n\nPer obtenir més informació, contacta amb l\'administrador.\n\nA més, estàs connectat a una VPN, que també pot supervisar la teva activitat a la xarxa."</string>
-    <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"El teu pare o la teva mare gestionen aquest dispositiu, i poden veure i gestionar informació com ara les aplicacions que utilitzes, la teva ubicació i el teu temps de connexió."</string>
+    <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"El teu pare o mare gestionen aquest dispositiu, i poden veure i gestionar informació com ara les aplicacions que utilitzes, la teva ubicació i el teu temps de connexió."</string>
     <string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
     <string name="monitoring_description_app" msgid="376868879287922929">"Estàs connectat a <xliff:g id="APPLICATION">%1$s</xliff:g>, que pot supervisar la teva activitat a la xarxa, com ara els correus electrònics, les aplicacions i els llocs web."</string>
     <string name="monitoring_description_app_personal" msgid="1970094872688265987">"Estàs connectat a <xliff:g id="APPLICATION">%1$s</xliff:g>, que pot supervisar la teva activitat personal a la xarxa, com ara els correus electrònics, les aplicacions i els llocs web."</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 5fed1b3..1cac5f7 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Přiblížit na celou obrazovku"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Na celou obrazovku"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Snímek obrazovky"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Funkce Smart Lock je deaktivována"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"odesílá obrázek"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Ukládání snímku obrazovky..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Ukládání snímku obrazovky..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Obličej se nepodařilo rozpoznat. Použijte místo něj otisk prstu."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Pokračujte přiložením prstu"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Otisk prstu se nepodařilo rozpoznat. Použijte místo něj zámek obrazovky."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Hledáme vás…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Ikona obličeje"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Tlačítko úpravy velikosti z důvodu kompatibility"</string>
@@ -456,6 +459,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Oznámení otevřete opětovným klepnutím"</string>
     <string name="tap_again" msgid="1315420114387908655">"Znovu klepněte"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Otevřete přejetím prstem nahoru"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Stisknutím otevřete"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Přejetím nahoru to zkusíte znovu"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC vyžaduje odemknutou obrazovku"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Toto zařízení patří vaší organizaci"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index facedf0f..48f8086 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Zoom til fuld skærm"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Stræk til fuld skærm"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock er deaktiveret"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"sendte et billede"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Gemmer screenshot..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Gemmer screenshot..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Ansigtet kan ikke genkendes. Brug fingeraftryk i stedet."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Brug dit fingeraftryk for at fortsætte"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Fingeraftrykket kan ikke genkendes. Brug skærmlåsen i stedet."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Forsøger at finde dig…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Ansigt"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Knap for kompatibilitetszoom."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Tryk igen for at åbne"</string>
     <string name="tap_again" msgid="1315420114387908655">"Tryk igen"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Stryg opad for at åbne"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Tryk for at åbne"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Stryg opad for at prøve igen"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Lås op for at bruge NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Denne enhed tilhører din organisation"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 5d10eb4..4c4a876 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Zoom auf Bildschirmgröße"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Auf Bildschirmgröße anpassen"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock deaktiviert"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"Bild gesendet"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Screenshot wird gespeichert..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Screenshot wird gespeichert..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Gesicht wurde nicht erkannt. Verwende stattdessen den Fingerabdruck."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Mithilfe deines Fingerabdrucks fortfahren"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Fingerabdruck wurde nicht erkannt. Verwende stattdessen die Displaysperre."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Wir suchen nach dir…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Gesichtssymbol"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Schaltfläche für Kompatibilitätszoom"</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Erneut tippen, um Benachrichtigung zu öffnen"</string>
     <string name="tap_again" msgid="1315420114387908655">"Noch einmal tippen"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Zum Öffnen nach oben wischen"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Zum Öffnen klicken"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Zum Wiederholen nach oben wischen"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Zur Verwendung von NFC entsperren"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Dieses Gerät gehört deiner Organisation"</string>
@@ -1082,7 +1086,7 @@
     <string name="controls_media_title" msgid="1746947284862928133">"Medien"</string>
     <string name="controls_media_close_session" msgid="1193000643003066508">"Diese Mediensitzung ausblenden?"</string>
     <string name="controls_media_active_session" msgid="3146882316024153337">"Die Mediensitzung kann nicht ausgeblendet werden."</string>
-    <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ablehnen"</string>
+    <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ausblenden"</string>
     <string name="controls_media_resume" msgid="1933520684481586053">"Fortsetzen"</string>
     <string name="controls_media_settings_button" msgid="5815790345117172504">"Einstellungen"</string>
     <string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> von <xliff:g id="ARTIST_NAME">%2$s</xliff:g> wird gerade über <xliff:g id="APP_LABEL">%3$s</xliff:g> wiedergegeben"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index cfda46f..0b37ed8 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Ζουμ σε πλήρη οθόνη"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Προβoλή σε πλήρη οθ."</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Στιγμιότυπο οθόνης"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Το Smart Lock έχει απενεργοποιηθεί"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"έστειλε μια εικόνα"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Αποθήκ. στιγμιότυπου οθόνης..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Αποθήκευση στιγμιότυπου οθόνης..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Το πρόσωπο δεν αναγνωρίζεται. Χρησιμ. δακτ. αποτ."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Χρησιμοποιήστε δακτυλ. αποτύπωμα για να συνεχίσετε"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Δεν είναι δυνατή η αναγνώριση του δακτυλικού αποτυπώματος. Χρησιμοποιήστε εναλλακτικά το κλείδωμα οθόνης."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Αναζήτηση για εσάς…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Εικονίδιο προσώπου"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Κουμπί εστίασης συμβατότητας."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Πατήστε ξανά για να ανοίξετε"</string>
     <string name="tap_again" msgid="1315420114387908655">"Πατήστε ξανά"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Σύρετε προς τα επάνω για άνοιγμα"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Πατήστε για άνοιγμα"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Σύρετε προς τα πάνω για να δοκιμάσετε ξανά"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Ξεκλείδωμα για χρήση του NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Αυτή η συσκευή ανήκει στον οργανισμό σας."</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 9af2d89..e50c00e 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Zoom to fill screen"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Stretch to fill screen"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock disabled"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"sent an image"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Saving screenshot…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Saving screenshot…"</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Can’t recognise face. Use fingerprint instead."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Use your fingerprint to continue"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Can’t recognise fingerprint. Use screen lock instead."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Looking for you…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Face icon"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Compatibility zoom button."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Tap again to open"</string>
     <string name="tap_again" msgid="1315420114387908655">"Tap again"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Swipe up to open"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Press to open"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Swipe up to try again"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Unlock to use NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"This device belongs to your organisation"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 398420a..fba313e 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Zoom to fill screen"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Stretch to fill screen"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock disabled"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"sent an image"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Saving screenshot…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Saving screenshot…"</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Can’t recognise face. Use fingerprint instead."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Use your fingerprint to continue"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Can’t recognise fingerprint. Use screen lock instead."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Looking for you…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Face icon"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Compatibility zoom button."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Tap again to open"</string>
     <string name="tap_again" msgid="1315420114387908655">"Tap again"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Swipe up to open"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Press to open"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Swipe up to try again"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Unlock to use NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"This device belongs to your organisation"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 9af2d89..e50c00e 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Zoom to fill screen"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Stretch to fill screen"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock disabled"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"sent an image"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Saving screenshot…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Saving screenshot…"</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Can’t recognise face. Use fingerprint instead."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Use your fingerprint to continue"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Can’t recognise fingerprint. Use screen lock instead."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Looking for you…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Face icon"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Compatibility zoom button."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Tap again to open"</string>
     <string name="tap_again" msgid="1315420114387908655">"Tap again"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Swipe up to open"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Press to open"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Swipe up to try again"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Unlock to use NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"This device belongs to your organisation"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 9af2d89..e50c00e 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Zoom to fill screen"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Stretch to fill screen"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock disabled"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"sent an image"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Saving screenshot…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Saving screenshot…"</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Can’t recognise face. Use fingerprint instead."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Use your fingerprint to continue"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Can’t recognise fingerprint. Use screen lock instead."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Looking for you…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Face icon"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Compatibility zoom button."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Tap again to open"</string>
     <string name="tap_again" msgid="1315420114387908655">"Tap again"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Swipe up to open"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Press to open"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Swipe up to try again"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Unlock to use NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"This device belongs to your organisation"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index b211026..a42a531 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‏‎‎‎‏‎‏‎‏‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‎‏‏‏‎‏‏‎‏‎‏‏‎‎‏‏‎‎‎‏‏‏‎‏‏‏‎‏‎‎‎Zoom to fill screen‎‏‎‎‏‎"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‏‎‏‏‎‎‏‎‏‎‎‎‏‏‏‎‎‎‎‎‎‎‎‏‎‎‏‎‏‏‎‏‏‏‏‏‎‎‎‏‏‎‏‏‏‎‎‏‎‎‏‎Stretch to fill screen‎‏‎‎‏‎"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‎‎‏‏‏‎‎‏‏‏‎‎‎‎‏‎‏‏‏‎‏‎‎‏‏‎‎‏‏‎‎‏‏‎‏‎‎‏‏‏‎‏‎‎‎‏‏‎‎‎‎‏‏‎‎Screenshot‎‏‎‎‏‎"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‎‏‎‎‎‎‏‏‏‎‏‎‏‎‎‏‏‎‏‎‏‎‏‏‎‎‏‎‏‏‏‏‎‎‎‏‎‎‎‎Smart Lock disabled‎‏‎‎‏‎"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‎‏‎‎‎‏‎‏‏‏‏‎‎‏‎‏‎‎‏‎‏‎‏‏‏‏‎‎‏‎‎‎‏‏‎‎‎‎‎‎‏‏‎‏‎‎‏‎‏‎‎‎‏‏‎sent an image‎‏‎‎‏‎"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‏‏‏‏‎‎‎‏‏‎‎‏‏‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‏‎‏‎‎‎‎‏‏‏‎‎‎‎‏‏‎‏‎‎‎‏‎‏‎‎‎Saving screenshot…‎‏‎‎‏‎"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‎‎‎‎‎‏‏‏‏‎‎‏‏‏‏‎‎‎‎‏‏‎‏‎‏‏‎‏‎‎‎‎‏‏‎‎‎‏‎‏‎‎‏‎‏‎Saving screenshot…‎‏‎‎‏‎"</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‏‏‏‎‏‎‏‏‏‏‎‏‎‎‏‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‎‏‎‏‎‎‏‏‎‎‎‏‎‎‎‏‎‎‎Can’t recognize face. Use fingerprint instead.‎‏‎‎‏‎"</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‏‏‎‏‎‎‎‏‏‏‏‎‏‎‏‎‎‏‎‏‎‎‎‏‎‎‏‎‎‏‏‏‎‏‎‏‏‏‎‏‏‏‎‏‎‏‏‏‎‏‎‎‏‏‎‏‎Use your fingerprint to continue‎‏‎‎‏‎"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‏‏‎‎‎‎‏‎‏‎‎‏‎‏‎‏‏‎‏‎‎‏‏‏‎‎‏‏‏‎‎‎‏‏‏‎‏‎‎‎‎‎‏‎‎‏‎‏‏‏‎‎‎‏‎Can’t recognize fingerprint. Use screen lock instead.‎‏‎‎‏‎"</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‏‏‏‏‏‎‎‎‎‎‏‎‏‏‎‏‎‎‏‎‏‎‎‏‎‏‎‏‎‏‎‏‏‎‏‎‏‏‏‏‎‏‎‎‏‎‏‎‎‎‏‎‏‎Looking for you…‎‏‎‎‏‎"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‎‏‏‎‎‎‎‏‏‎‏‎‏‎‏‎‎‏‏‎‎‎‏‎‎‏‏‏‏‏‎‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‎‎Face icon‎‏‎‎‏‎"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‎‎‎‎‎‎‏‏‏‎‎‏‏‏‏‏‏‎‎‎‏‏‏‎‎‏‏‏‎‎‎‎‏‎‏‏‎‎‏‏‎‎‏‎‎‏‏‏‎‏‏‎‏‎Compatibility zoom button.‎‏‎‎‏‎"</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‎‎‏‎‏‎‏‎‎‎‎‏‎‎‏‎‎‏‏‎‎‎‏‏‎‎‎‎‎‎‎‏‏‎‎‏‎‏‏‏‎‎‎‏‎‎‏‎‎‎‎‏‎Tap again to open‎‏‎‎‏‎"</string>
     <string name="tap_again" msgid="1315420114387908655">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‎‎‎‎‏‎‏‎‎‏‏‏‏‏‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‎‏‎‏‏‏‏‎Tap again‎‏‎‎‏‎"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‏‏‏‎‏‎‎‏‏‏‏‎‏‏‎‏‏‏‎‎‏‏‏‏‎‏‎‎‎‎‎‏‏‏‎‏‏‏‎‎‎‏‎‎‏‎‎‎‎‎‏‎Swipe up to open‎‏‎‎‏‎"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‎‏‏‎‎‏‎‏‎‏‏‎‏‏‏‎‏‏‏‎‏‏‏‏‎‏‏‏‏‎‎‏‎‏‎‏‏‎‏‏‏‏‏‎‏‎‏‎‎‎‏‎‎‎Press to open‎‏‎‎‏‎"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‎‏‎‎‏‏‏‎‏‎‎‎‏‏‏‎‎‎‏‏‎‎‏‎‎‎‏‎‎‎‎‎‏‎‎‎‏‎‏‎‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‏‎Swipe up to try again‎‏‎‎‏‎"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‏‎‏‎‏‏‏‎‏‎‎‏‏‎‏‎‏‏‏‏‏‏‎‎‎‎‏‏‏‏‏‎‎‎‎‏‎‏‎‎‏‏‏‏‎Unlock to use NFC‎‏‎‎‏‎"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‏‎‏‎‎‏‎‏‏‏‏‎‎‎‏‎‏‎‎‏‎‎‏‏‎‏‎‏‏‎‎‏‎‏‏‏‏‎‏‏‏‏‏‎This device belongs to your organization‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 4520ed3..9929020 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Zoom para ocupar la pantalla"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Estirar p/ ocupar la pantalla"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Captura de pantalla"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Se inhabilitó Smart Lock"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"envió una imagen"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Guardando captura de pantalla"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Guardando la captura de pantalla..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"No se reconoce el rostro. Usa la huella dactilar."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Utiliza tu huella dactilar para continuar"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"No se reconoce la huella dactilar. Utiliza el bloqueo de pantalla en su lugar."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Autenticando tu rostro…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Ícono de rostro"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Botón de zoom de compatibilidad"</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Presiona de nuevo para abrir"</string>
     <string name="tap_again" msgid="1315420114387908655">"Presiona otra vez"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Desliza el dedo hacia arriba para abrir"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Presiona para abrir"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Desliza el dedo hacia arriba para volver a intentarlo"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desbloquea el dispositivo para usar NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Este dispositivo pertenece a tu organización"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index c5096e9..6a42460 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Zoom para ajustar"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Expandir para ajustar"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Captura de pantalla"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock inhabilitado"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ha enviado una imagen"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Guardando captura..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Guardando captura..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"No se reconoce la cara. Usa la huella digital."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Usa tu huella digital para continuar"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"No se reconoce la huella digital. Usa el bloqueo de pantalla."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Buscando tu cara…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Icono de cara"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Botón de zoom de compatibilidad"</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Toca de nuevo para abrir"</string>
     <string name="tap_again" msgid="1315420114387908655">"Toca de nuevo"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Desliza el dedo hacia arriba para abrir"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Pulsa para abrirlo"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Desliza el dedo hacia arriba para volverlo a intentar"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desbloquea para usar el NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Este dispositivo pertenece a tu organización"</string>
@@ -674,8 +678,8 @@
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Ajustes de pantalla de bloqueo"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabajo"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Modo avión"</string>
-    <string name="add_tile" msgid="6239678623873086686">"Añadir icono"</string>
-    <string name="broadcast_tile" msgid="5224010633596487481">"Icono de emisión"</string>
+    <string name="add_tile" msgid="6239678623873086686">"Añadir recuadro"</string>
+    <string name="broadcast_tile" msgid="5224010633596487481">"Recuadro de emisión"</string>
     <string name="zen_alarm_warning_indef" msgid="5252866591716504287">"No oirás la próxima alarma (<xliff:g id="WHEN">%1$s</xliff:g>) a menos que desactives esta opción antes"</string>
     <string name="zen_alarm_warning" msgid="7844303238486849503">"No oirás la próxima alarma (<xliff:g id="WHEN">%1$s</xliff:g>)"</string>
     <string name="alarm_template" msgid="2234991538018805736">"a las <xliff:g id="WHEN">%1$s</xliff:g>"</string>
@@ -889,10 +893,10 @@
     <string name="right_keycode" msgid="2480715509844798438">"Código de teclado a la derecha"</string>
     <string name="left_icon" msgid="5036278531966897006">"Icono a la izquierda"</string>
     <string name="right_icon" msgid="1103955040645237425">"Icono a la derecha"</string>
-    <string name="drag_to_add_tiles" msgid="8933270127508303672">"Pulsa y arrastra para añadir funciones"</string>
-    <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Mantén pulsado un icono y arrástralo para reubicarlo"</string>
+    <string name="drag_to_add_tiles" msgid="8933270127508303672">"Pulsa y arrastra para añadir recuadros"</string>
+    <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Mantén pulsado un recuadro y arrástralo para reubicarlo"</string>
     <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arrastra aquí para quitar una función"</string>
-    <string name="drag_to_remove_disabled" msgid="933046987838658850">"Necesitas al menos <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> iconos"</string>
+    <string name="drag_to_remove_disabled" msgid="933046987838658850">"Necesitas al menos <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> recuadros"</string>
     <string name="qs_edit" msgid="5583565172803472437">"Editar"</string>
     <string name="tuner_time" msgid="2450785840990529997">"Hora"</string>
   <string-array name="clock_options">
@@ -907,15 +911,15 @@
   </string-array>
     <string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar iconos de notificaciones con prioridad baja"</string>
     <string name="other" msgid="429768510980739978">"Otros"</string>
-    <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"quitar icono"</string>
-    <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"añadir icono al final"</string>
-    <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover icono"</string>
-    <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Añadir icono"</string>
+    <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"quitar recuadro"</string>
+    <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"añadir recuadro al final"</string>
+    <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover recuadro"</string>
+    <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Añadir recuadro"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Añadir a la posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
-    <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Tarjeta añadida"</string>
-    <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Tarjeta quitada"</string>
+    <string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Recuadro añadido"</string>
+    <string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Recuadro quitado"</string>
     <string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de ajustes rápidos."</string>
     <string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificación de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
     <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir ajustes."</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index ee7c760..10becfb 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Suumi ekraani täitmiseks"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Venita ekraani täitmiseks"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Ekraanipilt"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock on keelatud"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"saatis kujutise"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Kuvatõmmise salvestamine ..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Kuvatõmmise salvestamine ..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Nägu ei õnnestu tuvastada. Kasutage sõrmejälge."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Jätkamiseks kasutage sõrmejälge"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Sõrmejälge ei õnnestu tuvastada. Kasutage selle asemel ekraanilukku."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Otsitakse teid …"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Näoikoon"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Sobivussuumi nupp."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Avamiseks puudutage uuesti"</string>
     <string name="tap_again" msgid="1315420114387908655">"Puudutage uuesti"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Pühkige avamiseks üles"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Avamiseks vajutage"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Uuesti proovimiseks pühkige üles"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC kasutamiseks avage."</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"See seade kuulub teie organisatsioonile"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 0972c26b..3dbbc07 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -58,7 +58,7 @@
     <string name="always_use_device" msgid="210535878779644679">"Ireki <xliff:g id="APPLICATION">%1$s</xliff:g> <xliff:g id="USB_DEVICE">%2$s</xliff:g> konektatzen den guztietan"</string>
     <string name="always_use_accessory" msgid="1977225429341838444">"Ireki <xliff:g id="APPLICATION">%1$s</xliff:g> <xliff:g id="USB_ACCESSORY">%2$s</xliff:g> konektatzen den guztietan"</string>
     <string name="usb_debugging_title" msgid="8274884945238642726">"USB bidezko arazketa onartu?"</string>
-    <string name="usb_debugging_message" msgid="5794616114463921773">"Ordenagailuaren RSA gakoaren erreferentzia-gako digitala hau da:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
+    <string name="usb_debugging_message" msgid="5794616114463921773">"Ordenagailuaren RSA gakoaren aztarna digitala hau da:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
     <string name="usb_debugging_always" msgid="4003121804294739548">"Eman beti ordenagailu honetatik arazteko baimena"</string>
     <string name="usb_debugging_allow" msgid="1722643858015321328">"Eman baimena"</string>
     <string name="usb_debugging_secondary_user_title" msgid="7843050591380107998">"Ez da onartzen USB bidezko arazketa"</string>
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Handiagotu pantaila betetzeko"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Luzatu pantaila betetzeko"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Pantaila-argazkia"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Desgaitu da Smart Lock"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"erabiltzaileak irudi bat bidali du"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Pantaila-argazkia gordetzen…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Pantaila-argazkia gordetzen…"</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Ez da hauteman aurpegia. Erabili hatz-marka."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Aurrera egiteko, erabili hatz-marka"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Ez da hauteman hatz-marka. Erabili pantailaren blokeoa."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Zure bila…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Aurpegiaren ikonoa"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Zoom-bateragarritasunaren botoia."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Irekitzeko, ukitu berriro"</string>
     <string name="tap_again" msgid="1315420114387908655">"Sakatu berriro"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Pasatu hatza gora irekitzeko"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Sakatu irekitzeko"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Berriro saiatzeko, pasatu hatza gora"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desblokea ezazu NFC erabiltzeko"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Gailu hau zure erakundearena da"</string>
@@ -574,7 +578,7 @@
     <string name="monitoring_description_ca_cert_settings_separator" msgid="7107390013344435439">" "</string>
     <string name="monitoring_description_ca_cert_settings" msgid="8329781950135541003">"Ireki kredentzial fidagarriak"</string>
     <string name="monitoring_description_network_logging" msgid="577305979174002252">"Administratzaileak sarearen erregistroak aktibatu ditu; horrela, zure gailuko trafikoa gainbegira dezake.\n\nInformazio gehiago lortzeko, jarri administratzailearekin harremanetan."</string>
-    <string name="monitoring_description_vpn" msgid="1685428000684586870">"Aplikazio bati VPN konexio bat konfiguratzeko baimena eman diozu.\n\nAplikazio horrek gailuko eta sareko jarduerak kontrola ditzake, mezu elektronikoak, aplikazioak eta webguneak barne."</string>
+    <string name="monitoring_description_vpn" msgid="1685428000684586870">"Aplikazio bati VPN bidezko konexio bat konfiguratzeko baimena eman diozu.\n\nAplikazio horrek gailuko eta sareko jarduerak kontrola ditzake, mezu elektronikoak, aplikazioak eta webguneak barne."</string>
     <string name="monitoring_description_vpn_profile_owned" msgid="4964237035412372751">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> erakundeak kudeatzen du zure laneko profila.\n\nAdministratzaileak sareko jarduerak kontrola diezazkizuke, besteak beste, posta elektronikoa, aplikazioak eta webguneak.\n\nInformazio gehiago lortzeko, jarri administratzailearekin harremanetan.\n\nHorrez gain, VPN batera zaude konektatuta, eta hark ere kontrola ditzake zure sareko jarduerak."</string>
     <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Zure gurasoak kudeatzen du gailua. Zure gurasoak gailuko informazioa ikusi eta kudea dezake; besteak beste, zer aplikazio erabiltzen dituzun, zure kokapena zein den eta pantaila aurrean zenbat eta noiz egoten zaren."</string>
     <string name="legacy_vpn_name" msgid="4174223520162559145">"VPN konexioa"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 2899e17..4606ae7 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"بزرگ‌نمایی برای پر کردن صفحه"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"گسترده کردن برای پر کردن صفحه"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"نماگرفت"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"‏Smart Lock غیرفعال شد"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"تصویری ارسال کرد"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"در حال ذخیره نماگرفت..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"درحال ذخیره نماگرفت…"</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"چهره شناسایی نشد. درعوض از اثر انگشت استفاده کنید."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"برای ادامه، از اثر انگشتتان استفاده کنید"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"اثر انگشت شناسایی نشد. درعوض از قفل صفحه استفاده کنید."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"درحال جستجوی شما…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"نماد چهره"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"دکمه بزرگ‌نمایی سازگار."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"دوباره ضربه بزنید تا باز شود"</string>
     <string name="tap_again" msgid="1315420114387908655">"دوباره ضربه بزنید"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"برای باز کردن، انگشتتان را تند به‌بالا بکشید"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"برای باز کردن فشار دهید"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"برای امتحان مجدد، انگشتتان را تند به‌بالا بکشید"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"‏برای استفاده از NFC، قفل را باز کنید"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"این دستگاه به سازمان شما تعلق دارد"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index dc49957..9d9b152 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Zoomaa koko näyttöön"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Venytä koko näyttöön"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Kuvakaappaus"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock poistettu käytöstä"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"lähetti kuvan"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Tallennetaan kuvakaappausta..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Tallennetaan kuvakaappausta..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Kasvoja ei voi tunnistaa. Käytä sormenjälkeä."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Jatka sormenjäljen avulla"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Sormenjälkeä ei voi tunnistaa. Käytä sen sijaan näytön lukitusta."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Etsitään kasvoja…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Kasvokuvake"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Yhteensopivuuszoomaus-painike."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Avaa napauttamalla uudelleen"</string>
     <string name="tap_again" msgid="1315420114387908655">"Napauta uudelleen"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Avaa pyyhkäisemällä ylös"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Avaa painamalla"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Yritä uudelleen pyyhkäisemällä ylös"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Avaa lukitus, jotta voit käyttää NFC:tä"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Organisaatiosi omistaa tämän laitteen"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 83c0ef4..22e77a0 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -77,6 +77,8 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Zoomer pour remplir l\'écran"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Étirer pour remplir l\'écran"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Capture d\'écran"</string>
+    <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
+    <skip />
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"a envoyé une image"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Enregistrement capture écran…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Enregistrement capture écran…"</string>
@@ -184,6 +186,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Visage non reconnu. Utilisez plutôt l\'empreinte digitale."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Utilisez votre empreinte digitale pour continuer"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Empreinte digitale non reconnue. Utilisez plutôt le verrouillage de l\'écran."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Recherche de votre visage…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Icône de visage"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Bouton \"Zoom de compatibilité\""</string>
@@ -452,6 +456,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Touchez à nouveau pour ouvrir"</string>
     <string name="tap_again" msgid="1315420114387908655">"Toucher de nouveau"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Balayez l\'écran vers le haut pour ouvrir"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Appuyez pour ouvrir"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Balayez l\'écran vers le haut pour réessayer"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Déverrouillez l\'écran pour utiliser la CCP"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Cet appareil appartient à votre organisation"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index bb3183e..5510ef1 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Zoomer pour remplir l\'écran"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Étirer pour remplir l\'écran"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Capture d\'écran"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock désactivé"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"a envoyé une image"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Enregistrement capture écran…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Enregistrement de la capture d\'écran…"</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Visage non reconnu. Utilisez votre empreinte."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Utilisez votre empreinte pour continuer"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Empreinte non reconnue. Utilisez le verrouillage de l\'écran."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Recherche de votre visage…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Icône représentant un visage"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Bouton \"Zoom de compatibilité\""</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Appuyer à nouveau pour ouvrir"</string>
     <string name="tap_again" msgid="1315420114387908655">"Appuyer à nouveau"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Balayer vers le haut pour ouvrir"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Appuyez pour ouvrir"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Balayez l\'écran vers le haut pour réessayer"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Déverrouillez l\'écran pour pouvoir utiliser NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Cet appareil appartient à votre organisation"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index d8fe4b8..a9f0593 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Ampliar ata ocupar todo"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Estirar ata ocupar todo"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Facer captura"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock está desactivado"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"enviou unha imaxe"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Gardando captura de pantalla…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Gardando captura de pantalla…"</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Non se recoñeceu a cara. Usa a impresión dixital."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Utiliza a túa impresión dixital para continuar"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Non se puido recoñecer a impresión dixital. Mellor usa o bloqueo de pantalla."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Buscándote…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Icona de cara"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Botón de zoom de compatibilidade"</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Toca de novo para abrir"</string>
     <string name="tap_again" msgid="1315420114387908655">"Toca de novo"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Pasa o dedo cara arriba para abrir"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Preme para abrir"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Pasa o dedo cara arriba para tentalo de novo"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desbloquea o dispositivo para utilizar a NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Este dispositivo pertence á túa organización."</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 553924a..9f05de5 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -77,6 +77,8 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"સ્ક્રીન ભરવા માટે ઝૂમ કરો"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"સ્ક્રીન ભરવા માટે ખેંચો"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"સ્ક્રીનશૉટ"</string>
+    <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
+    <skip />
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"છબી મોકલી"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"સ્ક્રીનશોટ સાચવી રહ્યું છે…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"સ્ક્રીનશોટ સાચવી રહ્યું છે…"</string>
@@ -184,6 +186,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"ચહેરો ઓળખી શકતા નથી. તેને બદલે ફિંગરપ્રિન્ટ વાપરો."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"આગળ વધવા માટે તમારી ફિંગરપ્રિન્ટનો ઉપયોગ કરો"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"ફિંગરપ્રિન્ટ ઓળખી શકતા નથી. તેને બદલે સ્ક્રીન લૉકનો ઉપયોગ કરો."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"તમારા માટે શોધી રહ્યાં છે..."</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"ચહેરા આઇકન"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"સુસંગતતા ઝૂમ બટન."</string>
@@ -452,6 +456,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"ખોલવા માટે ફરીથી ટૅપ કરો"</string>
     <string name="tap_again" msgid="1315420114387908655">"ફરીથી ટૅપ કરો"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"ખોલવા માટે ઉપરની તરફ સ્વાઇપ કરો"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"અનલૉક કરવા માટે દબાવો"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"ફરી પ્રયાસ કરવા માટે ઉપરની તરફ સ્વાઇપ કરો"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFCનો ઉપયોગ કરવા માટે અનલૉક કરો"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"આ ડિવાઇસ તમારી સંસ્થાની માલિકીનું છે"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index ac4a903..ffe3a5b 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"स्‍क्रीन भरने के लिए ज़ूम करें"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"स्‍क्रीन भरने के लिए खींचें"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"स्क्रीनशॉट"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock की सुविधा बंद कर दी गई है"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"एक इमेज भेजी गई"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"स्क्रीनशॉट सहेजा जा रहा है..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"स्क्रीनशॉट सहेजा जा रहा है..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"चेहरे की पहचान नहीं हुई. फ़िंगरप्रिंट इस्तेमाल करें."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"जारी रखने के लिए, फ़िंगरप्रिंट का इस्तेमाल करें"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"फ़िंगरप्रिंट पहचाना नहीं जा सका. इसके बजाय, स्क्रीन लॉक इस्तेमाल करके देखें."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"आपको पहचान रहा है…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"चेहरे का आइकॉन"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"संगतता ज़ूम बटन."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"खोलने के लिए फिर से टैप करें"</string>
     <string name="tap_again" msgid="1315420114387908655">"फिर से टैप करें"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"खोलने के लिए ऊपर स्वाइप करें"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"अनलॉक करने के लिए दबाएं"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"फिर से कोशिश करने के लिए ऊपर की ओर स्वाइप करें"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"एनएफ़सी इस्तेमाल करने के लिए स्क्रीन को अनलॉक करें"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"इस डिवाइस का मालिकाना हक आपके संगठन के पास है"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 571ca24..260dba9 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Zumiraj i ispuni zaslon"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Rastegni i ispuni zaslon"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Snimka zaslona"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock onemogućen"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"šalje sliku"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Spremanje snimke zaslona..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Spremanje snimke zaslona..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Prepoznavanje lica nije uspjelo. Upotrijebite otisak prsta."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Nastavite pomoću otiska prsta"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Prepoznavanje otiska prsta nije uspjelo. Umjesto toga upotrebljavajte zaključavanje zaslona."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Tražimo vas…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Ikona lica"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Gumb za kompatibilnost zumiranja."</string>
@@ -454,6 +457,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Dodirnite opet za otvaranje"</string>
     <string name="tap_again" msgid="1315420114387908655">"Dodirnite ponovo"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Prijeđite prstom prema gore da biste otvorili"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Pritisnite da biste otvorili"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Prijeđite prstom prema gore za ponovni pokušaj"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Otključajte da biste upotrijebili NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Ovaj uređaj pripada vašoj organizaciji"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 9a7ca9d6..4a71826 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Nagyítás a kitöltéshez"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Nyújtás kitöltéshez"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Képernyőkép"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock kikapcsolva"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"képet küldött"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Képernyőkép mentése..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Képernyőkép mentése..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Az arc nem felismerhető. Használjon ujjlenyomatot."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"A folytatáshoz használja ujjlenyomatát"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Az ujjlenyomat nem ismerhető fel. Használja inkább a képernyőzárat."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Keresem az Ön arcát…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Arcikon"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Kompatibilitási zoom gomb."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Koppintson ismét a megnyitáshoz"</string>
     <string name="tap_again" msgid="1315420114387908655">"Koppintson újra"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Csúsztasson felfelé a megnyitáshoz"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"A megnyitáshoz nyomja meg"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Az újrapróbálkozáshoz csúsztassa felfelé az ujját"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Az NFC használatához oldja fel a képernyőzárat"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Ez az eszköz az Ön szervezetének tulajdonában van"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 74ab34d..bdebdad 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Խոշորացնել` էկրանը լցնելու համար"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Ձգել` էկրանը լցնելու համար"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Սքրինշոթ"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock-ն անջատված է"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"պատկեր է ուղարկվել"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Սքրինշոթը պահվում է…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Սքրինշոթը պահվում է..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Դեմքը չի հաջողվում ճանաչել։ Օգտագործեք մատնահետքը։"</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Շարունակելու համար անհրաժեշտ է ձեր մատնահետքը"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Մատնահետքը չի հաջողվում ճանաչել։ Օգտագործեք էկրանի կողպումը։"</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Դեմքի ճանաչում…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Դեմքի պատկերակ"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Համատեղելիության խոշորացման կոճակը:"</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Կրկին հպեք՝ բացելու համար"</string>
     <string name="tap_again" msgid="1315420114387908655">"Նորից հպեք"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Բացելու համար սահեցրեք վերև"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Սեղմեք՝ բացելու համար"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Սահեցրեք վերև՝ նորից փորձելու համար"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Ապակողպեք՝ NFC-ն օգտագործելու համար"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Այս սարքը պատկանում է ձեր կազմակերպությանը"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 204484c..15b64c6 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Perbesar utk mengisi layar"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Rentangkn utk mngisi layar"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock dinonaktifkan"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"mengirim gambar"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Menyimpan screenshot..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Menyimpan screenshot..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Tidak dapat mengenali wajah. Gunakan sidik jari."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Gunakan sidik jari untuk melanjutkan"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Tidak dapat mengenali sidik jari. Gunakan kunci layar."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Mencari wajah Anda…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Ikon wajah"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Tombol perbesar/perkecil kompatibilitas."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Ketuk lagi untuk membuka"</string>
     <string name="tap_again" msgid="1315420114387908655">"Ketuk lagi"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Geser ke atas untuk membuka"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Tekan untuk membuka"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Geser ke atas untuk mencoba lagi"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Buka kunci untuk menggunakan NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Perangkat ini milik organisasi Anda"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 5afc8bc..e0ff373 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Fylla skjá með aðdrætti"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Teygja yfir allan skjáinn"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Skjámynd"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Slökkt á Smart Lock"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"sendi mynd"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Vistar skjámynd…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Vistar skjámynd…"</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Andlit þekkist ekki. Notaðu fingrafar í staðinn."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Notaðu fingrafarið þitt til að halda áfram"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Fingrafar þekkist ekki. Notaðu skjálás í staðinn."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Leitar að þér ..."</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Andlitstákn"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Hnappur fyrir samhæfisaðdrátt."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Ýttu aftur til að opna"</string>
     <string name="tap_again" msgid="1315420114387908655">"Ýttu aftur"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Strjúktu upp til að opna"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Ýttu til að opna"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Strjúktu upp til að reyna aftur"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Taktu úr lás til að nota NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Þetta tæki tilheyrir fyrirtækinu þínu"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 9c28363..3d26cbb 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Zoom per riempire schermo"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Estendi per riemp. schermo"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Funzionalità Smart Lock disattivata"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"è stata inviata un\'immagine"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Salvataggio screenshot…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Salvataggio screenshot…"</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Impossibile riconoscere il volto. Usa l\'impronta."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Utilizza la tua impronta per continuare"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Impossibile riconoscere l\'impronta. Usa il blocco schermo."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"In attesa del volto…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Icona volto"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Pulsante zoom compatibilità."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Tocca ancora per aprire"</string>
     <string name="tap_again" msgid="1315420114387908655">"Tocca di nuovo"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Scorri verso l\'alto per aprire"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Premi per aprire"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Scorri verso l\'alto per riprovare"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Sblocca per usare NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Questo dispositivo appartiene alla tua organizzazione"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index b7153b1..ed1ea36 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -77,6 +77,8 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"הגדלת התצוגה למילוי המסך"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"מתיחה למילוי של המסך"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"צילום מסך"</string>
+    <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
+    <skip />
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"נשלחה תמונה"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"צילום המסך נשמר..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"המערכת שומרת את צילום המסך..."</string>
@@ -184,6 +186,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"לא ניתן לזהות את הפנים. יש להשתמש בטביעת אצבע במקום."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"יש להשתמש בטביעת האצבע כדי להמשיך"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"לא ניתן לזהות את טביעת האצבע. יש להשתמש בנעילת המסך במקום."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"מתבצע חיפוש…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"סמל הפנים"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"לחצן מרחק מתצוגה של תאימות."</string>
@@ -456,6 +460,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"יש להקיש שוב כדי לפתוח את ההתראה"</string>
     <string name="tap_again" msgid="1315420114387908655">"צריך להקיש פעם נוספת"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"צריך להחליק כדי לפתוח"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"יש להקיש כדי לפתוח"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"יש להחליק למעלה כדי לנסות שוב"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"‏יש לבטל את הנעילה כדי להשתמש ב-NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"המכשיר הזה שייך לארגון שלך"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index a8fb87d..502f962 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"画面サイズに合わせて拡大"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"画面サイズに合わせて拡大"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"スクリーンショット"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock は無効です"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"画像を送信しました"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"スクリーンショットを保存中..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"スクリーンショットを保存しています..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"顔を認識できません。指紋認証を使用してください。"</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"続行するには指紋認証を使用してください"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"指紋を認識できません。代わりに画面ロックを使用してください。"</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"顔を認証しています…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"顔アイコン"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"互換ズームボタン。"</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"開くにはもう一度タップしてください"</string>
     <string name="tap_again" msgid="1315420114387908655">"もう一度タップしてください"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"開くには上にスワイプします"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"押すと開きます"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"上にスワイプしてもう一度お試しください"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC を使用するには、ロックを解除してください"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"これは組織が所有するデバイスです"</string>
@@ -798,7 +802,7 @@
       <item quantity="other">%d分</item>
       <item quantity="one">%d分</item>
     </plurals>
-    <string name="battery_panel_title" msgid="5931157246673665963">"電池の使用状況"</string>
+    <string name="battery_panel_title" msgid="5931157246673665963">"バッテリーの使用状況"</string>
     <string name="battery_detail_charging_summary" msgid="8821202155297559706">"充電中はバッテリー セーバーは利用できません"</string>
     <string name="battery_detail_switch_title" msgid="6940976502957380405">"バッテリー セーバー"</string>
     <string name="battery_detail_switch_summary" msgid="3668748557848025990">"パフォーマンスとバックグラウンド データを制限します"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index da4f5bc..38c59bf 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"მასშტაბი შეცვალეთ ეკრანის შესავსებად."</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"გაწიეთ ეკრანის შესავსებად."</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"ეკრანის ანაბეჭდი"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock გათიშულია"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"გაიგზავნა სურათი"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"სკრინშოტის შენახვა…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"ეკრანის სურათის შენახვა…"</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"სახის ამოცნობა ვერ ხერხდება. სანაცვლოდ თითის ანაბეჭდი გამოიყენეთ."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"გასაგრძელებლად გამოიყენეთ თქვენი თითის ანაბეჭდი"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"თითის ანაბეჭდის ამოცნობა ვერ ხერხდება. სანაცვლოდ, გამოიყენეთ ეკრანის დაბლოკვა."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"მიმდინარეობს თქვენი ძიება…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"სახის ხატულა"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"თავსებადი მასშტაბირების ღილაკი."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"შეეხეთ ისევ გასახსნელად"</string>
     <string name="tap_again" msgid="1315420114387908655">"შეეხეთ ხელახლა"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"გასახსნელად გადაფურცლეთ ზემოთ"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"დააჭირეთ გასახსნელად"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"ხელახლა საცდელად გადაფურცლეთ ზემოთ"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"განბლოკეთ NFC-ის გამოსაყენებლად"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"ამ მოწყობილობას ფლობს თქვენი ორგანიზაცია"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index c15c6be..afb46e8 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Экранды толтыру үшін ұлғайту"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Экранды толтыру үшін созу"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Скриншот"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock өшірілді"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"сурет жіберілді"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Скриншотты сақтауда…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Скриншотты сақтауда…"</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Бет танылмады. Орнына саусақ ізін пайдаланыңыз."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Жалғастыру үшін саусақ ізін пайдаланыңыз."</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Саусақ ізі танылмады. Орнына экран құлпын пайдаланыңыз."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Бет ізделуде…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Бет белгішесі"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Үйлесімділік ұлғайту түймесі."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Ашу үшін қайта түртіңіз"</string>
     <string name="tap_again" msgid="1315420114387908655">"Қайта түртіңіз."</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Ашу үшін жоғары қарай сырғытыңыз."</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Ашу үшін басыңыз."</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Әрекетті қайталау үшін жоғары сырғытыңыз."</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC пайдалану үшін құлыпты ашыңыз."</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Бұл құрылғы ұйымыңызға тиесілі."</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 31de2be..9f4ddf3 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"ពង្រីក​​ដើម្បី​ឲ្យ​ពេញ​អេក្រង់"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"ទាញ​ដើម្បី​ឲ្យ​ពេញ​អេក្រង់"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"រូបថតអេក្រង់"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"បានបិទ Smart Lock"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"បាន​ផ្ញើរូបភាព"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"កំពុង​រក្សាទុក​រូបថត​អេក្រង់…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"កំពុង​រក្សាទុក​រូបថត​អេក្រង់..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"មិនអាចសម្គាល់មុខបានទេ។ សូមប្រើស្នាមម្រាមដៃជំនួសវិញ។"</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"ប្រើ​ស្នាមម្រាមដៃ​របស់អ្នក ដើម្បីបន្ត"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"មិនអាចសម្គាល់​ស្នាមម្រាមដៃបានទេ។ សូមប្រើសោ​អេក្រង់ជំនួសវិញ។"</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"កំពុងស្វែងរកអ្នក…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"រូប​ផ្ទៃមុខ"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"ប៊ូតុង​ពង្រីក​ត្រូវ​គ្នា។"</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"ប៉ះ​ម្ដង​ទៀត ដើម្បី​បើក"</string>
     <string name="tap_again" msgid="1315420114387908655">"ចុច​ម្ដងទៀត"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"អូសឡើងលើ​ដើម្បីបើក"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"ចុច ដើម្បីបើក"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"អូសឡើងលើ ដើម្បី​ព្យាយាម​ម្ដងទៀត"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"ដោះសោ ដើម្បីប្រើ NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"ឧបករណ៍​នេះគឺជា​កម្មសិទ្ធិរបស់​ស្ថាប័ន​អ្នក"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 9b9138c..36d3d7d 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -77,6 +77,8 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"ಪರದೆ ತುಂಬಿಸಲು ಝೂಮ್ ಮಾಡು"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"ಪರದೆ ತುಂಬಿಸಲು ವಿಸ್ತಾರಗೊಳಿಸು"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್"</string>
+    <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
+    <skip />
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ಚಿತ್ರವನ್ನು ಕಳುಹಿಸಲಾಗಿದೆ"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್ ಉಳಿಸಲಾಗುತ್ತಿದೆ…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್ ಉಳಿಸಲಾಗುತ್ತಿದೆ…"</string>
@@ -184,6 +186,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"ಮುಖ ಗುರುತಿಸಲಾಗುತ್ತಿಲ್ಲ ಬದಲಿಗೆ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಬಳಸಿ."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"ಮುಂದುವರಿಸಲು ನಿಮ್ಮ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಬಳಸಿ"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅನ್ನು ಗುರುತಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಬದಲಾಗಿ ಪರದೆಲಾಕ್ ಬಳಸಿ."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"ನಿಮಗಾಗಿ ಹುಡುಕಲಾಗುತ್ತಿದೆ…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"ಮುಖದ ಐಕಾನ್‌"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"ಹೊಂದಾಣಿಕೆಯ ಝೂಮ್ ಬಟನ್."</string>
@@ -452,6 +456,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"ತೆರೆಯಲು ಮತ್ತೆ ಟ್ಯಾಪ್‌ ಮಾಡಿ"</string>
     <string name="tap_again" msgid="1315420114387908655">"ಪುನಃ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"ತೆರೆಯಲು ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"ತೆರೆಯಲು ಒತ್ತಿ"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಲು ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC ಬಳಸಲು ಅನ್‌ಲಾಕ್ ಮಾಡಿ"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"ಈ ಸಾಧನವು ನಿಮ್ಮ ಸಂಸ್ಥೆಗೆ ಸೇರಿದೆ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 9aaea76..a846bc1 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"전체화면 모드로 확대"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"전체화면 모드로 확대"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"스크린샷"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock 사용 중지됨"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"이미지 보냄"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"캡쳐화면 저장 중..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"캡쳐화면 저장 중..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"얼굴을 인식할 수 없습니다. 대신 지문을 사용하세요."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"계속하려면 지문을 사용하세요."</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"지문을 인식할 수 없습니다. 화면 잠금을 대신 사용하세요."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"찾는 중..."</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"얼굴 아이콘"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"호환성 확대/축소 버튼입니다."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"다시 탭하여 열기"</string>
     <string name="tap_again" msgid="1315420114387908655">"다시 탭하세요."</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"위로 스와이프하여 열기"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"열려면 누르세요"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"위로 스와이프하여 다시 시도해 주세요"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"잠금 해제하여 NFC 사용"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"내 조직에 속한 기기입니다."</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 0a0bf4c..124dc53 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Экрнд тлтр ү. чен өлч өзг"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Экранды толтуруу ү-н чоюу"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Скриншот"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock өчүрүлдү"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"сүрөт жөнөттү"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Скриншот сакталууда…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Скриншот сакталууда..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Жүз таанылбай жатат. Манжа изин колдонуңуз."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Улантуу үчүн манжаңызды сканерге тийгизиңиз"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Манжа изи таанылбай жатат. Эрканды кулпулоо функциясын колдонуңуз."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Жүзүңүз изделүүдө…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Жүздүн сүрөтчөсү"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Масштабды сыйыштыруу баскычы."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Ачуу үчүн кайра таптап коюңуз"</string>
     <string name="tap_again" msgid="1315420114387908655">"Кайра таптап коюңуз"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Ачуу үчүн өйдө сүрүңүз"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Ачуу үчүн басыңыз"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Кайталоо үчүн экранды өйдө сүрүңүз"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC колдонуу үчүн түзмөктүн кулпусун ачыңыз"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Бул түзмөк уюмуңузга таандык"</string>
@@ -706,11 +710,11 @@
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"Эскертмелерди башкаруу каражаттары"</string>
     <string name="tuner_full_importance_settings_on" msgid="917981436602311547">"Күйүк"</string>
     <string name="tuner_full_importance_settings_off" msgid="5580102038749680829">"Өчүк"</string>
-    <string name="power_notification_controls_description" msgid="1334963837572708952">"Бул функциянын жардамы менен, ар бир колдонмо үчүн билдирменин маанилүүлүгүн 0дон 5ке чейин бааласаңыз болот. \n\n"<b>"5-деңгээл"</b>" \n- Билдирмелер тизмесинин өйдө жагында көрсөтүлөт \n- Билдирмелер толук экранда көрсөтүлөт \n- Калкып чыгуучу билдирмелерге уруксат берилет \n\n"<b>"4-деңгээл"</b>" \n- Билдирмелер толук экранда көрүнбөйт \n- Калкып чыгуучу билдирмелерге уруксат берилет \n\n"<b>"3-деңгээл"</b>" \n- Билдирмелер толук экранда көрүнбөйт \n- Калкып чыгуучу билдирмелерге тыюу салынат \n\n"<b>"2-деңгээл"</b>" \n- Билдирмелер толук экранда көрүнбөйт \n- Калкып чыгуучу билдирмелерге тыюу салынат \n- Эч качан үн чыкпайт же дирилдебейт \n\n"<b>"1-деңгээл"</b>" \n- Билдирмелер толук экранда көрүнбөйт \n- Калкып чыгуучу билдирмелерге тыюу салынат \n- Эч качан үн чыкпайт же дирилдебейт \n- Кулпуланган экрандан жана абал тилкесинен жашырылат \n- Билдирмелер тизмесинин ылдый жагында көрсөтүлөт \n\n"<b>"0-деңгээл"</b>" \n- Колдонмодон алынган бардык билдирмелер бөгөттөлөт"</string>
+    <string name="power_notification_controls_description" msgid="1334963837572708952">"Бул функциянын жардамы менен, ар бир колдонмо үчүн билдирменин маанилүүлүгүн 0дон 5ке чейин бааласаңыз болот. \n\n"<b>"5-деңгээл"</b>" \n- Билдирмелер тизмесинин өйдө жагында көрүнөт \n- Билдирмелер толук экранда көрүнөт \n- Калкып чыгуучу билдирмелерге уруксат берилет \n\n"<b>"4-деңгээл"</b>" \n- Билдирмелер толук экранда көрүнбөйт \n- Калкып чыгуучу билдирмелерге уруксат берилет \n\n"<b>"3-деңгээл"</b>" \n- Билдирмелер толук экранда көрүнбөйт \n- Калкып чыгуучу билдирмелерге тыюу салынат \n\n"<b>"2-деңгээл"</b>" \n- Билдирмелер толук экранда көрүнбөйт \n- Калкып чыгуучу билдирмелерге тыюу салынат \n- Эч качан үн чыкпайт же дирилдебейт \n\n"<b>"1-деңгээл"</b>" \n- Билдирмелер толук экранда көрүнбөйт \n- Калкып чыгуучу билдирмелерге тыюу салынат \n- Эч качан үн чыкпайт же дирилдебейт \n- Кулпуланган экрандан жана абал тилкесинен жашырылат \n- Билдирмелер тизмесинин ылдый жагында көрүнөт \n\n"<b>"0-деңгээл"</b>" \n- Колдонмодон алынган бардык билдирмелер бөгөттөлөт"</string>
     <string name="notification_header_default_channel" msgid="225454696914642444">"Билдирмелер"</string>
     <string name="notification_channel_disabled" msgid="928065923928416337">"Мындан ары бул билдирмелер сизге көрүнбөйт"</string>
     <string name="notification_channel_minimized" msgid="6892672757877552959">"Бул билдирмелер кичирейтилет"</string>
-    <string name="notification_channel_silenced" msgid="1995937493874511359">"Бул билдирмелер үнсүз көрсөтүлөт"</string>
+    <string name="notification_channel_silenced" msgid="1995937493874511359">"Бул билдирмелер үнсүз көрүнөт"</string>
     <string name="notification_channel_unsilenced" msgid="94878840742161152">"Бул билдирмелер тууралуу кабарлап турабыз"</string>
     <string name="inline_blocking_helper" msgid="2891486013649543452">"Адатта мындай билдирмелерди өткөрүп жибересиз. \nАлар көрүнө берсинби?"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"Бүттү"</string>
diff --git a/packages/SystemUI/res/values-land/config.xml b/packages/SystemUI/res/values-land/config.xml
index ea456d8..ac4dfd2 100644
--- a/packages/SystemUI/res/values-land/config.xml
+++ b/packages/SystemUI/res/values-land/config.xml
@@ -28,9 +28,6 @@
     <!-- The number of columns that the top level tiles span in the QuickSettings -->
     <integer name="quick_settings_user_time_settings_tile_span">2</integer>
 
-    <!-- We have only space for one notification on phone landscape layouts. -->
-    <integer name="keyguard_max_notification_count">1</integer>
-
     <!-- orientation of the dead zone when touches have recently occurred elsewhere on screen -->
     <integer name="navigation_bar_deadzone_orientation">1</integer>
 
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index ee0e4b3..fc5edf3 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -41,7 +41,7 @@
     <dimen name="battery_detail_graph_space_top">9dp</dimen>
     <dimen name="battery_detail_graph_space_bottom">9dp</dimen>
 
-    <dimen name="qs_detail_margin_top">14dp</dimen>
+    <dimen name="qs_detail_header_margin_top">14dp</dimen>
 
     <dimen name="volume_tool_tip_top_margin">12dp</dimen>
     <dimen name="volume_row_slider_height">128dp</dimen>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 53a0ace..c27394c 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"ຊູມໃຫ້ເຕັມໜ້າຈໍ"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"ປັບໃຫ້ເຕັມໜ້າຈໍ"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"ພາບໜ້າຈໍ"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"ປິດການນຳໃຊ້ Smart Lock ແລ້ວ"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ສົ່ງຮູບແລ້ວ"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"ກຳລັງບັນທຶກຮູບໜ້າຈໍ"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"ກຳລັງບັນທຶກພາບໜ້າຈໍ..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"ບໍ່ສາມາດຈຳແນກໜ້າໄດ້. ກະລຸນາໃຊ້ລາຍນິ້ວມືແທນ."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"ກະລຸນາໃຊ້ລາຍນິ້ວມືຂອງທ່ານເພື່ອສືບຕໍ່"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"ບໍ່ສາມາດຈຳແນກລາຍນິ້ວມືໄດ້. ກະລຸນາໃຊ້ການລອກໜ້າຈໍແທນ."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"ກຳລັງຊອກຫາທ່ານ…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"ໄອຄອນໃບໜ້າ"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"ປຸ່ມຊູມທີ່ໃຊ້ຮ່ວມກັນໄດ້."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"ແຕະ​ອີກ​ຄັ້ງ​ເພື່ອ​ເປີດ"</string>
     <string name="tap_again" msgid="1315420114387908655">"ແຕະອີກເທື່ອໜຶ່ງ"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"ປັດຂຶ້ນເພື່ອເປີດ"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"ກົດເພື່ອເປີດ"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"ປັດຂຶ້ນເພື່ອລອງໃໝ່"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"ປົດລັອກເພື່ອໃຊ້ NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"ອຸປະກອນນີ້ເປັນຂອງອົງການທ່ານ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 56f187e..a1cd602 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Keisti mast., kad atit. ekr."</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Ištempti, kad atit. ekr."</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Ekrano kopija"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"„Smart Lock“ išjungta"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"išsiuntė vaizdą"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Išsaugoma ekrano kopija..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Išsaugoma ekrano kopija..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Veidas neatpažintas. Naudokite kontrolinį kodą."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Jei norite tęsti, naudokite kontrolinį kodą"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Nepavyko atpažinti kontrolinio kodo. Naudokite ekrano užraktą."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Ieškoma jūsų…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Veido piktograma"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Suderinamumo priartinimo mygtukas."</string>
@@ -456,6 +459,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Palieskite dar kartą, kad atidarytumėte"</string>
     <string name="tap_again" msgid="1315420114387908655">"Palieskite dar kartą"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Perbraukite aukštyn, kad atidarytumėte"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Paspauskite, kad atidarytumėte"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Jei norite bandyti dar kartą, perbraukite aukštyn"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Norėdami naudoti NFC, atrakinkite"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Šis įrenginys priklauso jūsų organizacijai"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 4522b98..b0da7f7 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Tālumm., lai aizp. ekr."</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Stiepiet, lai aizp. ekr."</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Ekrānuzņēmums"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Sistēma Smart Lock ir atspējota"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"nosūtīts attēls"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Saglabā ekrānuzņēmumu…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Notiek ekrānuzņēmuma saglabāšana..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Nevar atpazīt seju. Lietojiet pirksta nospiedumu."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Lai turpinātu, izmantojiet pirksta nospiedumu."</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Nevar atpazīt pirksta nospiedumu. Izmantojiet ekrāna bloķēšanu."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Notiek jūsu sejas meklēšana…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Sejas ikona"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Saderības tālummaiņas poga."</string>
@@ -454,6 +457,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Pieskarieties vēlreiz, lai atvērtu"</string>
     <string name="tap_again" msgid="1315420114387908655">"Pieskarieties vēlreiz"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Velciet augšup, lai atvērtu"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Nospiediet, lai atvērtu"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Velciet augšup, lai mēģinātu vēlreiz"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Atbloķējiet ierīci, lai izmantotu NFC."</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Šī ierīce pieder jūsu organizācijai."</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 9a8de86..bee9bff 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Зумирај да се исполни екранот"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Растегни да се исполни екранот"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Слика од екранот"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Оневозможено е Smart Lock"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"испрати слика"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Сликата на екранот се зачувува..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Сликата на екранот се зачувува..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Не се препознава ликот. Користете отпечаток."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Употребете го отпечатокот за да продолжите"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Не се препознава отпечатокот. Користете заклучување екран."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Ве бараме вас…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Икона за лице"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Копче за компатибилност на зум."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Допрете повторно за да се отвори"</string>
     <string name="tap_again" msgid="1315420114387908655">"Допрете повторно"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Повлечете за да отворите"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Притиснете за да отворите"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Повлечете нагоре за да се обидете повторно"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Отклучете за да користите NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Уредов е во сопственост на организацијата"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index e798c7d..061f43c 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"സ്‌ക്രീനിൽ ഉൾക്കൊള്ളിക്കാൻ സൂം ചെയ്യുക"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"സ്‌ക്രീനിൽ ഉൾക്കൊള്ളിക്കാൻ വലിച്ചുനീട്ടുക"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"സ്ക്രീൻഷോട്ട്"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock പ്രവർത്തനരഹിതമാക്കി"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ചിത്രം അയച്ചു"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"സ്‌ക്രീൻഷോട്ട് സംരക്ഷിക്കുന്നു..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"സ്‌ക്രീൻഷോട്ട് സംരക്ഷിക്കുന്നു..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"മുഖം തിരിച്ചറിയാനായില്ല. പകരം ഫിംഗർപ്രിന്റ് ഉപയോഗിക്കൂ."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"തുടരുന്നതിന് നിങ്ങളുടെ ഫിംഗർപ്രിന്റ് ഉപയോഗിക്കുക"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"ഫിംഗർപ്രിന്റ് തിരിച്ചറിയാനാകുന്നില്ല. പകരം സ്‌ക്രീൻ ലോക്ക് ഉപയോഗിക്കുക."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"നിങ്ങൾക്കായി തിരയുന്നു…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"മുഖത്തിന്റെ ഐക്കൺ"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"അനുയോജ്യതാ സൂം ബട്ടൺ."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"തുറക്കുന്നതിന് വീണ്ടും ടാപ്പുചെയ്യുക"</string>
     <string name="tap_again" msgid="1315420114387908655">"വീണ്ടും ടാപ്പ് ചെയ്യുക"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"തുറക്കാൻ മുകളിലോട്ട് സ്വൈപ്പ് ചെയ്യുക"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"തുറക്കാൻ അമർത്തുക"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"വീണ്ടും ശ്രമിക്കാൻ മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്യുക"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC ഉപയോഗിക്കാൻ അൺലോക്ക് ചെയ്യുക"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"ഈ ഉപകരണം നിങ്ങളുടെ സ്ഥാപനത്തിന്റേതാണ്"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 0e4cac02..8bf0dbc 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Дэлгэц дүүргэх бол өсгөнө үү"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Дэлгэц дүүргэх бол татна уу"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Дэлгэцийн зураг дарах"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Ухаалаг түгжээг идэвхгүй болгосон"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"зураг илгээсэн"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Дэлгэцийн агшинг хадгалж байна…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Дэлгэцийн агшинг хадгалж байна…"</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Царай таних боломжгүй. Оронд нь хурууны хээ ашигла"</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Үргэлжлүүлэхийн тулд хурууныхаа хээг ашиглана уу"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Хурууны хээг таних боломжгүй. Оронд нь дэлгэцийн түгжээ ашиглана уу."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Таныг хайж байна…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Царайны дүрс тэмдэг"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Тохиромжтой өсгөх товч."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Нээхийн тулд дахин товшино уу"</string>
     <string name="tap_again" msgid="1315420114387908655">"Дaхин товшино уу"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Нээхийн тулд дээш шударна уу"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Нээхийн тулд дарна уу"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Дахин оролдохын тулд дээш шударна уу"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC-г ашиглахын тулд түгжээг тайлна уу"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Энэ төхөөрөмж танай байгууллагад харьяалагддаг"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 45a9a1b..7d23729 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -77,6 +77,8 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"स्क्रीन भरण्यासाठी झूम करा"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"स्क्रीन भरण्यासाठी ताणा"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"स्क्रीनशॉट"</string>
+    <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
+    <skip />
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"इमेज पाठवली आहे"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"स्क्रीनशॉट सेव्ह करत आहे…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"स्क्रीनशॉट सेव्ह करत आहे…"</string>
@@ -148,7 +150,7 @@
     <string name="voice_assist_label" msgid="3725967093735929020">"व्हॉइस सहाय्य उघडा"</string>
     <string name="camera_label" msgid="8253821920931143699">"कॅमेरा उघडा"</string>
     <string name="cancel" msgid="1089011503403416730">"रद्द करा"</string>
-    <string name="biometric_dialog_confirm" msgid="2005978443007344895">"खात्री करा"</string>
+    <string name="biometric_dialog_confirm" msgid="2005978443007344895">"कंफर्म करा"</string>
     <string name="biometric_dialog_try_again" msgid="8575345628117768844">"पुन्हा प्रयत्न करा"</string>
     <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"ऑथेंटिकेशन रद्द करण्यासाठी टॅप करा"</string>
     <string name="biometric_dialog_face_icon_description_idle" msgid="4351777022315116816">"कृपया पुन्हा प्रयत्न करा"</string>
@@ -184,6 +186,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"चेहरा ओळखू शकत नाही. त्याऐवजी फिंगरप्रिंट वापरा."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"पुढे सुरू ठेवण्‍यासाठी तुमची फिंगरप्रिंट वापरा"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"फिंगरप्रिंट ओळखता आली नाही. त्याऐवजी स्क्रीन लॉक वापरा."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"तुमच्यासाठी शोधत आहे…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"चेहरा आयकन"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"सुसंगतता झूम बटण."</string>
@@ -452,6 +456,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"उघडण्यासाठी पुन्हा टॅप करा"</string>
     <string name="tap_again" msgid="1315420114387908655">"पुन्हा टॅप करा"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"उघडण्यासाठी वर स्वाइप करा"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"उघडण्यासाठी दाबा"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"पुन्हा प्रयत्न करण्यासाठी वर स्‍वाइप करा"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC वापरण्यासाठी स्क्रीन अनलॉक करा"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"हे डिव्हाइस तुमच्या संस्थेचे आहे"</string>
@@ -550,7 +555,7 @@
     <string name="disable_vpn" msgid="482685974985502922">"VPN अक्षम करा"</string>
     <string name="disconnect_vpn" msgid="26286850045344557">"VPN डिस्कनेक्ट करा"</string>
     <string name="monitoring_button_view_policies" msgid="3869724835853502410">"धोरणे पहा"</string>
-    <string name="monitoring_button_view_controls" msgid="8316440345340701117">"नियंत्रणे पाहा"</string>
+    <string name="monitoring_button_view_controls" msgid="8316440345340701117">"नियंत्रणे पहा"</string>
     <string name="monitoring_description_named_management" msgid="505833016545056036">"हे डिव्हाइस <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> चे आहे.\n\nतुमचा आयटी ॲडमिन सेटिंग्ज, कॉर्पोरेट अ‍ॅक्सेस, ॲप्‍स, तुमच्‍या डिव्‍हाइसशी संबंधित डेटा आणि तुमच्‍या डिव्‍हाइसच्‍या स्‍थानाची माहिती यांचे परीक्षण व व्‍यवस्‍थापन करू शकतो.\n\nअधिक माहितीसाठी तुमच्‍या आयटी ॲडमिनशी संपर्क साधा."</string>
     <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"<xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g> कदाचित या डिव्हाइसशी संलग्न असलेला डेटा अ‍ॅक्सेस करू शकते, ॲप्सचे व्यवस्थापन करू शकते आणि ही डिव्हाइस सेटिंग्ज बदलू शकते.\n\nतुम्हाला प्रश्न असल्यास, <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g> शी संपर्क साधा."</string>
     <string name="monitoring_description_management" msgid="4308879039175729014">"हे डिव्हाइस तुमच्या संस्थेचे आहे.\n\nतुमचा आयटी ॲडमिन सेटिंग्ज, कॉर्पोरेट अ‍ॅक्सेस, ॲप्‍स, तुमच्‍या डिव्‍हाइसशी संबंधित डेटा आणि तुमच्‍या डिव्‍हाइसच्‍या स्‍थानाची माहिती यांचे परीक्षण व व्‍यवस्‍थापन करू शकतो.\n\nअधिक माहितीसाठी तुमच्‍या आयटी ॲडमिनशी संपर्क साधा."</string>
@@ -648,7 +653,7 @@
     <string name="output_title" msgid="3938776561655668350">"मीडिया आउटपुट"</string>
     <string name="output_calls_title" msgid="7085583034267889109">"फोन कॉल आउटपुट"</string>
     <string name="output_none_found" msgid="5488087293120982770">"कोणतीही डिव्हाइस सापडली नाहीत"</string>
-    <string name="output_none_found_service_off" msgid="935667567681386368">"कोणतीही डिव्हाइस सापडली नाहीत. <xliff:g id="SERVICE">%1$s</xliff:g> सुरू करून पाहा"</string>
+    <string name="output_none_found_service_off" msgid="935667567681386368">"कोणतीही डिव्हाइस सापडली नाहीत. <xliff:g id="SERVICE">%1$s</xliff:g> सुरू करून पहा"</string>
     <string name="output_service_bt" msgid="4315362133973911687">"ब्लूटूथ"</string>
     <string name="output_service_wifi" msgid="9003667810868222134">"वाय-फाय"</string>
     <string name="output_service_bt_wifi" msgid="7186882540475524124">"ब्लूटूथ आणि वाय-फाय"</string>
@@ -751,7 +756,7 @@
     <string name="notification_multichannel_desc" msgid="7414593090056236179">"या सूचनांचा संच येथे कॉंफिगर केला जाऊ शकत नाही"</string>
     <string name="notification_delegate_header" msgid="1264510071031479920">"प्रॉक्सी केलेल्या सूचना"</string>
     <string name="notification_channel_dialog_title" msgid="6856514143093200019">"सर्व <xliff:g id="APP_NAME">%1$s</xliff:g> वरील सूचना"</string>
-    <string name="see_more_title" msgid="7409317011708185729">"आणखी पाहा"</string>
+    <string name="see_more_title" msgid="7409317011708185729">"आणखी पहा"</string>
     <string name="appops_camera" msgid="5215967620896725715">"हे अ‍ॅप कॅमेरा वापरत आहे."</string>
     <string name="appops_microphone" msgid="8805468338613070149">"हे अ‍ॅप मायक्रोफोन वापरत आहे."</string>
     <string name="appops_overlay" msgid="4822261562576558490">"हे अ‍ॅप स्क्रीनवरील इतर अ‍ॅप्स वर प्रदर्शित होत आहे."</string>
@@ -935,14 +940,14 @@
     <string name="thermal_shutdown_title" msgid="2702966892682930264">"तापल्‍यामुळे फोन बंद झाला"</string>
     <string name="thermal_shutdown_message" msgid="6142269839066172984">"तुमचा फोन आता नेहमीप्रमाणे काम करत आहे.\nअधिक माहितीसाठी टॅप करा"</string>
     <string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"तुमचा फोन खूप तापलाय, म्हणून तो थंड होण्यासाठी बंद झाला आहे. तुमचा फोन आता व्‍यवस्थित सुरू आहे.\n\nतुम्ही असे केल्यास तुमचा फोन खूप तापेल:\n	•संसाधन केंद्रित अ‍ॅप वापरणे (गेमिंग, व्हिडिओ किंवा नेव्हिगेशन अ‍ॅप यासारखे)\n	•मोठ्या फाइल डाउनलोड किंवा अपलोड करणे\n	•उच्च तापमानामध्ये तुमचा फोन वापरणे"</string>
-    <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"काय काळजी घ्यावी ते पाहा"</string>
+    <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"काय काळजी घ्यावी ते पहा"</string>
     <string name="high_temp_title" msgid="2218333576838496100">"फोन ऊष्ण होत आहे"</string>
     <string name="high_temp_notif_message" msgid="1277346543068257549">"फोन थंड होईपर्यंत काही वैशिष्ट्ये मर्यादित केली.\nअधिक माहितीसाठी टॅप करा"</string>
     <string name="high_temp_dialog_message" msgid="3793606072661253968">"तुमचा फोन स्वयंचलितपणे थंड होईल. तुम्ही अद्यापही तुमचा फोन वापरू शकता परंतु तो कदाचित धीमेपणे कार्य करेल.\n\nतुमचा फोन एकदा थंड झाला की, तो सामान्यपणे कार्य करेल."</string>
-    <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"काय काळजी घ्यावी ते पाहा"</string>
+    <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"काय काळजी घ्यावी ते पहा"</string>
     <string name="high_temp_alarm_title" msgid="2359958549570161495">"चार्जर अनप्लग करा"</string>
     <string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"हे डिव्हाइस चार्ज करताना समस्या आहे. पॉवर अडॅप्टर अनप्लग करा आणि शक्य तेवढी काळजी घ्या कदाचित केबल गरम असू शकते."</string>
-    <string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"काय काळजी घ्यावी ते पाहा"</string>
+    <string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"काय काळजी घ्यावी ते पहा"</string>
     <string name="lockscreen_shortcut_left" msgid="1238765178956067599">"डावा शॉर्टकट"</string>
     <string name="lockscreen_shortcut_right" msgid="4138414674531853719">"उजवा शॉर्टकट"</string>
     <string name="lockscreen_unlock_left" msgid="1417801334370269374">"डावा शॉर्टकट देखील अनलॉक करतो"</string>
@@ -1032,7 +1037,7 @@
     <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"फुल स्क्रीन मॅग्निफाय करा"</string>
     <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रीनचा काही भाग मॅग्निफाय करा"</string>
     <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"स्विच करा"</string>
-    <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"अ‍ॅक्सेसिबिलिटी जेश्चर हे आता अ‍ॅक्सेसिबिलिटी बटण आहे \n\n"<annotation id="link">"सेटिंग्ज पाहा"</annotation></string>
+    <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"अ‍ॅक्सेसिबिलिटी जेश्चर हे आता अ‍ॅक्सेसिबिलिटी बटण आहे \n\n"<annotation id="link">"सेटिंग्ज पहा"</annotation></string>
     <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"तुम्ही अ‍ॅक्सेसिबिलिटी जेश्चरवरून बटणवर स्विच करू शकता \n\n"<annotation id="link">"सेटिंग्ज"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"बटण तात्पुरते लपवण्यासाठी ते कोपर्‍यामध्ये हलवा"</string>
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"वर डावीकडे हलवा"</string>
@@ -1141,7 +1146,7 @@
     <string name="status_before_loading" msgid="1500477307859631381">"आशय लवकरच दाखवला जाईल"</string>
     <string name="missed_call" msgid="4228016077700161689">"मिस्ड कॉल"</string>
     <string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
-    <string name="people_tile_description" msgid="8154966188085545556">"अलीकडील मेसेज, मिस्ड कॉल आणि स्टेटस अपडेट पाहा"</string>
+    <string name="people_tile_description" msgid="8154966188085545556">"अलीकडील मेसेज, मिस्ड कॉल आणि स्टेटस अपडेट पहा"</string>
     <string name="people_tile_title" msgid="6589377493334871272">"संभाषण"</string>
     <string name="paused_by_dnd" msgid="7856941866433556428">"व्यत्यय आणू नका द्वारे थांबवले गेले"</string>
     <string name="new_notification_text_content_description" msgid="2915029960094389291">"<xliff:g id="NAME">%1$s</xliff:g> यांनी मेसेज पाठवला: <xliff:g id="NOTIFICATION">%2$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 2349e28..b662271 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Zum untuk memenuhi skrin"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Regang utk memenuhi skrin"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Tangkapan skrin"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock dilumpuhkan"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"menghantar imej"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Menyimpan tangkapan skrin..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Menyimpan tangkapan skrin..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Tidak mengenali wajah. Gunakan cap jari."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Gunakan cap jari anda untuk teruskan"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Tidak mengenali cap jari. Sebaliknya, gunakan kunci skrin."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Mencari anda…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Ikon wajah"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Butang zum keserasian."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Ketik lagi untuk membuka"</string>
     <string name="tap_again" msgid="1315420114387908655">"Ketik sekali lagi"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Leret ke atas untuk buka"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Tekan untuk buka"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Leret ke atas untuk mencuba lagi"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Buka kunci untuk menggunakan NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Peranti ini milik organisasi anda"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 23de47f..900b98a 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"ဇူးမ်အပြည့်ဆွဲခြင်း"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"ဖန်သားပြင်အပြည့်ဆန့်ခြင်း"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"ဖန်သားပြင်ဓာတ်ပုံ"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock ပိတ်ထားသည်"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ပုံပို့ထားသည်"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"ဖန်သားပြင်ဓါတ်ပုံသိမ်းစဉ်.."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"ဖန်သားပြင်ဓါတ်ပုံရိုက်ခြင်းအား သိမ်းဆည်းပါမည်"</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"မျက်နှာကို မမှတ်မိပါ။ လက်ဗွေကို အစားထိုးသုံးပါ။"</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"ရှေ့ဆက်ရန် သင့်လက်ဗွေကို သုံးပါ"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"လက်ဗွေကို မမှတ်မိပါ။ ဖန်သားပြင်လော့ခ်ချခြင်းကို အစားထိုးသုံးပါ။"</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"သင့်ကို ရှာဖွေနေသည်…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"မျက်နှာသင်္ကေတ"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"အံဝင်ခွင်ကျ ဇူးမ်ခလုတ်"</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"ဖွင့်ရန် ထပ်ပြီး ပုတ်ပါ"</string>
     <string name="tap_again" msgid="1315420114387908655">"ထပ်တို့ပါ"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"ဖွင့်ရန် အပေါ်သို့ပွတ်ဆွဲပါ"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"ဖွင့်ရန် နှိပ်ပါ"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"ထပ်စမ်းကြည့်ရန် အပေါ်သို့ပွတ်ဆွဲပါ"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC ကို အသုံးပြုရန် လော့ခ်ဖွင့်ပါ"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"ဤစက်ကို သင့်အဖွဲ့အစည်းက ပိုင်ဆိုင်သည်"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 9c18b95..938234d 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Zoom for å fylle skjermen"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Strekk for å fylle skjerm"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Skjermdump"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock er slått av"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"har sendt et bilde"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Lagrer skjermdumpen …"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Lagrer skjermdumpen …"</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Ansiktet gjenkjennes ikke. Bruk fingeravtrykk."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Bruk fingeravtrykket for å fortsette"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Fingeravtrykket gjenkjennes ikke. Bruk skjermlås i stedet."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Ser etter deg …"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Ansiktikon"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Zoomknapp for kompatibilitet."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Trykk på nytt for å åpne"</string>
     <string name="tap_again" msgid="1315420114387908655">"Trykk igjen"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Sveip opp for å åpne"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Trykk for å åpne"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Sveip opp for å prøve igjen"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Lås opp for å bruke NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Denne enheten tilhører organisasjonen din"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 9ae2d43e2..440e8af 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -77,6 +77,8 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"स्क्रिन भर्न जुम गर्नुहोस्"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"स्क्रिन भर्न तन्काउनुहोस्"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"स्क्रिनसट"</string>
+    <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
+    <skip />
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"कुनै छवि पठाइयो"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"स्क्रिनसट बचत गर्दै…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"स्क्रिनसट बचत गर्दै…"</string>
@@ -184,6 +186,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"अनुहार पहिचान गर्न सकिएन। बरु फिंगरप्रिन्ट प्रयोग गर्नुहोस्।"</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"जारी राख्न आफ्नो फिंगरप्रिन्ट प्रयोग गर्नुहोस्"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"फिंगरप्रिन्ट पहिचान गर्न सकिएन। बरु स्क्रिन लक प्रयोग गर्नुहोस्।"</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"तपाईंलाई खोज्दै…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"अनुहारको आइकन"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"मिलाउने जुम बटन।"</string>
@@ -452,6 +456,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"खोल्न पुनः ट्याप गर्नुहोस्"</string>
     <string name="tap_again" msgid="1315420114387908655">"फेरि ट्याप गर्नुहोस्"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"खोल्न माथितिर स्वाइप गर्नुहोस्"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"अनलक गर्न प्रेस गर्नुहोस्"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"फेरि प्रयास गर्न माथितिर स्वाइप गर्नुहोस्"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC प्रयोग गर्न स्क्रिन अनलक गर्नुहोस्"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"यो डिभाइस तपाईंको सङ्गठनको स्वामित्वमा छ"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 155d1ad..66ec98a 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Zoom om scherm te vullen"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Rek uit v. schermvulling"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock staat uit"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"heeft een afbeelding gestuurd"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Screenshot opslaan..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Screenshot opslaan..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Gezicht niet herkend. Gebruik je vingerafdruk."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Gebruik je vingerafdruk om door te gaan."</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Vingerafdruk niet herkend. Gebruik in plaats daarvan de schermvergrendeling."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Jouw gezicht zoeken…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Gezichtspictogram"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Knop voor compatibiliteitszoom."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Tik nog eens om te openen"</string>
     <string name="tap_again" msgid="1315420114387908655">"Tik nog een keer"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Swipe omhoog om te openen"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Druk om te openen"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Swipe omhoog om het opnieuw te proberen"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Ontgrendel het apparaat om NFC te gebruiken"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Dit apparaat is eigendom van je organisatie"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 4d44690b..f76adfa 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -77,6 +77,8 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"ସ୍କ୍ରୀନ ଭରିବା ପାଇଁ ଜୁମ୍ କରନ୍ତୁ"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"ସ୍କ୍ରୀନ୍‌କୁ ଭରିବା ପାଇଁ ଟାଣନ୍ତୁ"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"ସ୍କ୍ରିନ୍‌ସଟ୍ ନିଅନ୍ତୁ"</string>
+    <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
+    <skip />
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ଏକ ଛବି ପଠାଯାଇଛି"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"ସ୍କ୍ରୀନଶଟ୍‍ ସେଭ୍‍ କରାଯାଉଛି…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"ସ୍କ୍ରୀନଶଟ୍‍ ସେଭ୍‍ କରାଯାଉଛି…"</string>
@@ -184,6 +186,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"ଫେସ୍ ଚିହ୍ନଟ କରିହେବ ନାହିଁ। ଟିପଚିହ୍ନ ବ୍ୟବହାର କରନ୍ତୁ।"</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"ଜାରି ରଖିବାକୁ ଆପଣଙ୍କ ଟିପଚିହ୍ନ ବ୍ୟବହାର କରନ୍ତୁ"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"ଟିପଚିହ୍ନକୁ ଚିହ୍ନଟ କରାଯାଇପାରିବ ନାହିଁ। ଏହା ପରିବର୍ତ୍ତେ ସ୍କ୍ରିନ୍ ଲକ୍ ବ୍ୟବହାର କରନ୍ତୁ।"</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"ଆପଣଙ୍କୁ ଚିହ୍ନଟ କରୁଛି…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"ମୁହଁ ଆଇକନ୍"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"କମ୍ପାଟିବିଲିଟୀ ଜୁମ୍ ବଟନ୍।"</string>
@@ -452,6 +456,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"ଖୋଲିବା ପାଇଁ ପୁଣି ଟାପ୍‍ କରନ୍ତୁ"</string>
     <string name="tap_again" msgid="1315420114387908655">"ପୁଣି ଟାପ୍ କରନ୍ତୁ"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"ଖୋଲିବା ପାଇଁ ଉପରକୁ ସ୍ୱାଇପ୍ କରନ୍ତୁ"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"ଖୋଲିବାକୁ ଦବାନ୍ତୁ"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"ପୁଣି ଚେଷ୍ଟା କରିବା ପାଇଁ ଉପରକୁ ସ୍ୱାଇପ୍ କରନ୍ତୁ"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC ବ୍ୟବହାର କରିବାକୁ ଅନଲକ୍ କରନ୍ତୁ"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"ଏହି ଡିଭାଇସଟି ଆପଣଙ୍କ ସଂସ୍ଥାର ଅଟେ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 7bb60a0..9d1edfa 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -77,6 +77,8 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"ਸਕ੍ਰੀਨ ਭਰਨ ਲਈ ਜ਼ੂਮ ਕਰੋ"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"ਸਕ੍ਰੀਨ ਭਰਨ ਲਈ ਸਟ੍ਰੈਚ ਕਰੋ"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"ਸਕ੍ਰੀਨਸ਼ਾਟ"</string>
+    <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
+    <skip />
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ਚਿੱਤਰ ਭੇਜਿਆ ਗਿਆ"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਸੁਰੱਖਿਅਤ ਕਰ ਰਿਹਾ ਹੈ…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਸੁਰੱਖਿਅਤ ਕਰ ਰਿਹਾ ਹੈ…"</string>
@@ -184,6 +186,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"ਚਿਹਰਾ ਨਹੀਂ ਪਛਾਣ ਸਕਦੇ। ਇਸਦੀ ਬਜਾਏ ਫਿੰਗਰਪ੍ਰਿੰਟ ਵਰਤੋ।"</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"ਜਾਰੀ ਰੱਖਣ ਲਈ ਆਪਣਾ ਫਿੰਗਰਪ੍ਰਿੰਟ ਵਰਤੋ"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦੀ ਪਛਾਣ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ। ਇਸਦੀ ਬਜਾਏ ਸਕ੍ਰੀਨ ਲਾਕ ਵਰਤੋ।"</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"ਤੁਹਾਡੀ ਪਛਾਣ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"ਚਿਹਰਾ ਪ੍ਰਤੀਕ"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"ਅਨੁਰੂਪਤਾ ਜ਼ੂਮ ਬਟਨ।"</string>
@@ -452,6 +456,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"ਖੋਲ੍ਹਣ ਲਈ ਦੁਬਾਰਾ ਟੈਪ ਕਰੋ"</string>
     <string name="tap_again" msgid="1315420114387908655">"ਦੁਬਾਰਾ ਟੈਪ ਕਰੋ"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"ਖੋਲ੍ਹਣ ਲਈ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"ਖੋਲ੍ਹਣ ਲਈ ਦਬਾਓ"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰਨ ਲਈ ਉੱਤੇ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC ਵਰਤਣ ਲਈ ਅਣਲਾਕ ਕਰੋ"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"ਇਹ ਡੀਵਾਈਸ ਤੁਹਾਡੀ ਸੰਸਥਾ ਨਾਲ ਸੰਬੰਧਿਤ ਹੈ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index ea6f225..b6412e7 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Powiększ, aby wypełnić ekran"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Rozciągnij, aby wypełnić ekran"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Zrzut ekranu"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Wyłączono Smart Lock"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"wysłano obraz"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Zapisywanie zrzutu ekranu..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Zapisywanie zrzutu ekranu..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Nie rozpoznaję twarzy. Użyj odcisku palca."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Użyj odcisku palca, aby kontynuować"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Nie rozpoznaję odcisku palca. Użyj blokady ekranu."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Szukam Cię…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Ikona twarzy"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Przycisk powiększenia na potrzeby zgodności."</string>
@@ -456,6 +459,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Kliknij ponownie, by otworzyć"</string>
     <string name="tap_again" msgid="1315420114387908655">"Kliknij jeszcze raz"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Przesuń w górę, by otworzyć"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Naciśnij, by otworzyć"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Przesuń w górę, by spróbować ponownie"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Odblokuj, by użyć komunikacji NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"To urządzenie należy do Twojej organizacji"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 2f1501b..7c3f864 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Zoom p/ preencher a tela"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Ampliar p/ preencher tela"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Capturar tela"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"O Smart Lock foi desativado"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"enviou uma imagem"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Salvando captura de tela..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Salvando captura de tela..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Não foi possível reconhecer o rosto Use a impressão digital."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Use sua impressão digital para continuar"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Não foi possível reconhecer a impressão digital. Use o bloqueio de tela."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Procurando você…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Ícone facial"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Botão de zoom da compatibilidade."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Toque novamente para abrir"</string>
     <string name="tap_again" msgid="1315420114387908655">"Toque novamente"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Deslize para cima para abrir"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Pressione para abrir"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Deslize para cima para tentar novamente"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desbloqueie para usar a NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Este dispositivo pertence à sua organização"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 563e99d..df48773 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Zoom para preencher o ecrã"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Esticar p. caber em ec. int."</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Captura de ecrã"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock desativado"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"enviou uma imagem"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"A guardar captura de ecrã..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"A guardar captura de ecrã..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Impos. reconh. rosto. Utilize a impressão digital."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Utilize a sua impressão digital para continuar"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Não é possível reconhecer a impressão digital. Em alternativa, utilize o bloqueio de ecrã."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"À sua procura…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Ícone de rosto"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Botão zoom de compatibilidade."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Toque novamente para abrir"</string>
     <string name="tap_again" msgid="1315420114387908655">"Toque novamente"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Deslize rapidamente para cima para abrir"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Prima para abrir"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Deslize rapidamente para cima para tentar novamente."</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desbloquear para utilizar o NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Este dispositivo pertence à sua entidade."</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 2f1501b..7c3f864 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Zoom p/ preencher a tela"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Ampliar p/ preencher tela"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Capturar tela"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"O Smart Lock foi desativado"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"enviou uma imagem"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Salvando captura de tela..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Salvando captura de tela..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Não foi possível reconhecer o rosto Use a impressão digital."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Use sua impressão digital para continuar"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Não foi possível reconhecer a impressão digital. Use o bloqueio de tela."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Procurando você…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Ícone facial"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Botão de zoom da compatibilidade."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Toque novamente para abrir"</string>
     <string name="tap_again" msgid="1315420114387908655">"Toque novamente"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Deslize para cima para abrir"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Pressione para abrir"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Deslize para cima para tentar novamente"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Desbloqueie para usar a NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Este dispositivo pertence à sua organização"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 2102600..c36f7e7 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Zoom pt. a umple ecranul"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Înt. pt. a umple ecranul"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Captură de ecran"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock dezactivat"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"a trimis o imagine"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Se salv. captura de ecran..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Se salvează captura de ecran..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Chipul nu a fost recunoscut. Folosiți amprenta."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Folosiți amprenta pentru a continua"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Amprenta nu a fost recunoscută. Folosiți blocarea ecranului."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Vă căutăm…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Pictograma chip"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Buton zoom pentru compatibilitate."</string>
@@ -454,6 +457,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Atingeți din nou pentru a deschide"</string>
     <string name="tap_again" msgid="1315420114387908655">"Atingeți din nou"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Glisați în sus pentru a deschide"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Apăsați pentru a deschide"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Glisați pentru a încerca din nou"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Deblocați pentru a folosi NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Dispozitivul aparține organizației dvs."</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index eeb1f3e..cc63a91 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Подогнать по размерам экрана"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Растянуть на весь экран"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Скриншот"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Функция Smart Lock отключена."</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"отправлено изображение"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Сохранение..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Сохранение..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Не удалось распознать лицо. Используйте отпечаток."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Чтобы продолжить, прикоснитесь пальцем к сканеру."</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Не удалось распознать отпечаток пальца. Используйте другой способ разблокировки экрана."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Поиск лица…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Значок лица"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Кнопка масштабирования (режим совместимости)"</string>
@@ -456,6 +459,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Нажмите ещё раз, чтобы открыть"</string>
     <string name="tap_again" msgid="1315420114387908655">"Нажмите ещё раз"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Проведите вверх, чтобы открыть"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Нажмите, чтобы открыть."</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Чтобы повторить попытку, проведите вверх"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Чтобы использовать NFC, разблокируйте устройство."</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Это устройство принадлежит вашей организации"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 247b7a5..c08d6f7 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"තිරය පිරවීමට විශාලනය කරන්න"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"තිරය පිරවීමට අදින්න"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"තිර රුව"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock අබලයි"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"රූපයක් එවන ලදී"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"තිර රුව සුරකිමින්…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"තිර රුව සුරැකෙමින් පවතී…"</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"මුහුණ හැඳිනිය නොහැක. ඒ වෙනුවට ඇඟිලි සලකුණ භාවිත ක."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"ඉදිරියට යාමට ඔබගේ ඇඟිලි සලකුණ භාවිත කරන්න"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"ඇඟිලි සලකුණ හඳුනා ගත නොහැකිය. ඒ වෙනුවට තිර අගුල භාවිත කරන්න."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"ඔබව සොයමින්…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"මුහුණ නිරූපකය"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"ගැළපෙන විශාලන බොත්තම."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"විවෘත කිරීමට නැවත තට්ටු කරන්න"</string>
     <string name="tap_again" msgid="1315420114387908655">"නැවත තට්ටු කරන්න"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"විවෘත කිරීමට ස්වයිප් කරන්න"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"විවෘත කිරීමට ඔබන්න"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"නැවත උත්සාහ කිරීමට ඉහළට ස්වයිප් කරන්න"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC භාවිත කිරීමට අගුලු හරින්න"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"මෙම උපාංගය ඔබේ සංවිධානයට අයිතිය"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 74b24bd..0aabf7c 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Priblížiť na celú obrazovku"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Na celú obrazovku"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Snímka obrazovky"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Funkcia Smart Lock je deaktivovaná"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"odoslal(a) obrázok"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Prebieha ukladanie snímky obrazovky..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Prebieha ukladanie snímky obrazovky..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Tvár sa nedá rozpoznať. Použite odtlačok prsta."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Pokračujte nasnímaním odtlačku prsta"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Odtlačok prsta sa nedá rozpoznať. Použite radšej zámku obrazovky."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Hľadáme vás…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Ikona tváre"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Tlačidlo úpravy veľkosti z dôvodu kompatibility."</string>
@@ -456,6 +459,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Upozornenie otvoríte opätovným klepnutím"</string>
     <string name="tap_again" msgid="1315420114387908655">"Klepnite znova"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Otvorte potiahnutím prstom nahor"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Stlačením otvoríte"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Potiahnutím nahor to skúste znova"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Ak chcete použiť NFC, odomknite"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Toto zariadenie patrí vašej organizácii"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index cfb89d4..ecf5ca7 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Povečava čez cel zaslon"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Raztegnitev čez zaslon"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Posnetek zaslona"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Storitev Smart Lock je onemogočena."</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"je poslal(-a) sliko"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Shranjev. posnetka zaslona ..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Shranjevanje posnetka zaslona ..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Obraza ni mogoče prepoznati. Uporabite prstni odtis."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Uporabite prstni odtis, če želite nadaljevati."</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Prstnega odtisa ni mogoče prepoznati. Uporabite odklepanje s poverilnico."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Preverjanje vašega obraza …"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Ikona obraza"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Gumb povečave za združljivost."</string>
@@ -456,6 +459,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Znova se dotaknite, da odprete"</string>
     <string name="tap_again" msgid="1315420114387908655">"Znova se dotaknite možnosti"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Povlecite navzgor, da odprete"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Pritisnite, če želite odpreti."</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Povlecite navzgor za vnovičen poskus"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Odklenite napravo, če želite uporabljati NFC."</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Ta naprava pripada vaši organizaciji"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index f7753c7..86bd5a1 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -77,6 +77,8 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Zmadho për të mbushur ekranin"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Shtrije për të mbushur ekranin"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Pamja e ekranit"</string>
+    <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
+    <skip />
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"dërgoi një imazh"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Po ruan pamjen e ekranit..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Po ruan pamjen e ekranit…"</string>
@@ -184,6 +186,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Nuk mund ta dallojë fytyrën. Përdor më mirë gjurmën e gishtit."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Përdor gjurmën e gishtit për të vazhduar"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Nuk mund ta dallojë gjurmën e gishtit. Përdor më mirë kyçjen e ekranit."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Po të kërkojmë…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Ikona e fytyrës"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Butoni i zmadhimit të pajtueshmërisë."</string>
@@ -452,6 +456,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Trokit përsëri për ta hapur"</string>
     <string name="tap_again" msgid="1315420114387908655">"Trokit sërish"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Rrëshqit lart për ta hapur"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Shtyp për të hapur"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Rrëshqit lart për të provuar përsëri"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Shkyçe për të përdorur NFC-në"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Kjo pajisje i përket organizatës sate"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 73ecc4c..83317d2 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Зумирај на целом екрану"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Развуци на цео екран"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Снимак екрана"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock је онемогућен"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"је послао/ла слику"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Чување снимка екрана..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Чување снимка екрана..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Лице није препознато. Користите отисак прста."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Наставите помоћу отиска прста"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Препознавање отиска прста није успело. Користите закључавање екрана уместо тога."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Тражимо вас…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Икона лица"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Дугме Зум компатибилности."</string>
@@ -454,6 +457,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Додирните поново да бисте отворили"</string>
     <string name="tap_again" msgid="1315420114387908655">"Додирните поново"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Превуците нагоре да бисте отворили"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Притисните да бисте отворили"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Превуците нагоре да бисте пробали поново"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Откључајте да бисте користили NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Овај уређај припада организацији"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index c27cd83..86b0fc7 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Zooma för att fylla skärm"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Dra för att fylla skärmen"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Skärmbild"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock har inaktiverats"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"har skickat en bild"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Skärmbilden sparas ..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Skärmbilden sparas ..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Ansiktet kändes inte igen. Använd fingeravtryck."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Fortsätt med hjälp av ditt fingeravtryck"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Fingeravtrycket kändes inte igen. Använd låsskärmen i stället."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Håller utkik efter dig …"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Ansiktsikon"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Knapp för kompatibilitetszoom."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Tryck igen för att öppna"</string>
     <string name="tap_again" msgid="1315420114387908655">"Tryck igen"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Öppna genom att svepa uppåt"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Tryck för att öppna"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Svep uppåt om du vill försöka igen"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Lås upp om du vill använda NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Den här enheten tillhör organisationen"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 7ac26be4..58401b5 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Kuza ili kujaza skrini"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Tanua ili kujaza skrini"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Picha ya skrini"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Kipengele cha Smart Lock kimezimwa"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"imetuma picha"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Inahifadhi picha ya skrini..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Inahifadhi picha ya skrini..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Imeshindwa kutambua uso. Tumia alama ya kidole."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Tumia alama ya kidole chako ili uendelee"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Imeshindwa kutambua alama ya kidole. Tumia mbinu ya kufunga skrini badala yake."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Inakutafuta…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Aikoni ya uso"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Kichupo cha kukuza kwa utangamanifu"</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Gusa tena ili ufungue"</string>
     <string name="tap_again" msgid="1315420114387908655">"Gusa tena"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Telezesha kidole juu ili ufungue"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Bofya ili ufungue"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Telezesha kidole juu ili ujaribu tena"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Fungua ili utumie NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Kifaa hiki kinamilikiwa na shirika lako"</string>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 527537b..d921d49 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -100,4 +100,10 @@
     same as phones in portrait  -->
     <dimen name="qs_security_footer_single_line_height">48dp</dimen>
     <dimen name="qs_security_footer_background_inset">0dp</dimen>
+
+    <!-- When split shade is used, this panel should be aligned to the top -->
+    <dimen name="qs_detail_margin_top">0dp</dimen>
+
+    <!-- Internet panel related dimensions -->
+    <dimen name="internet_dialog_list_max_width">624dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values-sw720dp/config.xml b/packages/SystemUI/res/values-sw720dp/config.xml
index 64e2760e..436f8d0 100644
--- a/packages/SystemUI/res/values-sw720dp/config.xml
+++ b/packages/SystemUI/res/values-sw720dp/config.xml
@@ -22,8 +22,5 @@
 <resources>
     <integer name="status_bar_config_maxNotificationIcons">5</integer>
 
-    <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
-         card. -->
-    <integer name="keyguard_max_notification_count">5</integer>
 </resources>
 
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 426ddd4..efb704f 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"திரையை நிரப்ப அளவை மாற்று"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"திரையை நிரப்ப இழு"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"ஸ்கிரீன்ஷாட்"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock முடக்கப்பட்டது"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"படம் அனுப்பப்பட்டது"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"ஸ்க்ரீன் ஷாட்டைச் சேமிக்கிறது…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"ஸ்க்ரீன் ஷாட்டைச் சேமிக்கிறது…"</string>
@@ -122,10 +123,10 @@
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"திரை ரெக்கார்டிங்கை நீக்குவதில் பிழை"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"அனுமதிகளைப் பெற இயலவில்லை"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"ஸ்கிரீன் ரெக்கார்டிங்கைத் தொடங்குவதில் பிழை"</string>
-    <string name="usb_preference_title" msgid="1439924437558480718">"USB கோப்பு இடமாற்ற விருப்பங்கள்"</string>
+    <string name="usb_preference_title" msgid="1439924437558480718">"USB ஃபைல் இடமாற்ற விருப்பங்கள்"</string>
     <string name="use_mtp_button_title" msgid="5036082897886518086">"(MTP) மீடியா பிளேயராக ஏற்று"</string>
     <string name="use_ptp_button_title" msgid="7676427598943446826">"(PTP) கேமராவாக ஏற்று"</string>
-    <string name="installer_cd_button_title" msgid="5499998592841984743">"Mac க்கான Android கோப்பு இடமாற்ற ஆப்ஸை நிறுவு"</string>
+    <string name="installer_cd_button_title" msgid="5499998592841984743">"Mac க்கான Android ஃபைல் இடமாற்ற ஆப்ஸை நிறுவு"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"பின்செல்"</string>
     <string name="accessibility_home" msgid="5430449841237966217">"முகப்பு"</string>
     <string name="accessibility_menu" msgid="2701163794470513040">"மெனு"</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"முகத்தை அடையாளம் காண முடியவில்லை. கைரேகையைப் பயன்படுத்தவும்."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"தொடர்வதற்குக் கைரேகையைப் பயன்படுத்தவும்"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"கைரேகையை அடையாளம் காண முடியவில்லை. அதற்குப் பதிலாகத் திரைப்பூட்டைப் பயன்படுத்தவும்."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"உங்கள் முகத்தைத் தேடுகிறது…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"முக ஐகான்"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"பொருந்துமாறு அளவை மாற்றும் பட்டன்."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"திறக்க, மீண்டும் தட்டவும்"</string>
     <string name="tap_again" msgid="1315420114387908655">"மீண்டும் தட்டவும்"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"திறப்பதற்கு மேல் நோக்கி ஸ்வைப் செய்யவும்"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"திறப்பதற்கு அழுத்தவும்"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"மீண்டும் முயல மேல்நோக்கி ஸ்வைப் செய்யவும்"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFCயைப் பயன்படுத்த அன்லாக் செய்யவும்"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"இந்த சாதனம் உங்கள் நிறுவனத்துக்கு சொந்தமானது"</string>
diff --git a/packages/SystemUI/res/values-te-ldrtl/strings.xml b/packages/SystemUI/res/values-te-ldrtl/strings.xml
index 1c1b562..94bdbcf 100644
--- a/packages/SystemUI/res/values-te-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-te-ldrtl/strings.xml
@@ -19,5 +19,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"యాప్‌లను శీఘ్రంగా స్విచ్ చేయడానికి ఎడమ వైపుకు లాగండి"</string>
+    <string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"యాప్‌లను శీఘ్రంగా స్విచ్ చేయడానికి ఎడమ వైపునకు లాగండి"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index b8a3ccd..6546059 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -54,7 +54,7 @@
     <string name="usb_accessory_confirm_prompt" msgid="5728408382798643421">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g>ని నిర్వహించడానికి <xliff:g id="APPLICATION">%1$s</xliff:g>ని తెరవాలా?"</string>
     <string name="usb_accessory_uri_prompt" msgid="6756649383432542382">"ఈ USB ఉపకరణంతో ఇన్‌స్టాల్ చేయబడిన యాప్‌లు ఏవీ పని చేయవు. ఈ ఉపకరణం గురించి <xliff:g id="URL">%1$s</xliff:g>లో మరింత తెలుసుకోండి"</string>
     <string name="title_usb_accessory" msgid="1236358027511638648">"USB ఉపకరణం"</string>
-    <string name="label_view" msgid="6815442985276363364">"వీక్షించండి"</string>
+    <string name="label_view" msgid="6815442985276363364">"చూడండి"</string>
     <string name="always_use_device" msgid="210535878779644679">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> కనెక్ట్ అయి ఉన్న ఎల్లప్పుడూ <xliff:g id="APPLICATION">%1$s</xliff:g>ని తెరవండి"</string>
     <string name="always_use_accessory" msgid="1977225429341838444">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> కనెక్ట్ అయి ఉన్న ఎల్లప్పుడూ <xliff:g id="APPLICATION">%1$s</xliff:g>ని తెరవండి"</string>
     <string name="usb_debugging_title" msgid="8274884945238642726">"USB డీబగ్గింగ్‌ను అనుమతించాలా?"</string>
@@ -77,6 +77,8 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"స్క్రీన్‌కు నింపేలా జూమ్ చేయండి"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"స్క్రీన్‌కు నింపేలా విస్తరించండి"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"స్క్రీన్‌షాట్"</string>
+    <!-- no translation found for global_action_smart_lock_disabled (9097102067802412936) -->
+    <skip />
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ఇమేజ్‌ను పంపారు"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"స్క్రీన్‌షాట్‌ను సేవ్ చేస్తోంది…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"స్క్రీన్‌షాట్‌ను సేవ్ చేస్తోంది…"</string>
@@ -103,7 +105,7 @@
     <string name="screenrecord_description" msgid="1123231719680353736">"రికార్డ్ చేస్తున్నప్పుడు, Android సిస్టమ్ మీ స్క్రీన్‌పై ప్రదర్శించబడిన లేదా మీ పరికరం నుండి ప్లే చేయబడిన ఏ సున్నితమైన సమాచారాన్నైనా క్యాప్చర్ చేయగలదు. ఈ సమాచారంలో, పాస్‌వర్డ్‌లు, పేమెంట్ వివరాలు, ఫోటోలు, మెసేజ్‌లు, ఆడియో ఉంటాయి."</string>
     <string name="screenrecord_audio_label" msgid="6183558856175159629">"ఆడియోను రికార్డ్ చేయి"</string>
     <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"పరికరం ఆడియో"</string>
-    <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"మీ పరికరం నుండి వచ్చే సంగీతం, కాల్‌లు, రింగ్‌టోన్‌ల వంటి ధ్వనులు"</string>
+    <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"మీ పరికరం నుండి వచ్చే సంగీతం, కాల్స్‌, రింగ్‌టోన్‌ల వంటి ధ్వనులు"</string>
     <string name="screenrecord_mic_label" msgid="2111264835791332350">"మైక్రోఫోన్"</string>
     <string name="screenrecord_device_audio_and_mic_label" msgid="1831323771978646841">"పరికరం ఆడియో, మైక్రోఫోన్"</string>
     <string name="screenrecord_start" msgid="330991441575775004">"ప్రారంభించు"</string>
@@ -113,7 +115,7 @@
     <string name="screenrecord_stop_text" msgid="6549288689506057686">"ఆపడానికి ట్యాప్ చేయండి"</string>
     <string name="screenrecord_stop_label" msgid="72699670052087989">"ఆపివేయి"</string>
     <string name="screenrecord_pause_label" msgid="6004054907104549857">"పాజ్ చేయి"</string>
-    <string name="screenrecord_resume_label" msgid="4972223043729555575">"కొనసాగించు"</string>
+    <string name="screenrecord_resume_label" msgid="4972223043729555575">"కొనసాగించండి"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"రద్దు చేయి"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"షేర్ చేయి"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"స్క్రీన్ రికార్డ్ రద్దు చేయబడింది"</string>
@@ -184,6 +186,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"ముఖం గుర్తించలేము. బదులుగా వేలిముద్ర ఉపయోగించండి."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"కొనసాగించడానికి మీ వేలిముద్రను ఉపయోగించండి"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"వేలిముద్రను గుర్తించడం సాధ్యపడదు. బదులుగా స్క్రీన్ లాక్‌ను ఉపయోగించండి."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"మీ కోసం చూస్తోంది…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"ముఖ చిహ్నం"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"అనుకూలత జూమ్ బటన్."</string>
@@ -306,7 +310,7 @@
     <string name="data_usage_disabled_dialog_enable" msgid="2796648546086408937">"పునఃప్రారంభించు"</string>
     <string name="gps_notification_searching_text" msgid="231304732649348313">"GPS కోసం శోధిస్తోంది"</string>
     <string name="gps_notification_found_text" msgid="3145873880174658526">"స్థానం GPS ద్వారా సెట్ చేయబడింది"</string>
-    <string name="accessibility_location_active" msgid="2845747916764660369">"స్థాన అభ్యర్థనలు సక్రియంగా ఉన్నాయి"</string>
+    <string name="accessibility_location_active" msgid="2845747916764660369">"స్థాన రిక్వెస్ట్‌లు సక్రియంగా ఉన్నాయి"</string>
     <string name="accessibility_sensors_off_active" msgid="2619725434618911551">"సెన్సార్‌లు ఆఫ్ యాక్టివ్‌లో ఉంది"</string>
     <string name="accessibility_clear_all" msgid="970525598287244592">"అన్ని నోటిఫికేషన్‌లను క్లియర్ చేయండి."</string>
     <string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
@@ -317,10 +321,10 @@
     <string name="notification_summary_message_format" msgid="5158219088501909966">"<xliff:g id="CONTACT_NAME">%1$s</xliff:g>: <xliff:g id="MESSAGE_CONTENT">%2$s</xliff:g>"</string>
     <string name="status_bar_notification_inspect_item_title" msgid="6818779631806163080">"నోటిఫికేషన్ సెట్టింగ్‌లు"</string>
     <string name="status_bar_notification_app_settings_title" msgid="5050006438806013903">"<xliff:g id="APP_NAME">%s</xliff:g> సెట్టింగ్‌లు"</string>
-    <string name="accessibility_rotation_lock_off" msgid="3880436123632448930">"స్క్రీన్ స్వయంచాలకంగా తిప్పబడుతుంది."</string>
+    <string name="accessibility_rotation_lock_off" msgid="3880436123632448930">"స్క్రీన్ ఆటోమేటిక్‌గా తిప్పబడుతుంది."</string>
     <string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"స్క్రీన్ ల్యాండ్‌స్కేప్ దృగ్విన్యాసంలో లాక్ చేయబడుతుంది."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"స్క్రీన్ పోర్ట్రెయిట్ దృగ్విన్యాసంలో లాక్ చేయబడుతుంది."</string>
-    <string name="accessibility_rotation_lock_off_changed" msgid="5772498370935088261">"స్క్రీన్ ఇప్పుడు స్వయంచాలకంగా తిరుగుతుంది."</string>
+    <string name="accessibility_rotation_lock_off_changed" msgid="5772498370935088261">"స్క్రీన్ ఇప్పుడు ఆటోమేటిక్‌గా తిరుగుతుంది."</string>
     <string name="accessibility_rotation_lock_on_landscape_changed" msgid="5785739044300729592">"స్క్రీన్ ఇప్పుడు ల్యాండ్‌స్కేప్ దృగ్విన్యాసంలో లాక్ చేయబడింది."</string>
     <string name="accessibility_rotation_lock_on_portrait_changed" msgid="5580170829728987989">"స్క్రీన్ ఇప్పుడు పోర్ట్రెయిట్ దృగ్విన్యాసంలో లాక్ చేయబడింది."</string>
     <string name="dessert_case" msgid="9104973640704357717">"డెజర్ట్ కేస్"</string>
@@ -445,13 +449,14 @@
     <string name="zen_priority_introduction" msgid="3159291973383796646">"మీరు పేర్కొనే అలారాలు, రిమైండర్‌లు, ఈవెంట్‌లు మరియు కాలర్‌ల నుండి మినహా మరే ఇతర ధ్వనులు మరియు వైబ్రేషన్‌లతో మీకు అంతరాయం కలగదు. మీరు ఇప్పటికీ సంగీతం, వీడియోలు మరియు గేమ్‌లతో సహా మీరు ప్లే చేయడానికి ఎంచుకున్నవి ఏవైనా వింటారు."</string>
     <string name="zen_alarms_introduction" msgid="3987266042682300470">"అలారాలు నుండి మినహా మరే ఇతర ధ్వనులు మరియు వైబ్రేషన్‌లతో మీకు అంతరాయం కలగదు. మీరు ఇప్పటికీ సంగీతం, వీడియోలు మరియు గేమ్‌లతో సహా మీరు ప్లే చేయడానికి ఎంచుకున్నవి ఏవైనా వింటారు."</string>
     <string name="zen_priority_customize_button" msgid="4119213187257195047">"అనుకూలీకరించు"</string>
-    <string name="zen_silence_introduction_voice" msgid="853573681302712348">"ఇది అలారాలు, సంగీతం, వీడియోలు మరియు గేమ్‌లతో సహా అన్ని ధ్వనులు మరియు వైబ్రేషన్‌లను బ్లాక్ చేస్తుంది. మీరు ఇప్పటికీ ఫోన్ కాల్‌లు చేయగలుగుతారు."</string>
+    <string name="zen_silence_introduction_voice" msgid="853573681302712348">"ఇది అలారాలు, సంగీతం, వీడియోలు మరియు గేమ్‌లతో సహా అన్ని ధ్వనులు మరియు వైబ్రేషన్‌లను బ్లాక్ చేస్తుంది. మీరు ఇప్పటికీ ఫోన్ కాల్స్‌ చేయగలుగుతారు."</string>
     <string name="zen_silence_introduction" msgid="6117517737057344014">"ఇది అలారాలు, సంగీతం, వీడియోలు మరియు గేమ్‌లతో సహా అన్ని ధ్వనులు మరియు వైబ్రేషన్‌లను బ్లాక్ చేస్తుంది."</string>
     <string name="keyguard_more_overflow_text" msgid="5819512373606638727">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
     <string name="speed_bump_explanation" msgid="7248696377626341060">"తక్కువ అత్యవసర నోటిఫికేషన్‌లు దిగువన"</string>
     <string name="notification_tap_again" msgid="4477318164947497249">"తెరవడానికి మళ్లీ నొక్కండి"</string>
     <string name="tap_again" msgid="1315420114387908655">"మళ్లీ ట్యాప్ చేయండి"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"తెరవడానికి, పైకి స్వైప్ చేయండి"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"తెరవడానికి నొక్కండి"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"మళ్ళీ ప్రయత్నించడానికి పైకి స్వైప్ చేయండి"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFCని ఉపయోగించడానికి అన్‌లాక్ చేయండి"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"ఈ పరికరం మీ సంస్థకు చెందినది"</string>
@@ -503,7 +508,7 @@
     <string name="battery_saver_notification_title" msgid="8419266546034372562">"బ్యాటరీ సేవర్ ఆన్‌లో ఉంది"</string>
     <string name="battery_saver_notification_text" msgid="2617841636449016951">"పనితీరుని మరియు నేపథ్య డేటాను తగ్గిస్తుంది"</string>
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"బ్యాటరీ సేవర్‌ను ఆఫ్ చేయండి"</string>
-    <string name="media_projection_dialog_text" msgid="1755705274910034772">"రికార్డ్ చేస్తున్నప్పుడు లేదా ప్రసారం చేస్తున్నప్పుడు, మీ స్క్రీన్‌పై ప్రదర్శించబడిన లేదా మీ పరికరం నుండి ప్లే చేయబడిన సమాచారం మొత్తాన్ని, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> యాక్సెస్ చేయగలుగుతుంది. ఈ సమాచారంలో, పాస్‌వర్డ్‌లు, చెల్లింపు వివరాలు, ఫోటోలు, సందేశాలు, మీరు ప్లే చేసే ఆడియో వంటివి ఉంటాయి."</string>
+    <string name="media_projection_dialog_text" msgid="1755705274910034772">"రికార్డ్ చేస్తున్నప్పుడు లేదా ప్రసారం చేస్తున్నప్పుడు, మీ స్క్రీన్‌పై ప్రదర్శించబడిన లేదా మీ పరికరం నుండి ప్లే చేయబడిన సమాచారం మొత్తాన్ని, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> యాక్సెస్ చేయగలుగుతుంది. ఈ సమాచారంలో, పాస్‌వర్డ్‌లు, చెల్లింపు వివరాలు, ఫోటోలు, మెసేజ్‌లు, మీరు ప్లే చేసే ఆడియో వంటివి ఉంటాయి."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"రికార్డ్ చేస్తున్నప్పుడు లేదా ప్రసారం చేస్తున్నప్పుడు మీ స్క్రీన్‌పై ప్రదర్శించబడిన లేదా మీ పరికరం నుండి ప్లే చేయబడిన సమాచారం మొత్తాన్ని, ఈ ఫంక్షన్‌ను అందిస్తున్న సర్వీస్ యాక్సెస్ చేయగలదు. ఈ సమాచారంలో, పాస్‌వర్డ్‌లు, పేమెంట్ వివరాలు, ఫోటోలు,  మెసేజ్‌లు, మీరు ప్లే చేసే ఆడియో వంటివి ఉంటాయి."</string>
     <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"రికార్డ్ చేయడం లేదా ప్రసారం చేయడం ప్రారంభించాలా?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>తో రికార్డ్ చేయడం లేదా ప్రసారం చేయడం ప్రారంభించాలా?"</string>
@@ -549,7 +554,7 @@
     <string name="monitoring_subtitle_ca_certificate" msgid="8588092029755175800">"CA ప్రమాణపత్రాలు"</string>
     <string name="disable_vpn" msgid="482685974985502922">"VPNని నిలిపివేయి"</string>
     <string name="disconnect_vpn" msgid="26286850045344557">"VPNను డిస్‌కనెక్ట్ చేయి"</string>
-    <string name="monitoring_button_view_policies" msgid="3869724835853502410">"విధానాలను వీక్షించండి"</string>
+    <string name="monitoring_button_view_policies" msgid="3869724835853502410">"విధానాలను చూడండి"</string>
     <string name="monitoring_button_view_controls" msgid="8316440345340701117">"నియంత్రణలను చూడండి"</string>
     <string name="monitoring_description_named_management" msgid="505833016545056036">"ఈ పరికరం <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>కు చెందినది.\n\nసెట్టింగ్‌లను, కార్పొరేట్ యాక్సెస్‌ను, యాప్‌లను, మీ పరికరానికి సంబంధించిన డేటాను, అలాగే మీ పరికరం యొక్క లొకేషన్ సమాచారాన్ని మీ IT అడ్మిన్ పర్యవేక్షించగలరు, మేనేజ్ చేయగలరు.\n\nమరింత సమాచారం కోసం, మీ IT అడ్మిన్‌ను సంప్రదించండి."</string>
     <string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"<xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g>, ఈ పరికరంతో అనుబంధించబడిన డేటాను యాక్సెస్ చేయవచ్చు, యాప్‌లను మేనేజ్ చేయవచ్చు అలాగే ఈ పరికరాల సెట్టింగ్‌లను మార్చవచ్చు.\n\nమీకు ఏవైనా సందేహాలు ఉంటే, <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g>ను కాంటాక్ట్ చేయండి."</string>
@@ -557,7 +562,7 @@
     <string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"ఈ పరికరంలో మీ సంస్థ ఒక ప్రమాణపత్ర అధికారాన్ని ఇన్‌స్టాల్ చేసింది. మీ సురక్షిత నెట్‌వర్క్ ట్రాఫిక్ పర్యవేక్షించబడవచ్చు లేదా సవరించబడవచ్చు."</string>
     <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"మీ కార్యాలయ ప్రొఫైల్‌లో మీ సంస్థ ఒక ప్రమాణపత్ర అధికారాన్ని ఇన్‌స్టాల్ చేసింది. మీ సురక్షిత నెట్‌వర్క్ ట్రాఫిక్ పర్యవేక్షించబడవచ్చు లేదా సవరించబడవచ్చు."</string>
     <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"ఈ పరికరంలో ప్రమాణపత్ర అధికారం ఇన్‌స్టాల్ చేయబడింది. మీ సురక్షిత నెట్‌వర్క్ ట్రాఫిక్ పర్యవేక్షించబడవచ్చు లేదా సవరించబడవచ్చు."</string>
-    <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"మీ నిర్వాహకులు మీ పరికరంలోని ట్రాఫిక్‌ని పర్యవేక్షించగల నెట్‌వర్క్ లాగింగ్‌ని ఆన్ చేసారు."</string>
+    <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"మీ నిర్వాహకులు మీ పరికరంలోని ట్రాఫిక్‌ని పర్యవేక్షించగల నెట్‌వర్క్ లాగింగ్‌ని ఆన్ చేశారు."</string>
     <string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"మీ అడ్మిన్ నెట్‌వర్క్ లాగింగ్‌ను ఆన్ చేశారు, ఇది మీ వర్క్ ప్రొఫైల్‌లోని ట్రాఫిక్‌ను పర్యవేక్షిస్తుంది కానీ మీ వ్యక్తిగత ప్రొఫైల్‌లో కాదు."</string>
     <string name="monitoring_description_named_vpn" msgid="5749932930634037027">"మీరు <xliff:g id="VPN_APP">%1$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు, ఇది ఇమెయిల్‌లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
     <string name="monitoring_description_two_named_vpns" msgid="3516830755681229463">"మీరు ఇమెయిల్‌లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల <xliff:g id="VPN_APP_0">%1$s</xliff:g> మరియు <xliff:g id="VPN_APP_1">%2$s</xliff:g>కి కనెక్ట్ చేయబడ్డారు."</string>
@@ -573,8 +578,8 @@
     <string name="monitoring_description_vpn_settings" msgid="5264167033247632071">"VPN సెట్టింగ్‌లను తెరవండి"</string>
     <string name="monitoring_description_ca_cert_settings_separator" msgid="7107390013344435439">" "</string>
     <string name="monitoring_description_ca_cert_settings" msgid="8329781950135541003">"విశ్వసనీయ ఆధారాలను తెరువు"</string>
-    <string name="monitoring_description_network_logging" msgid="577305979174002252">"మీ నిర్వాహకులు మీ పరికరంలోని ట్రాఫిక్‌ని పర్యవేక్షించగల నెట్‌వర్క్ లాగింగ్‌ని ఆన్ చేసారు.\n\nమరింత సమాచారం కావాలంటే, మీ నిర్వాహకులను సంప్రదించండి."</string>
-    <string name="monitoring_description_vpn" msgid="1685428000684586870">"మీరు VPN కనెక్షన్ సెటప్ చేయడానికి ఒక యాప్‌నకు అనుమతి ఇచ్చారు.\n\nఈ యాప్ ఇమెయిల్‌లు,యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ డివైజ్ మరియు నెట్‌వర్క్ కార్యకలాపాన్ని పర్యవేక్షించగలదు."</string>
+    <string name="monitoring_description_network_logging" msgid="577305979174002252">"మీ నిర్వాహకులు మీ పరికరంలోని ట్రాఫిక్‌ని పర్యవేక్షించగల నెట్‌వర్క్ లాగింగ్‌ని ఆన్ చేశారు.\n\nమరింత సమాచారం కావాలంటే, మీ నిర్వాహకులను సంప్రదించండి."</string>
+    <string name="monitoring_description_vpn" msgid="1685428000684586870">"మీరు VPN కనెక్షన్ సెటప్ చేయడానికి ఒక యాప్‌నకు అనుమతి ఇచ్చారు.\n\nఈ యాప్ ఇమెయిల్‌లు,యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ డివైజ్ మరియు నెట్‌వర్క్ యాక్టివిటీని పర్యవేక్షించగలదు."</string>
     <string name="monitoring_description_vpn_profile_owned" msgid="4964237035412372751">"<xliff:g id="ORGANIZATION">%1$s</xliff:g> ద్వారా మీ కార్యాలయ ప్రొఫైల్ నిర్వహించబడుతోంది.\n\nఇమెయిల్‌లు, యాప్‌లు మరియు వెబ్‌సైట్‌లతో సహా మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగల సామర్థ్యం మీ నిర్వాహకులకు ఉంది.\n\nమరింత సమాచారం కావాలంటే, మీ నిర్వాహకులను సంప్రదించండి.\n\nమీరు VPNకి కూడా కనెక్ట్ అయ్యారు, ఇది మీ నెట్‌వర్క్ కార్యాచరణను పర్యవేక్షించగలదు."</string>
     <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"ఈ పరికరాన్ని మీ తల్లి/తండ్రి మేనేజ్ చేస్తున్నారు. మీ తల్లి/తండ్రి, మీరు ఉపయోగించే యాప్‌లు, మీ లొకేషన్, అలాగే మీ పరికర వినియోగ వ్యవధి వంటి సమాచారాన్ని చూడగలరు, మేనేజ్ చేయగలరు."</string>
     <string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
@@ -628,7 +633,7 @@
     <string name="stream_bluetooth_sco" msgid="6234562365528664331">"బ్లూటూత్"</string>
     <string name="stream_dtmf" msgid="7322536356554673067">"డ్యూయల్ మల్టీ టోన్ ఫ్రీక్వెన్సీ"</string>
     <string name="stream_accessibility" msgid="3873610336741987152">"యాక్సెసిబిలిటీ"</string>
-    <string name="ring_toggle_title" msgid="5973120187287633224">"కాల్‌లు"</string>
+    <string name="ring_toggle_title" msgid="5973120187287633224">"కాల్స్‌"</string>
     <string name="volume_ringer_status_normal" msgid="1339039682222461143">"రింగ్"</string>
     <string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"వైబ్రేట్"</string>
     <string name="volume_ringer_status_silent" msgid="3691324657849880883">"మ్యూట్"</string>
@@ -644,7 +649,7 @@
     <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"అన్‌మ్యూట్ చేయి"</string>
     <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"వైబ్రేట్"</string>
     <string name="volume_dialog_title" msgid="6502703403483577940">"%s వాల్యూమ్ నియంత్రణలు"</string>
-    <string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"కాల్‌లు మరియు నోటిఫికేషన్‌లు రింగ్ అవుతాయి (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
+    <string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"కాల్స్‌ మరియు నోటిఫికేషన్‌లు రింగ్ అవుతాయి (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
     <string name="output_title" msgid="3938776561655668350">"మీడియా అవుట్‌పుట్"</string>
     <string name="output_calls_title" msgid="7085583034267889109">"ఫోన్ కాల్ అవుట్‌పుట్"</string>
     <string name="output_none_found" msgid="5488087293120982770">"పరికరాలు ఏవీ కనుగొనబడలేదు"</string>
@@ -706,7 +711,7 @@
     <string name="tuner_full_importance_settings" msgid="1388025816553459059">"పవర్ నోటిఫికేషన్ నియంత్రణలు"</string>
     <string name="tuner_full_importance_settings_on" msgid="917981436602311547">"ఆన్‌లో ఉన్నాయి"</string>
     <string name="tuner_full_importance_settings_off" msgid="5580102038749680829">"ఆఫ్‌లో ఉన్నాయి"</string>
-    <string name="power_notification_controls_description" msgid="1334963837572708952">"పవర్ నోటిఫికేషన్ నియంత్రణలతో, మీరు యాప్ నోటిఫికేషన్‌ల కోసం ప్రాముఖ్యత స్థాయిని 0 నుండి 5 వరకు సెట్ చేయవచ్చు. \n\n"<b>"స్థాయి 5"</b>" \n- నోటిఫికేషన్ జాబితా పైభాగంలో చూపబడతాయి \n- పూర్తి స్క్రీన్ అంతరాయం అనుమతించబడుతుంది \n- ఎల్లప్పుడూ త్వరిత వీక్షణ అందించబడుతుంది \n\n"<b>"స్థాయి 4"</b>\n"- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎల్లప్పుడూ త్వరిత వీక్షణ అందించబడుతుంది \n\n"<b>"స్థాయి 3"</b>" \n- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎప్పుడూ త్వరిత వీక్షణ అందించబడదు \n\n"<b>"స్థాయి 2"</b>" \n- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎప్పుడూ త్వరిత వీక్షణ అందించబడదు \n- ఎప్పుడూ శబ్దం మరియు వైబ్రేషన్ చేయవు \n\n"<b>"స్థాయి 1"</b>" \n- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎప్పుడూ త్వరిత వీక్షణ అందించబడదు \n- ఎప్పుడూ శబ్దం లేదా వైబ్రేట్ చేయవు \n- లాక్ స్క్రీన్ మరియు స్థితి పట్టీ నుండి దాచబడతాయి \n- నోటిఫికేషన్ జాబితా దిగువ భాగంలో చూపబడతాయి \n\n"<b>"స్థాయి 0"</b>" \n- యాప్ నుండి అన్ని నోటిఫికేషన్‌లు బ్లాక్ చేయబడతాయి"</string>
+    <string name="power_notification_controls_description" msgid="1334963837572708952">"పవర్ నోటిఫికేషన్ నియంత్రణలతో, మీరు యాప్ నోటిఫికేషన్‌ల కోసం ప్రాముఖ్యత స్థాయిని 0 నుండి 5 వరకు సెట్ చేయవచ్చు. \n\n"<b>"స్థాయి 5"</b>" \n- నోటిఫికేషన్ లిస్ట్‌ పైభాగంలో చూపబడతాయి \n- పూర్తి స్క్రీన్ అంతరాయం అనుమతించబడుతుంది \n- ఎల్లప్పుడూ త్వరిత వీక్షణ అందించబడుతుంది \n\n"<b>"స్థాయి 4"</b>\n"- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎల్లప్పుడూ త్వరిత వీక్షణ అందించబడుతుంది \n\n"<b>"స్థాయి 3"</b>" \n- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎప్పుడూ త్వరిత వీక్షణ అందించబడదు \n\n"<b>"స్థాయి 2"</b>" \n- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎప్పుడూ త్వరిత వీక్షణ అందించబడదు \n- ఎప్పుడూ శబ్దం మరియు వైబ్రేషన్ చేయవు \n\n"<b>"స్థాయి 1"</b>" \n- పూర్తి స్క్రీన్ అంతరాయం నిరోధించబడుతుంది \n- ఎప్పుడూ త్వరిత వీక్షణ అందించబడదు \n- ఎప్పుడూ శబ్దం లేదా వైబ్రేట్ చేయవు \n- లాక్ స్క్రీన్ మరియు స్థితి పట్టీ నుండి దాచబడతాయి \n- నోటిఫికేషన్ లిస్ట్‌ దిగువ భాగంలో చూపబడతాయి \n\n"<b>"స్థాయి 0"</b>" \n- యాప్ నుండి అన్ని నోటిఫికేషన్‌లు బ్లాక్ చేయబడతాయి"</string>
     <string name="notification_header_default_channel" msgid="225454696914642444">"నోటిఫికేషన్‌లు"</string>
     <string name="notification_channel_disabled" msgid="928065923928416337">"ఇకపై మీకు ఈ నోటిఫికేషన్‌లు కనిపించవు"</string>
     <string name="notification_channel_minimized" msgid="6892672757877552959">"ఈ నోటిఫికేషన్‌లు కుదించబడ్డాయి"</string>
@@ -922,23 +927,23 @@
     <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"శీఘ్ర సెట్టింగ్‌లను తెరవండి."</string>
     <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"శీఘ్ర సెట్టింగ్‌లను మూసివేయండి."</string>
     <string name="accessibility_quick_settings_alarm_set" msgid="7237918261045099853">"అలారం సెట్ చేయబడింది."</string>
-    <string name="accessibility_quick_settings_user" msgid="505821942882668619">"<xliff:g id="ID_1">%s</xliff:g> వలె సైన్ ఇన్ చేసారు"</string>
+    <string name="accessibility_quick_settings_user" msgid="505821942882668619">"<xliff:g id="ID_1">%s</xliff:g> వలె సైన్ ఇన్ చేశారు"</string>
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"యూజర్‌ను ఎంపిక చేయండి"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"ఇంటర్నెట్ లేదు"</string>
     <string name="accessibility_quick_settings_open_details" msgid="4879279912389052142">"వివరాలను తెరవండి."</string>
     <string name="accessibility_quick_settings_not_available" msgid="6860875849497473854">"<xliff:g id="REASON">%s</xliff:g> కారణంగా అందుబాటులో లేదు"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g> సెట్టింగ్‌లను తెరవండి."</string>
-    <string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"సెట్టింగ్‌ల క్రమాన్ని సవరించండి."</string>
+    <string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"సెట్టింగ్‌ల క్రమాన్ని ఎడిట్ చేయండి."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"పవర్ మెనూ"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g>లో <xliff:g id="ID_1">%1$d</xliff:g>వ పేజీ"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"లాక్ స్క్రీన్"</string>
     <string name="thermal_shutdown_title" msgid="2702966892682930264">"వేడెక్కినందుకు ఫోన్ ఆఫ్ చేయబడింది"</string>
     <string name="thermal_shutdown_message" msgid="6142269839066172984">"మీ ఫోన్ ఇప్పుడు సాధారణంగా పని చేస్తోంది.\nమరింత సమాచారం కోసం ట్యాప్ చేయండి"</string>
-    <string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"మీ ఫోన్ చాలా వేడిగా ఉంది, కనుక చల్లబర్చడానికి ఆఫ్ చేయబడింది. మీ ఫోన్ ఇప్పుడు సాధారణంగా పని చేస్తుంది.\n\nమీరు ఇలా చేస్తే మీ ఫోన్ చాలా వేడెక్కవచ్చు:\n	• వనరు-ఆధారిత యాప్‌లు (గేమింగ్, వీడియో లేదా నావిగేషన్ వంటి యాప్‌లు) ఉపయోగించడం\n	• పెద్ద ఫైల్‌లను డౌన్‌లోడ్ లేదా అప్‌లోడ్ చేయడం\n	• అధిక ఉష్ణోగ్రతలలో మీ ఫోన్‌ని ఉపయోగించడం"</string>
+    <string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"మీ ఫోన్ చాలా వేడిగా ఉంది, కనుక చల్లబర్చడానికి ఆఫ్ చేయబడింది. మీ ఫోన్ ఇప్పుడు సాధారణంగా పని చేస్తుంది.\n\nమీరు ఇలా చేస్తే మీ ఫోన్ చాలా వేడెక్కవచ్చు:\n	• వనరు-ఆధారిత యాప్‌లు (గేమింగ్, వీడియో లేదా నావిగేషన్ వంటి యాప్‌లు) ఉపయోగించడం\n	• పెద్ద ఫైళ్లను డౌన్‌లోడ్ లేదా అప్‌లోడ్ చేయడం\n	• అధిక ఉష్ణోగ్రతలలో మీ ఫోన్‌ని ఉపయోగించడం"</string>
     <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"తీసుకోవాల్సిన జాగ్రత్తలు ఏమిటో చూడండి"</string>
     <string name="high_temp_title" msgid="2218333576838496100">"ఫోన్ వేడెక్కుతోంది"</string>
     <string name="high_temp_notif_message" msgid="1277346543068257549">"ఫోన్‌ను చల్లబరిచే క్రమంలో కొన్ని ఫీచర్లు పరిమితం చేయబడ్డాయి.\nమరింత సమాచారం కోసం ట్యాప్ చేయండి"</string>
-    <string name="high_temp_dialog_message" msgid="3793606072661253968">"మీ ఫోన్ స్వయంచాలకంగా చల్లబడటానికి ప్రయత్నిస్తుంది. మీరు ఇప్పటికీ మీ ఫోన్‌ను ఉపయోగించవచ్చు, కానీ దాని పనితీరు నెమ్మదిగా ఉండవచ్చు.\n\nమీ ఫోన్ చల్లబడిన తర్వాత, అది సాధారణ రీతిలో పని చేస్తుంది."</string>
+    <string name="high_temp_dialog_message" msgid="3793606072661253968">"మీ ఫోన్ ఆటోమేటిక్‌గా చల్లబడటానికి ప్రయత్నిస్తుంది. మీరు ఇప్పటికీ మీ ఫోన్‌ను ఉపయోగించవచ్చు, కానీ దాని పనితీరు నెమ్మదిగా ఉండవచ్చు.\n\nమీ ఫోన్ చల్లబడిన తర్వాత, అది సాధారణ రీతిలో పని చేస్తుంది."</string>
     <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"తీసుకోవాల్సిన జాగ్రత్తలు ఏమిటో చూడండి"</string>
     <string name="high_temp_alarm_title" msgid="2359958549570161495">"ప్లగ్ నుండి ఛార్జర్‌ తీసివేయండి"</string>
     <string name="high_temp_alarm_notify_message" msgid="7186272817783835089">"ఈ పరికరాన్ని ఛార్జ్ చేయడంలో సమస్య ఉంది. పవర్ అడాప్టర్‌ను ప్లగ్ నుండి తీసివేసి, కేబుల్ ఏమైనా వేడిగా అయితే తగిన జాగ్రత్తలు తీసుకోండి."</string>
@@ -960,7 +965,7 @@
     <string name="notification_channel_alerts" msgid="3385787053375150046">"అలర్ట్‌లు"</string>
     <string name="notification_channel_battery" msgid="9219995638046695106">"బ్యాటరీ"</string>
     <string name="notification_channel_screenshot" msgid="7665814998932211997">"స్క్రీన్‌షాట్‌లు"</string>
-    <string name="notification_channel_general" msgid="4384774889645929705">"సాధారణ సందేశాలు"</string>
+    <string name="notification_channel_general" msgid="4384774889645929705">"సాధారణ మెసేజ్‌లు"</string>
     <string name="notification_channel_storage" msgid="2720725707628094977">"స్టోరేజ్"</string>
     <string name="notification_channel_hints" msgid="7703783206000346876">"సూచనలు"</string>
     <string name="instant_apps" msgid="8337185853050247304">"ఇన్‌స్టంట్ యాప్‌లు"</string>
@@ -975,9 +980,9 @@
     <string name="wifi_is_off" msgid="5389597396308001471">"Wi-Fi ఆఫ్‌లో ఉంది"</string>
     <string name="bt_is_off" msgid="7436344904889461591">"బ్లూటూత్ ఆఫ్‌లో ఉంది"</string>
     <string name="dnd_is_off" msgid="3185706903793094463">"అంతరాయం కలిగించవద్దు ఆఫ్‌లో ఉంది"</string>
-    <string name="qs_dnd_prompt_auto_rule" msgid="3535469468310002616">"స్వయంచాలక నియమం (<xliff:g id="ID_1">%s</xliff:g>) ద్వారా అంతరాయం కలిగించవద్దు ఆన్ చేయబడింది."</string>
+    <string name="qs_dnd_prompt_auto_rule" msgid="3535469468310002616">"ఆటోమేటిక్‌ నియమం (<xliff:g id="ID_1">%s</xliff:g>) ద్వారా అంతరాయం కలిగించవద్దు ఆన్ చేయబడింది."</string>
     <string name="qs_dnd_prompt_app" msgid="4027984447935396820">"యాప్ (<xliff:g id="ID_1">%s</xliff:g>) ద్వారా అంతరాయం కలిగించవద్దు ఆన్ చేయబడింది."</string>
-    <string name="qs_dnd_prompt_auto_rule_app" msgid="1841469944118486580">"స్వయంచాలక నియమం లేదా యాప్ ద్వారా అంతరాయం కలిగించవద్దు ఆన్ చేయబడింది."</string>
+    <string name="qs_dnd_prompt_auto_rule_app" msgid="1841469944118486580">"ఆటోమేటిక్‌ నియమం లేదా యాప్ ద్వారా అంతరాయం కలిగించవద్దు ఆన్ చేయబడింది."</string>
     <string name="qs_dnd_until" msgid="7844269319043747955">"<xliff:g id="ID_1">%s</xliff:g> వరకు"</string>
     <string name="qs_dnd_keep" msgid="3829697305432866434">"Keep"</string>
     <string name="qs_dnd_replace" msgid="7712119051407052689">"భర్తీ చేయి"</string>
@@ -986,7 +991,7 @@
     <string name="mobile_data_disable_title" msgid="5366476131671617790">"మొబైల్ డేటాను ఆఫ్ చేయాలా?"</string>
     <string name="mobile_data_disable_message" msgid="8604966027899770415">"\"<xliff:g id="CARRIER">%s</xliff:g>\" ద్వారా మీకు డేటా లేదా ఇంటర్నెట్‌కు యాక్సెస్ ఉండదు. Wi-Fi ద్వారా మాత్రమే ఇంటర్నెట్ అందుబాటులో ఉంటుంది."</string>
     <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"మీ క్యారియర్"</string>
-    <string name="touch_filtered_warning" msgid="8119511393338714836">"అనుమతి అభ్యర్థనకు ఒక యాప్ అడ్డు తగులుతున్నందున సెట్టింగ్‌లు మీ ప్రతిస్పందనను ధృవీకరించలేకపోయాయి."</string>
+    <string name="touch_filtered_warning" msgid="8119511393338714836">"అనుమతి రిక్వెస్ట్‌కు ఒక యాప్ అడ్డు తగులుతున్నందున సెట్టింగ్‌లు మీ ప్రతిస్పందనను ధృవీకరించలేకపోయాయి."</string>
     <string name="slice_permission_title" msgid="3262615140094151017">"<xliff:g id="APP_2">%2$s</xliff:g> స్లైస్‌లను చూపించడానికి <xliff:g id="APP_0">%1$s</xliff:g>ని అనుమతించండి?"</string>
     <string name="slice_permission_text_1" msgid="6675965177075443714">"- ఇది <xliff:g id="APP">%1$s</xliff:g> నుండి సమాచారాన్ని చదువుతుంది"</string>
     <string name="slice_permission_text_2" msgid="6758906940360746983">"- ఇది <xliff:g id="APP">%1$s</xliff:g> లోపల చర్యలు తీసుకుంటుంది"</string>
@@ -1141,7 +1146,7 @@
     <string name="status_before_loading" msgid="1500477307859631381">"కంటెంట్ త్వరలో కనిపిస్తుంది"</string>
     <string name="missed_call" msgid="4228016077700161689">"మిస్డ్ కాల్"</string>
     <string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
-    <string name="people_tile_description" msgid="8154966188085545556">"ఇటీవలి మెసేజ్‌లు, మిస్డ్ కాల్‌లు, అలాగే స్టేటస్ అప్‌డేట్‌లను చూడండి"</string>
+    <string name="people_tile_description" msgid="8154966188085545556">"ఇటీవలి మెసేజ్‌లు, మిస్డ్ కాల్స్‌, అలాగే స్టేటస్ అప్‌డేట్‌లను చూడండి"</string>
     <string name="people_tile_title" msgid="6589377493334871272">"సంభాషణ"</string>
     <string name="paused_by_dnd" msgid="7856941866433556428">"అంతరాయం కలిగించవద్దు ద్వారా పాజ్ చేయబడింది"</string>
     <string name="new_notification_text_content_description" msgid="2915029960094389291">"<xliff:g id="NAME">%1$s</xliff:g> మెసేజ్‌ను పంపారు: <xliff:g id="NOTIFICATION">%2$s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index e8a6c16..df358a3 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"ขยายจนเต็มหน้าจอ"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"ยืดจนเต็มหน้าจอ"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"ภาพหน้าจอ"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"ปิดใช้ Smart Lock แล้ว"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ส่งรูปภาพ"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"กำลังบันทึกภาพหน้าจอ..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"กำลังบันทึกภาพหน้าจอ..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"ไม่รู้จักใบหน้า ใช้ลายนิ้วมือแทน"</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"ใช้ลายนิ้วมือของคุณเพื่อดำเนินการต่อ"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"ไม่รู้จักลายนิ้วมือ ใช้การล็อกหน้าจอแทน"</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"กำลังหาใบหน้าคุณ…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"ไอคอนใบหน้า"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"ปุ่มซูมที่ใช้งานร่วมกันได้"</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"แตะอีกครั้งเพื่อเปิด"</string>
     <string name="tap_again" msgid="1315420114387908655">"แตะอีกครั้ง"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"เลื่อนขึ้นเพื่อเปิด"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"กดเพื่อเปิด"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"เลื่อนขึ้นเพื่อลองอีกครั้ง"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"ปลดล็อกเพื่อใช้ NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"องค์กรของคุณเป็นเจ้าของอุปกรณ์นี้"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 8d0a847..a74345d 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"I-zoom upang punan screen"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"I-stretch upang mapuno screen"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Screenshot"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Naka-disable ang Smart Lock"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"nagpadala ng larawan"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Sine-save ang screenshot…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Sine-save ang screenshot…"</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Hindi makilala ang mukha. Gumamit ng fingerprint."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Gamitin ang iyong fingerprint para magpatuloy"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Hindi makilala ang fingerprint. Gamitin na lang ang lock ng screen."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Hinahanap ka…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Face icon"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Button ng zoom ng pagiging tugma."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"I-tap ulit upang buksan"</string>
     <string name="tap_again" msgid="1315420114387908655">"I-tap ulit"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Mag-swipe pataas para buksan"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Pindutin para buksan"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Mag-swipe pataas para subukan ulit"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"I-unlock para magamit ang NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Pagmamay-ari ng iyong organisasyon ang device na ito"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 6b55d1c..51e32458 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Yakınlaştır (ekranı kaplasın)"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Genişlet (ekran kapansın)"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Ekran görüntüsü"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock devre dışı"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"bir resim gönderildi"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Ekran görüntüsü kaydediliyor..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Ekran görüntüsü kaydediliyor..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Yüz tanınamadı. Bunun yerine parmak izi kullanın."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Devam etmek için parmak izinizi kullanın"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Parmak izi tanınamadı. Bunun yerine ekran kilidini kullanın."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Yüzünüz tanınmaya çalışılıyor…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Yüz simgesi"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Uyumluluk zum düğmesi."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Açmak için tekrar dokunun"</string>
     <string name="tap_again" msgid="1315420114387908655">"Tekrar dokunun"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Açmak için yukarı kaydırın"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Açmak için basın"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Tekrar denemek için yukarı kaydırın"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC\'yi kullanmak için kilidi açın"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Bu cihaz, kuruluşunuza ait"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 0a5992b..b1febf5 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Масштабув. на весь екран"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Розтягнути на весь екран"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Знімок екрана"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock вимкнено"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"надіслане зображення"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Збереження знімка екрана..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Збереження знімка екрана..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Обличчя не розпізнано. Скористайтеся відбитком пальця."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Щоб продовжити, скористайтеся відбитком пальця"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Відбиток пальця не розпізнано. Використайте натомість дані для розблокування екрана."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Пошук обличчя…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Значок обличчя"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Кнопка масштабування сумісності."</string>
@@ -456,6 +459,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Торкніться знову, щоб відкрити"</string>
     <string name="tap_again" msgid="1315420114387908655">"Натисніть знову"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Проведіть пальцем угору, щоб відкрити"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Натисніть, щоб відкрити"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Проведіть пальцем угору, щоб повторити спробу"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Розблокуйте екран, щоб скористатись NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Цей пристрій належить вашій організації"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 5a96fa8..fcb53a4 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"پوری سکرین پر زوم کریں"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"پوری سکرین پر پھیلائیں"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"اسکرین شاٹ"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"‏Smart Lock کو غیر فعال کیا گیا"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ایک تصویر بھیجی"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"اسکرین شاٹ محفوظ ہو رہا ہے…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"اسکرین شاٹ محفوظ ہو رہا ہے…"</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"چہرے کی شناخت نہیں ہو سکی۔ اس کے بجائے فنگر پرنٹ استعمال کریں۔"</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"جاری رکھنے کے لیے اپنا فنگر پرنٹ استعمال کریں"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"فنگر پرنٹ کی شناخت نہیں کی جا سکی۔ اس کے بجائے اسکرین لاک کا استعمال کریں۔"</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"آپ کے لیے تلاش کیا جا رہا ہے…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"چہرے کا آئیکن"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"مطابقت پذیری زوم بٹن۔"</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"کھولنے کیلئے دوبارہ تھپتھپائیں"</string>
     <string name="tap_again" msgid="1315420114387908655">"دوبارہ تھپتھپائیں"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"کھولنے کے لیے اوپر سوائپ کريں"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"کھولنے کے لیے دبائیں"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"دوبارہ کوشش کرنے کے لیے اوپر سوائپ کريں"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"‏NFC استعمال کرنے کیلئے غیر مقفل کریں"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"یہ آلہ آپ کی تنظیم کا ہے"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index d96ea064..7822b84 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Ekranga moslashtirish"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Ekran hajmida cho‘zish"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Skrinshot"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock faolsizlantirildi"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"rasm yuborildi"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Skrinshot saqlanmoqda…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Skrinshot saqlanmoqda…"</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Bu yuz notanish. Barmoq izi orqali urining."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Davom etish uchun barmoq izingizdan foydalaning"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Bu barmoq izi notanish. Ekran qulfi orqali urining."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Yuzingiz tekshirilmoqda…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Yuz belgisi"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Kattalashtirish tugmasi mosligi."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Ochish uchun yana bosing"</string>
     <string name="tap_again" msgid="1315420114387908655">"Yana bosing"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Ochish uchun tepaga suring"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Ochish uchun bosing"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Qayta urinish uchun tepaga suring"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"NFC ishlatish uchun qurilma qulfini oching"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Bu qurilma tashkilotingizga tegishli"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 54b4967..5a1ea4c 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"T.phóng để lấp đầy m.hình"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Giãn ra để lấp đầy m.hình"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Chụp ảnh màn hình"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Tính năng Smart Lock đã tắt"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"đã gửi hình ảnh"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Đang lưu ảnh chụp màn hình..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Đang lưu ảnh chụp màn hình..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Không thể nhận dạng khuôn mặt. Hãy dùng vân tay."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Hãy dùng vân tay để tiếp tục"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Không thể nhận dạng vân tay. Hãy dùng phương thức khóa màn hình."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Đang tìm kiếm bạn…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Biểu tượng khuôn mặt"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Nút thu phóng khả năng tương thích."</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Nhấn lại để mở"</string>
     <string name="tap_again" msgid="1315420114387908655">"Nhấn lại"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Vuốt lên để mở"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Nhấn để mở"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Vuốt lên để thử lại"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Mở khóa để sử dụng NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Thiết bị này thuộc về tổ chức của bạn"</string>
diff --git a/packages/SystemUI/res/values-h560dp-xhdpi/config.xml b/packages/SystemUI/res/values-w390dp/config.xml
similarity index 65%
copy from packages/SystemUI/res/values-h560dp-xhdpi/config.xml
copy to packages/SystemUI/res/values-w390dp/config.xml
index cf2017f..222d848 100644
--- a/packages/SystemUI/res/values-h560dp-xhdpi/config.xml
+++ b/packages/SystemUI/res/values-w390dp/config.xml
@@ -1,7 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-
 <!--
-  ~ Copyright (C) 2014 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.
@@ -13,11 +11,9 @@
   ~ distributed under the License is distributed on an "AS IS" BASIS,
   ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
+  ~ limitations under the License.
   -->
-<resources>
-    <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
-     card. -->
-    <integer name="keyguard_max_notification_count">3</integer>
-</resources>
 
+<resources>
+    <bool name="config_showBatteryEstimateQSBH">true</bool>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 3cbcf31..b1968ee 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"缩放以填满屏幕"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"拉伸以填满屏幕"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"屏幕截图"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock 已停用"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"发送了一张图片"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"正在保存屏幕截图..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"正在保存屏幕截图..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"无法识别人脸。请改用指纹。"</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"使用指纹验证身份后才能继续"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"无法识别指纹。请改用屏幕锁定功能。"</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"正在查找您的面孔…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"面孔图标"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"兼容性缩放按钮。"</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"再次点按即可打开"</string>
     <string name="tap_again" msgid="1315420114387908655">"请再点按一次"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"向上滑动即可打开"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"按一下即可打开"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"向上滑动即可重试"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"需要解锁才能使用 NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"此设备归贵单位所有"</string>
@@ -1055,7 +1059,7 @@
     <string name="accessibility_control_change_favorite" msgid="2943178027582253261">"收藏"</string>
     <string name="accessibility_control_change_unfavorite" msgid="6997408061750740327">"取消收藏"</string>
     <string name="accessibility_control_move" msgid="8980344493796647792">"移至位置 <xliff:g id="NUMBER">%d</xliff:g>"</string>
-    <string name="controls_favorite_default_title" msgid="967742178688938137">"控件"</string>
+    <string name="controls_favorite_default_title" msgid="967742178688938137">"控制"</string>
     <string name="controls_favorite_subtitle" msgid="6481675111056961083">"选择要从“快捷设置”菜单访问的控制项"</string>
     <string name="controls_favorite_rearrange" msgid="5616952398043063519">"按住并拖动即可重新排列控制器"</string>
     <string name="controls_favorite_removed" msgid="5276978408529217272">"已移除所有控制器"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 726947f..7c048ea 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"放大為全螢幕"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"放大為全螢幕"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"螢幕截圖"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock 已停用"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"已傳送圖片"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"正在儲存螢幕擷取畫面..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"正在儲存螢幕擷取畫面..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"無法辨識面孔,請改用指紋完成驗證。"</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"請使用您的指紋繼續"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"無法辨識指紋,請改用螢幕鎖定完成驗證。"</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"正在搜尋您的臉孔…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"面孔圖示"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"相容性縮放按鈕。"</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"再次輕按即可開啟"</string>
     <string name="tap_again" msgid="1315420114387908655">"再次輕按"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"向上滑動即可開啟"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"按下即可開啟"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"請向上滑動以再試一次"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"解鎖方可使用 NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"此裝置屬於您的機構"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 437bece..8647aed 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"放大為全螢幕"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"放大為全螢幕"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"螢幕截圖"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Smart Lock 已停用"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"傳送了一張圖片"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"正在儲存螢幕截圖…"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"正在儲存螢幕截圖…"</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"無法辨識臉孔,請改用指紋完成驗證。"</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"使用指紋完成驗證才能繼續操作"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"無法辨識指紋,請改用螢幕鎖定完成驗證。"</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"正在尋找你的臉孔…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"臉孔圖示"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"相容性縮放按鈕。"</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"再次輕觸即可開啟"</string>
     <string name="tap_again" msgid="1315420114387908655">"再輕觸一次"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"向上滑動即可開啟"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"按下即可開啟"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"向上滑動即可重試"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"如要使用 NFC,請先解鎖"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"這部裝置的擁有者為貴機構"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 6b7d577..b0c8121 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -77,6 +77,7 @@
     <string name="compat_mode_on" msgid="4963711187149440884">"Sondeza ukugcwalisa isikrini"</string>
     <string name="compat_mode_off" msgid="7682459748279487945">"Nweba ukugcwalisa isikrini"</string>
     <string name="global_action_screenshot" msgid="2760267567509131654">"Isithombe-skrini"</string>
+    <string name="global_action_smart_lock_disabled" msgid="9097102067802412936">"Ukhiye oSmathi ukhutshaziwe"</string>
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"uthumele isithombe"</string>
     <string name="screenshot_saving_ticker" msgid="6519186952674544916">"Ilondoloz umfanekiso weskrini..."</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Ilondoloz umfanekiso weskrini..."</string>
@@ -184,6 +185,8 @@
     <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Ayibazi ubuso. Sebenzisa izigxivizo zeminwe kunalokho."</string>
     <string name="fingerprint_dialog_use_fingerprint" msgid="923777032861374285">"Sebenzisa izigxivizo zakho zeminwe ukuze uqhubeke"</string>
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock" msgid="4805522676254378353">"Ayizazi izigxivizo zeminwe. Sebenzisa ukukhiya isikrini kunalokho."</string>
+    <!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
+    <skip />
     <string name="face_dialog_looking_for_face" msgid="2656848512116189509">"Kufunwa wena…"</string>
     <string name="accessibility_face_dialog_face_icon" msgid="8335095612223716768">"Isithonjana sobuso"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="5845799798708790509">"Inkinobho evumelekile yokusondeza"</string>
@@ -452,6 +455,7 @@
     <string name="notification_tap_again" msgid="4477318164947497249">"Thepha futhi ukuze uvule"</string>
     <string name="tap_again" msgid="1315420114387908655">"Thepha futhi"</string>
     <string name="keyguard_unlock" msgid="8031975796351361601">"Swayiphela phezulu ukuze uvule"</string>
+    <string name="keyguard_unlock_press" msgid="8488350566398524740">"Chofoza ukuze uvule"</string>
     <string name="keyguard_retry" msgid="886802522584053523">"Swayiphela phezulu ukuze uzame futhi"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Vula ukuze usebenzise i-NFC"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"Le divayisi eyenhlangano yakho"</string>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index b5337d3..3121ce3 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -69,6 +69,10 @@
     <declare-styleable name="DateView">
         <attr name="datePattern" format="string" />
     </declare-styleable>
+    <declare-styleable name="VariableDateView">
+        <attr name="longDatePattern" format="string" />
+        <attr name="shortDatePattern" format="string" />
+    </declare-styleable>
     <declare-styleable name="PseudoGridView">
         <attr name="numColumns" format="integer" />
         <attr name="verticalSpacing" format="dimension" />
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 2260d21..d901267 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -16,7 +16,7 @@
  * limitations under the License.
  */
 -->
-<resources>
+<resources xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
     <drawable name="notification_number_text_color">#ffffffff</drawable>
     <drawable name="ticker_background_color">#ff1d1d1d</drawable>
     <drawable name="system_bar_background">@color/system_bar_background_opaque</drawable>
@@ -69,6 +69,9 @@
     <!-- Shadows under the clock, date and other keyguard text fields -->
     <color name="keyguard_shadow_color">#B2000000</color>
 
+    <!-- Color for the images in keyguard number pad buttons   -->
+    <color name="keyguard_keypad_image_color">@android:color/background_light</color>
+
     <!-- Color for rounded background for activated user in keyguard user switcher -->
     <color name="kg_user_switcher_activated_background_color">#26000000</color>
     <!-- Icon color for user avatars in keyguard user switcher -->
@@ -149,7 +152,6 @@
     <!-- Chosen so fill over background matches single tone -->
     <color name="dark_mode_qs_icon_color_dual_tone_fill">#99000000</color>
 
-    <color name="docked_divider_background">#ff000000</color>
     <color name="docked_divider_handle">#ffffff</color>
     <drawable name="forced_resizable_background">#59000000</drawable>
     <color name="minimize_dock_shadow_start">#60000000</color>
@@ -281,4 +283,16 @@
     <color name="wallet_card_border">#33FFFFFF</color>
 
     <color name="people_tile_background">@android:color/system_accent2_50</color>
+
+    <!-- Internet Dialog -->
+    <!-- Material next state on color-->
+    <color name="settingslib_state_on_color">@color/settingslib_state_on</color>
+    <!-- Material next state off color-->
+    <color name="settingslib_state_off_color">@color/settingslib_state_off</color>
+    <!-- Material next track on color-->
+    <color name="settingslib_track_on_color">@color/settingslib_track_on</color>
+    <!-- Material next track off color-->
+    <color name="settingslib_track_off_color">@color/settingslib_track_off</color>
+    <color name="connected_network_primary_color">#191C18</color>
+    <color name="connected_network_secondary_color">#41493D</color>
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index bf29cf4..9f2938e 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -110,7 +110,7 @@
 
     <!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" -->
     <string name="quick_settings_tiles_stock" translatable="false">
-        internet,wifi,cell,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness
+        internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness
     </string>
 
     <!-- The tiles to display in QuickSettings -->
@@ -162,7 +162,7 @@
 
     <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
      card. -->
-    <integer name="keyguard_max_notification_count">3</integer>
+    <integer name="keyguard_max_notification_count">-1</integer>
 
     <!-- Defines the implementation of the velocity tracker to be used for the panel expansion. Can
          be 'platform' or 'noisy' (i.e. for noisy touch screens). -->
@@ -197,6 +197,11 @@
      low powered state yet. -->
     <bool name="doze_long_press_uses_prox">true</bool>
 
+    <!-- Doze: whether the brightness sensor uses the proximity sensor.
+     If both this parameter and doze_selectively_register_prox are true, registration for the
+     brightness sensor won't occur when the display state is ON. -->
+    <bool name="doze_brightness_uses_prox">true</bool>
+
     <!-- Doze: should notifications be used as a pulse signal? -->
     <bool name="doze_pulse_on_notifications">true</bool>
 
@@ -542,6 +547,11 @@
         <item>@*android:string/status_bar_headset</item>
     </string-array>
 
+
+    <!-- Whether to show estimate in QS header. Default to false in case there's not enough
+     space -->
+    <bool name="config_showBatteryEstimateQSBH">false</bool>
+
     <!-- A path similar to frameworks/base/core/res/res/values/config.xml
       config_mainBuiltInDisplayCutout that describes a path larger than the exact path of a display
       cutout. If present as well as config_enableDisplayCutoutProtection is set to true, then
@@ -666,4 +676,7 @@
          1 - Override the setting to always bypass keyguard
          2 - Override the setting to never bypass keyguard -->
     <integer name="config_face_unlock_bypass_override">0</integer>
+
+    <!-- Flag to activate notification to contents feature -->
+    <bool name="config_notificationToContents">false</bool>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2ee0026..9d37acce 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -56,11 +56,6 @@
     <!-- The amount by which the arrow is shifted to avoid the finger-->
     <dimen name="navigation_edge_finger_offset">48dp</dimen>
 
-    <!-- Luminance threshold to determine black/white contrast for the navigation affordances -->
-    <item name="navigation_luminance_threshold" type="dimen" format="float">0.5</item>
-    <!-- Luminance change threshold that allows applying new value if difference was exceeded -->
-    <item name="navigation_luminance_change_threshold" type="dimen" format="float">0.05</item>
-
     <dimen name="floating_rotation_button_diameter">40dp</dimen>
     <dimen name="floating_rotation_button_min_margin">20dp</dimen>
     <dimen name="floating_rotation_button_taskbar_left_margin">20dp</dimen>
@@ -333,11 +328,10 @@
     <dimen name="global_screenshot_x_scale">80dp</dimen>
     <dimen name="screenshot_bg_protection_height">242dp</dimen>
     <dimen name="screenshot_preview_elevation">4dp</dimen>
-    <dimen name="screenshot_offset_y">24dp</dimen>
+    <dimen name="screenshot_offset_y">8dp</dimen>
     <dimen name="screenshot_offset_x">16dp</dimen>
     <dimen name="screenshot_dismiss_button_tappable_size">48dp</dimen>
     <dimen name="screenshot_dismiss_button_margin">8dp</dimen>
-    <dimen name="screenshot_action_container_offset_y">16dp</dimen>
     <dimen name="screenshot_action_container_corner_radius">18dp</dimen>
     <dimen name="screenshot_action_container_padding_vertical">4dp</dimen>
     <dimen name="screenshot_action_container_margin_horizontal">8dp</dimen>
@@ -538,6 +532,9 @@
     <!-- Gravity for the notification panel -->
     <integer name="notification_panel_layout_gravity">0x31</integer><!-- center_horizontal|top -->
 
+    <!-- Padding for the lock icon on the keyguard. In pixels - should not scale with display size. -->
+    <dimen name="lock_icon_padding">48px</dimen>
+
     <!-- Height of the carrier/wifi name label -->
     <dimen name="carrier_label_height">24dp</dimen>
 
@@ -610,7 +607,7 @@
     <dimen name="qs_detail_item_primary_text_size">16sp</dimen>
     <dimen name="qs_detail_item_secondary_text_size">14sp</dimen>
     <dimen name="qs_detail_empty_text_size">14sp</dimen>
-    <dimen name="qs_detail_margin_top">28dp</dimen>
+    <dimen name="qs_detail_header_margin_top">28dp</dimen>
     <dimen name="qs_detail_back_margin_end">16dp</dimen>
     <dimen name="qs_detail_header_text_padding">16dp</dimen>
     <dimen name="qs_data_usage_text_size">14sp</dimen>
@@ -661,6 +658,8 @@
     <!-- Padding between subtitles and the following text in the QSFooter dialog -->
     <dimen name="qs_footer_dialog_subtitle_padding">20dp</dimen>
 
+    <dimen name="qs_detail_margin_top">@*android:dimen/quick_qs_offset_height</dimen>
+
     <dimen name="seek_bar_height">3dp</dimen>
     <dimen name="seek_bar_corner_radius">3dp</dimen>
 
@@ -918,6 +917,7 @@
 
     <dimen name="keyguard_affordance_fixed_height">48dp</dimen>
     <dimen name="keyguard_affordance_fixed_width">48dp</dimen>
+    <dimen name="keyguard_affordance_fixed_radius">24dp</dimen>
 
     <dimen name="keyguard_affordance_horizontal_offset">32dp</dimen>
     <dimen name="keyguard_affordance_vertical_offset">32dp</dimen>
@@ -1135,6 +1135,7 @@
     <dimen name="global_actions_button_padding">38dp</dimen>
     <dimen name="global_actions_corner_radius">28dp</dimen>
     <dimen name="global_actions_lite_padding">24dp</dimen>
+    <dimen name="global_actions_info_margin">32dp</dimen>
 
     <!-- The maximum offset in either direction that elements are moved horizontally to prevent
          burn-in on AOD. -->
@@ -1146,7 +1147,7 @@
 
     <!-- The maximum offset in either direction that elements are moved vertically to prevent
          burn-in on AOD. -->
-    <dimen name="burn_in_prevention_offset_y_large_clock">42dp</dimen>
+    <dimen name="burn_in_prevention_offset_y_clock">42dp</dimen>
 
     <!-- Clock maximum font size (dp is intentional, to prevent any further scaling) -->
     <dimen name="large_clock_text_size">150dp</dimen>
@@ -1293,7 +1294,7 @@
     <!-- Size of media cards in the QSPanel carousel -->
     <dimen name="qs_media_padding">16dp</dimen>
     <dimen name="qs_media_album_size_small">72dp</dimen>
-    <dimen name="qs_media_album_size">92dp</dimen>
+    <dimen name="qs_media_album_size">84dp</dimen>
     <dimen name="qs_media_album_radius">14dp</dimen>
     <dimen name="qs_media_album_device_padding">26dp</dimen>
     <dimen name="qs_media_info_margin">12dp</dimen>
@@ -1310,13 +1311,14 @@
     <dimen name="qs_footer_horizontal_margin">22dp</dimen>
     <dimen name="qs_media_disabled_seekbar_height">1dp</dimen>
     <dimen name="qs_media_enabled_seekbar_height">2dp</dimen>
-    <dimen name="qs_media_enabled_seekbar_vertical_padding">31dp</dimen>
-    <dimen name="qs_media_disabled_seekbar_vertical_padding">32dp</dimen>
+    <dimen name="qs_media_enabled_seekbar_vertical_padding">28dp</dimen>
+    <dimen name="qs_media_disabled_seekbar_vertical_padding">29dp</dimen>
 
     <!-- Size of Smartspace media recommendations cards in the QSPanel carousel -->
     <dimen name="qs_aa_media_rec_album_size_collapsed">72dp</dimen>
-    <dimen name="qs_aa_media_rec_album_size_expanded">80dp</dimen>
+    <dimen name="qs_aa_media_rec_album_size_expanded">76dp</dimen>
     <dimen name="qs_aa_media_rec_album_margin">8dp</dimen>
+    <dimen name="qs_aa_media_rec_album_margin_vert">4dp</dimen>
     <dimen name="qq_aa_media_rec_header_text_size">16sp</dimen>
 
     <!-- Window magnification -->
@@ -1467,6 +1469,10 @@
     <!-- Maximum overshoot for the pulse expansion -->
     <dimen name="pulse_expansion_max_top_overshoot">32dp</dimen>
 
+    <!-- Alpha in duration in ms for the auth ripple to become fully vislble. If set to 0,
+         it is immediately visible. -->
+    <integer name="auth_ripple_alpha_in_duration">100</integer>
+
     <dimen name="people_space_widget_radius">28dp</dimen>
     <dimen name="people_space_image_radius">20dp</dimen>
     <dimen name="people_space_messages_count_radius">12dp</dimen>
@@ -1591,4 +1597,42 @@
     <!-- The padding between the icon and the text. -->
     <dimen name="ongoing_call_chip_icon_text_padding">4dp</dimen>
     <dimen name="ongoing_call_chip_corner_radius">28dp</dimen>
+
+    <!-- Internet panel related dimensions -->
+    <dimen name="internet_dialog_list_margin">12dp</dimen>
+    <dimen name="internet_dialog_list_max_height">646dp</dimen>
+    <dimen name="internet_dialog_list_max_width">@dimen/match_parent</dimen>
+
+    <!-- Signal icon in internet dialog -->
+    <dimen name="signal_strength_icon_size">24dp</dimen>
+
+    <!-- Internet dialog related dimensions -->
+    <dimen name="internet_dialog_corner_radius">24dp</dimen>
+    <!-- End margin of network layout -->
+    <dimen name="internet_dialog_network_layout_margin">16dp</dimen>
+    <!-- Size of switch bar in internet dialog -->
+    <dimen name="settingslib_switchbar_margin">16dp</dimen>
+    <!-- Minimum width of switch -->
+    <dimen name="settingslib_min_switch_width">52dp</dimen>
+    <!-- Size of layout margin left -->
+    <dimen name="settingslib_switchbar_padding_left">20dp</dimen>
+    <!-- Size of layout margin right -->
+    <dimen name="settingslib_switchbar_padding_right">20dp</dimen>
+    <!-- Radius of switch bar -->
+    <dimen name="settingslib_switch_bar_radius">35dp</dimen>
+    <!-- Margin of switch thumb -->
+    <dimen name="settingslib_switch_thumb_margin">4dp</dimen>
+    <!-- Size of switch thumb -->
+    <dimen name="settingslib_switch_thumb_size">20dp</dimen>
+    <!-- Width of switch track -->
+    <dimen name="settingslib_switch_track_width">52dp</dimen>
+    <!-- Height of switch track -->
+    <dimen name="settingslib_switch_track_height">28dp</dimen>
+    <!-- Radius of switch track -->
+    <dimen name="settingslib_switch_track_radius">35dp</dimen>
+
+    <!-- Height percentage of the parent container occupied by the communal view -->
+    <item name="communal_source_height_percentage" format="float" type="dimen">0.80</item>
+
+    <dimen name="drag_and_drop_icon_size">70dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 62688ca..d785a03 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -211,6 +211,8 @@
 
     <!-- Power menu item for taking a screenshot [CHAR LIMIT=20]-->
     <string name="global_action_screenshot">Screenshot</string>
+    <!-- Message shown in power menu when smart lock has been disabled [CHAR_LIMIT=NONE] -->
+    <string name="global_action_smart_lock_disabled">Smart Lock disabled</string>
 
     <!-- text to show in place of RemoteInput images when they cannot be shown.
          [CHAR LIMIT=50] -->
@@ -438,6 +440,8 @@
     <string name="fingerprint_dialog_use_fingerprint">Use your fingerprint to continue</string>
     <!-- Message shown to ask the user to use screenlock to continue.[CHAR LIMIT=NONE] -->
     <string name="fingerprint_dialog_cant_recognize_fp_use_screenlock">Can\u2019t recognize fingerprint. Use screen lock instead.</string>
+    <!-- Message shown to inform the user a face cannot be recognized and fingerprint should instead be used.[CHAR LIMIT=50] -->
+    <string name="keyguard_face_failed_use_fp">@string/fingerprint_dialog_use_fingerprint_instead</string>
 
     <!-- Message shown when the system-provided face dialog is shown, asking for authentication [CHAR LIMIT=30] -->
     <string name="face_dialog_looking_for_face">Looking for you\u2026</string>
@@ -2985,4 +2989,37 @@
 
     <!-- Content description for a chip in the status bar showing that the user is currently on a phone call. [CHAR LIMIT=NONE] -->
     <string name="ongoing_phone_call_content_description">Ongoing phone call</string>
+
+    <!-- Provider Model: Default title of the mobile network in the mobile layout. [CHAR LIMIT=50] -->
+    <string name="mobile_data_settings_title">Mobile data</string>
+    <!-- Provider Model: Summary text separator for preferences including a short description
+         (eg. "Connected / 5G"). [CHAR LIMIT=50] -->
+    <string name="preference_summary_default_combination"><xliff:g id="state" example="Connected">%1$s</xliff:g> / <xliff:g id="networkMode" example="LTE">%2$s</xliff:g></string>
+    <!-- Provider Model:
+         Summary indicating that a SIM has an active mobile data connection [CHAR LIMIT=50] -->
+    <string name="mobile_data_connection_active">Connected</string>
+    <!-- Provider Model:
+     Summary indicating that a SIM has no mobile data connection [CHAR LIMIT=50] -->
+    <string name="mobile_data_off_summary">Internet won\u0027t auto\u2011connect</string>
+    <!-- Provider Model:
+     Summary indicating that a active SIM and no network available [CHAR LIMIT=50] -->
+    <string name="mobile_data_no_connection">No connection</string>
+    <!-- Provider Model: Summary indicating that no other networks available [CHAR LIMIT=50] -->
+    <string name="non_carrier_network_unavailable">No other networks available</string>
+    <!-- Provider Model: Summary indicating that no networks available [CHAR LIMIT=50] -->
+    <string name="all_network_unavailable">No networks available</string>
+    <!-- Provider Model: Panel title text for turning on the Wi-Fi networks. [CHAR LIMIT=40] -->
+    <string name="turn_on_wifi">Wi\u2011Fi</string>
+    <!-- Provider Model: Title for detail page of wifi network [CHAR LIMIT=30] -->
+    <string name="pref_title_network_details" msgid="7329759534269363308">"Network details"</string>
+    <!-- Provider Model: Panel subtitle for tapping a network to connect to internet. [CHAR LIMIT=60] -->
+    <string name="tap_a_network_to_connect">Tap a network to connect</string>
+    <!-- Provider Model: Panel subtitle for unlocking screen to view networks. [CHAR LIMIT=60] -->
+    <string name="unlock_to_view_networks">Unlock to view networks</string>
+    <!-- Provider Model: Wi-Fi settings. text displayed when Wi-Fi is on and network list is empty [CHAR LIMIT=50]-->
+    <string name="wifi_empty_list_wifi_on">Searching for networks\u2026</string>
+    <!-- Provider Model: Failure notification for connect -->
+    <string name="wifi_failed_connect_message">Failed to connect to network</string>
+    <!-- Provider Model: Title to see all the networks [CHAR LIMIT=50] -->
+    <string name="see_all_networks">See all</string>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 51eabf6..d254742 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -903,4 +903,52 @@
       <!-- Setting a placeholder will avoid using the SystemUI icon on the splash screen.  -->
       <item name="android:windowSplashScreenAnimatedIcon">@drawable/ic_blank</item>
     </style>
+
+    <style name="Animation.InternetDialog" parent="@android:style/Animation.InputMethod">
+    </style>
+
+    <style name="Widget.SliceView.Panel">
+        <item name="titleSize">16sp</item>
+        <item name="rowStyle">@style/SliceRow</item>
+        <item name="android:background">?android:attr/colorBackgroundFloating</item>
+    </style>
+
+    <style name="SliceRow">
+        <!-- 2dp start padding for the start icon -->
+        <item name="titleItemStartPadding">2dp</item>
+        <item name="titleItemEndPadding">0dp</item>
+
+        <!-- Padding between content and the start icon is 14dp -->
+        <item name="contentStartPadding">14dp</item>
+        <!-- Padding between content and end items is 16dp -->
+        <item name="contentEndPadding">16dp</item>
+
+        <!-- Both side margins of end item are 16dp -->
+        <item name="endItemStartPadding">16dp</item>
+        <item name="endItemEndPadding">16dp</item>
+
+        <!-- Both side margins of bottom divider are 12dp -->
+        <item name="bottomDividerStartPadding">12dp</item>
+        <item name="bottomDividerEndPadding">12dp</item>
+
+        <item name="actionDividerHeight">32dp</item>
+    </style>
+
+    <style name="Theme.SystemUI.Dialog.Internet">
+        <item name="android:windowBackground">@drawable/internet_dialog_background</item>
+    </style>
+
+    <style name="MainSwitch.Settingslib" parent="@android:style/Theme.DeviceDefault">
+        <item name="android:switchMinWidth">@dimen/settingslib_min_switch_width</item>
+    </style>
+
+    <style name="TrimmedHorizontalProgressBar"
+           parent="android:Widget.Material.ProgressBar.Horizontal">
+        <item name="android:indeterminateDrawable">
+            @drawable/progress_indeterminate_horizontal_material_trimmed
+        </item>
+        <item name="android:minHeight">4dp</item>
+        <item name="android:maxHeight">4dp</item>
+    </style>
+
 </resources>
diff --git a/packages/SystemUI/res/xml/media_expanded.xml b/packages/SystemUI/res/xml/media_expanded.xml
index 9d706c5..0e284e6 100644
--- a/packages/SystemUI/res/xml/media_expanded.xml
+++ b/packages/SystemUI/res/xml/media_expanded.xml
@@ -93,7 +93,7 @@
         android:layout_marginEnd="@dimen/qs_media_padding"
         android:layout_marginBottom="@dimen/qs_media_info_margin"
         app:layout_constrainedWidth="true"
-        android:layout_marginTop="@dimen/qs_media_info_spacing"
+        android:layout_marginTop="1dp"
         app:layout_constraintTop_toBottomOf="@id/header_title"
         app:layout_constraintStart_toStartOf="@id/header_title"
         app:layout_constraintEnd_toEndOf="parent"
@@ -104,7 +104,8 @@
         android:id="@+id/media_progress_bar"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
-        app:layout_constraintTop_toBottomOf="@id/header_artist"
+        android:layout_marginTop="34dp"
+        app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
         />
@@ -124,7 +125,6 @@
         android:id="@+id/action0"
         android:layout_width="48dp"
         android:layout_height="48dp"
-        android:layout_marginTop="@dimen/qs_media_action_margin"
         android:layout_marginStart="@dimen/qs_media_padding"
         android:layout_marginEnd="@dimen/qs_media_action_spacing"
         android:layout_marginBottom="@dimen/qs_media_action_margin"
@@ -139,7 +139,6 @@
         android:id="@+id/action1"
         android:layout_width="48dp"
         android:layout_height="48dp"
-        android:layout_marginTop="@dimen/qs_media_action_margin"
         android:layout_marginStart="@dimen/qs_media_action_spacing"
         android:layout_marginEnd="@dimen/qs_media_action_spacing"
         android:layout_marginBottom="@dimen/qs_media_action_margin"
@@ -153,7 +152,6 @@
         android:id="@+id/action2"
         android:layout_width="48dp"
         android:layout_height="48dp"
-        android:layout_marginTop="@dimen/qs_media_action_margin"
         android:layout_marginStart="@dimen/qs_media_action_spacing"
         android:layout_marginEnd="@dimen/qs_media_action_spacing"
         android:layout_marginBottom="@dimen/qs_media_action_margin"
@@ -167,7 +165,6 @@
         android:id="@+id/action3"
         android:layout_width="48dp"
         android:layout_height="48dp"
-        android:layout_marginTop="@dimen/qs_media_action_margin"
         android:layout_marginStart="@dimen/qs_media_action_spacing"
         android:layout_marginEnd="@dimen/qs_media_action_spacing"
         android:layout_marginBottom="@dimen/qs_media_action_margin"
@@ -181,7 +178,6 @@
         android:id="@+id/action4"
         android:layout_width="48dp"
         android:layout_height="48dp"
-        android:layout_marginTop="@dimen/qs_media_action_margin"
         android:layout_marginStart="@dimen/qs_media_action_spacing"
         android:layout_marginEnd="@dimen/qs_media_padding"
         android:layout_marginBottom="@dimen/qs_media_action_margin"
diff --git a/packages/SystemUI/res/xml/media_recommendation_expanded.xml b/packages/SystemUI/res/xml/media_recommendation_expanded.xml
index d8e132c..8a3d5ca 100644
--- a/packages/SystemUI/res/xml/media_recommendation_expanded.xml
+++ b/packages/SystemUI/res/xml/media_recommendation_expanded.xml
@@ -43,7 +43,7 @@
         android:layout_height="@dimen/qs_aa_media_rec_album_size_expanded"
         app:layout_constraintWidth_max="@dimen/qs_aa_media_rec_album_size_expanded"
         android:layout_marginTop="@dimen/qs_media_padding"
-        android:layout_marginBottom="@dimen/qs_aa_media_rec_album_margin"
+        android:layout_marginBottom="@dimen/qs_aa_media_rec_album_margin_vert"
         android:layout_marginEnd="@dimen/qs_aa_media_rec_album_margin"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintBottom_toTopOf="@+id/media_horizontal_center_guideline"
@@ -61,7 +61,7 @@
         android:layout_height="@dimen/qs_aa_media_rec_album_size_expanded"
         app:layout_constraintWidth_max="@dimen/qs_aa_media_rec_album_size_expanded"
         android:layout_marginTop="@dimen/qs_media_padding"
-        android:layout_marginBottom="@dimen/qs_aa_media_rec_album_margin"
+        android:layout_marginBottom="@dimen/qs_aa_media_rec_album_margin_vert"
         android:layout_marginEnd="@dimen/qs_aa_media_rec_album_margin"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintBottom_toTopOf="@+id/media_horizontal_center_guideline"
@@ -79,7 +79,7 @@
         android:layout_height="@dimen/qs_aa_media_rec_album_size_expanded"
         app:layout_constraintWidth_max="@dimen/qs_aa_media_rec_album_size_expanded"
         android:layout_marginTop="@dimen/qs_media_padding"
-        android:layout_marginBottom="@dimen/qs_aa_media_rec_album_margin"
+        android:layout_marginBottom="@dimen/qs_aa_media_rec_album_margin_vert"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintBottom_toTopOf="@+id/media_horizontal_center_guideline"
         app:layout_constraintStart_toEndOf="@id/media_cover2_container"
@@ -95,7 +95,7 @@
         android:layout_width="0dp"
         android:layout_height="@dimen/qs_aa_media_rec_album_size_expanded"
         app:layout_constraintWidth_max="@dimen/qs_aa_media_rec_album_size_expanded"
-        android:layout_marginTop="@dimen/qs_aa_media_rec_album_margin"
+        android:layout_marginTop="@dimen/qs_aa_media_rec_album_margin_vert"
         android:layout_marginBottom="@dimen/qs_media_padding"
         android:layout_marginEnd="@dimen/qs_aa_media_rec_album_margin"
         app:layout_constraintTop_toBottomOf="@+id/media_horizontal_center_guideline"
@@ -113,7 +113,7 @@
         android:layout_width="0dp"
         android:layout_height="@dimen/qs_aa_media_rec_album_size_expanded"
         app:layout_constraintWidth_max="@dimen/qs_aa_media_rec_album_size_expanded"
-        android:layout_marginTop="@dimen/qs_aa_media_rec_album_margin"
+        android:layout_marginTop="@dimen/qs_aa_media_rec_album_margin_vert"
         android:layout_marginBottom="@dimen/qs_media_padding"
         android:layout_marginEnd="@dimen/qs_aa_media_rec_album_margin"
         app:layout_constraintTop_toBottomOf="@+id/media_horizontal_center_guideline"
@@ -131,7 +131,7 @@
         android:layout_width="0dp"
         android:layout_height="@dimen/qs_aa_media_rec_album_size_expanded"
         app:layout_constraintWidth_max="@dimen/qs_aa_media_rec_album_size_expanded"
-        android:layout_marginTop="@dimen/qs_aa_media_rec_album_margin"
+        android:layout_marginTop="@dimen/qs_aa_media_rec_album_margin_vert"
         android:layout_marginBottom="@dimen/qs_media_padding"
         app:layout_constraintTop_toBottomOf="@id/media_horizontal_center_guideline"
         app:layout_constraintBottom_toBottomOf="parent"
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt
new file mode 100644
index 0000000..915e7f6
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt
@@ -0,0 +1,146 @@
+/*
+ * 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.shared.animation
+
+import android.graphics.Point
+import android.util.MathUtils.lerp
+import android.view.Surface
+import android.view.View
+import android.view.WindowManager
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import java.lang.ref.WeakReference
+
+/**
+ * Creates an animation where all registered views are moved into their final location
+ * by moving from the center of the screen to the sides
+ */
+class UnfoldMoveFromCenterAnimator(
+    private val windowManager: WindowManager,
+    /**
+     * Allows to set custom translation applier
+     * Could be useful when a view could be translated from
+     * several sources and we want to set the translation
+     * using custom methods instead of [View.setTranslationX] or
+     * [View.setTranslationY]
+     */
+    var translationApplier: TranslationApplier = object : TranslationApplier {}
+) : UnfoldTransitionProgressProvider.TransitionProgressListener {
+
+    private val screenSize = Point()
+    private var isVerticalFold = false
+
+    private val animatedViews: MutableList<AnimatedView> = arrayListOf()
+    private val tmpArray = IntArray(2)
+
+    /**
+     * Updates display properties in order to calculate the initial position for the views
+     * Must be called before [registerViewForAnimation]
+     */
+    fun updateDisplayProperties() {
+        windowManager.defaultDisplay.getSize(screenSize)
+
+        // Simple implementation to get current fold orientation,
+        // this might not be correct on all devices
+        // TODO: use JetPack WindowManager library to get the fold orientation
+        isVerticalFold = windowManager.defaultDisplay.rotation == Surface.ROTATION_0 ||
+            windowManager.defaultDisplay.rotation == Surface.ROTATION_180
+    }
+
+    /**
+     * Registers a view to be animated, the view should be measured and layouted
+     * After finishing the animation it is necessary to clear
+     * the views using [clearRegisteredViews]
+     */
+    fun registerViewForAnimation(view: View) {
+        val animatedView = createAnimatedView(view)
+        animatedViews.add(animatedView)
+    }
+
+    /**
+     * Unregisters all registered views and resets their translation
+     */
+    fun clearRegisteredViews() {
+        onTransitionProgress(1f)
+        animatedViews.clear()
+    }
+
+    override fun onTransitionProgress(progress: Float) {
+        animatedViews.forEach {
+            it.view.get()?.let { view ->
+                translationApplier.apply(
+                    view = view,
+                    x = lerp(it.startTranslationX, it.finishTranslationX, progress),
+                    y = lerp(it.startTranslationY, it.finishTranslationY, progress)
+                )
+            }
+        }
+    }
+
+    private fun createAnimatedView(view: View): AnimatedView {
+        val viewLocation = tmpArray
+        view.getLocationOnScreen(viewLocation)
+
+        val viewX = viewLocation[0].toFloat()
+        val viewY = viewLocation[1].toFloat()
+
+        val viewCenterX = viewX + view.width / 2
+        val viewCenterY = viewY + view.height / 2
+
+        val translationXDiff: Float
+        val translationYDiff: Float
+
+        if (isVerticalFold) {
+            val distanceFromScreenCenterToViewCenter = screenSize.x / 2 - viewCenterX
+            translationXDiff = distanceFromScreenCenterToViewCenter * TRANSLATION_PERCENTAGE
+            translationYDiff = 0f
+        } else {
+            val distanceFromScreenCenterToViewCenter = screenSize.y / 2 - viewCenterY
+            translationXDiff = 0f
+            translationYDiff = distanceFromScreenCenterToViewCenter * TRANSLATION_PERCENTAGE
+        }
+
+        return AnimatedView(
+            view = WeakReference(view),
+            startTranslationX = view.translationX + translationXDiff,
+            startTranslationY = view.translationY + translationYDiff,
+            finishTranslationX = view.translationX,
+            finishTranslationY = view.translationY
+        )
+    }
+
+    /**
+     * Interface that allows to use custom logic to apply translation to view
+     */
+    interface TranslationApplier {
+        /**
+         * Called when we need to apply [x] and [y] translation to [view]
+         */
+        fun apply(view: View, x: Float, y: Float) {
+            view.translationX = x
+            view.translationY = y
+        }
+    }
+
+    private class AnimatedView(
+        val view: WeakReference<View>,
+        val startTranslationX: Float,
+        val startTranslationY: Float,
+        val finishTranslationX: Float,
+        val finishTranslationY: Float
+    )
+}
+
+private const val TRANSLATION_PERCENTAGE = 0.3f
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/RegionSamplingHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/navigationbar/gestural/RegionSamplingHelper.java
rename to packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
index c9a9399..bcfb774 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/RegionSamplingHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.navigationbar.gestural;
+package com.android.systemui.shared.navigationbar;
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
-import android.content.res.Resources;
+import android.annotation.TargetApi;
 import android.graphics.Rect;
+import android.os.Build;
 import android.os.Handler;
 import android.view.CompositionSamplingListener;
 import android.view.SurfaceControl;
@@ -27,17 +28,22 @@
 import android.view.ViewRootImpl;
 import android.view.ViewTreeObserver;
 
-import com.android.systemui.R;
-
 import java.io.PrintWriter;
 import java.util.concurrent.Executor;
 
 /**
  * A helper class to sample regions on the screen and inspect its luminosity.
  */
+@TargetApi(Build.VERSION_CODES.Q)
 public class RegionSamplingHelper implements View.OnAttachStateChangeListener,
         View.OnLayoutChangeListener {
 
+    // Luminance threshold to determine black/white contrast for the navigation affordances.
+    // Passing the threshold of this luminance value will make the button black otherwise white
+    private static final float NAVIGATION_LUMINANCE_THRESHOLD = 0.5f;
+    // Luminance change threshold that allows applying new value if difference was exceeded
+    private static final float NAVIGATION_LUMINANCE_CHANGE_THRESHOLD = 0.05f;
+
     private final Handler mHandler = new Handler();
     private final View mSampledView;
 
@@ -62,9 +68,6 @@
     private boolean mWaitingOnDraw;
     private boolean mIsDestroyed;
 
-    // Passing the threshold of this luminance value will make the button black otherwise white
-    private final float mLuminanceThreshold;
-    private final float mLuminanceChangeThreshold;
     private boolean mFirstSamplingAfterStart;
     private boolean mWindowVisible;
     private boolean mWindowHasBlurs;
@@ -100,9 +103,6 @@
         mSampledView.addOnAttachStateChangeListener(this);
         mSampledView.addOnLayoutChangeListener(this);
 
-        final Resources res = sampledView.getResources();
-        mLuminanceThreshold = res.getFloat(R.dimen.navigation_luminance_threshold);
-        mLuminanceChangeThreshold = res.getFloat(R.dimen.navigation_luminance_change_threshold);
         mCallback = samplingCallback;
     }
 
@@ -217,8 +217,10 @@
 
         // If the difference between the new luma and the current luma is larger than threshold
         // then apply the current luma, this is to prevent small changes causing colors to flicker
-        if (Math.abs(mCurrentMedianLuma - mLastMedianLuma) > mLuminanceChangeThreshold) {
-            mCallback.onRegionDarknessChanged(medianLuma < mLuminanceThreshold /* isRegionDark */);
+        if (Math.abs(mCurrentMedianLuma - mLastMedianLuma)
+                > NAVIGATION_LUMINANCE_CHANGE_THRESHOLD) {
+            mCallback.onRegionDarknessChanged(
+                    medianLuma < NAVIGATION_LUMINANCE_THRESHOLD /* isRegionDark */);
             mLastMedianLuma = medianLuma;
         }
     }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java
index 42bc1d0..895b6cd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java
@@ -15,31 +15,20 @@
 package com.android.systemui.shared.plugins;
 
 import android.content.Context;
-import android.os.Looper;
 
 /**
  * Provides necessary components for initializing {@link PluginManagerImpl}.
  */
 public interface PluginInitializer {
 
-    Looper getBgLooper();
-
     /**
-     * Called from the bg looper during initialization of {@link PluginManagerImpl}.
+     * Return a list of plugins that don't get disabled when an exception occurs.
      */
-    void onPluginManagerInit();
+    String[] getPrivilegedPlugins(Context context);
 
-    String[] getWhitelistedPlugins(Context context);
-
-    PluginEnabler getPluginEnabler(Context context);
 
     /**
-     * Called from {@link PluginManagerImpl#handleWtfs()}.
+     * Called from {@link PluginInstanceManager}.
      */
     void handleWtfs();
-
-    /**
-     * Returns if pluging manager should run in debug mode.
-     */
-    boolean isDebuggable();
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
index 2b35bcd..dcd3b3e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
@@ -14,6 +14,7 @@
 
 package com.android.systemui.shared.plugins;
 
+import android.app.LoadedApk;
 import android.app.Notification;
 import android.app.Notification.Action;
 import android.app.NotificationManager;
@@ -28,9 +29,8 @@
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
+import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -42,9 +42,13 @@
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
 
+import dalvik.system.PathClassLoader;
+
+import java.io.File;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
 
 public class PluginInstanceManager<T extends Plugin> {
 
@@ -58,81 +62,70 @@
     private final String mAction;
     private final boolean mAllowMultiple;
     private final VersionInfo mVersion;
+    private final NotificationManager mNotificationManager;
+    private final PluginEnabler mPluginEnabler;
+    private final InstanceFactory<T> mInstanceFactory;
+    private final ArraySet<String> mPrivilegedPlugins = new ArraySet<>();
+    private final Map<String, ClassLoader> mClassLoaders = new ArrayMap<>();
 
     @VisibleForTesting
-    final MainHandler mMainHandler;
-    @VisibleForTesting
-    final PluginHandler mPluginHandler;
-    private final boolean isDebuggable;
+    private final ArrayList<PluginInfo<T>> mPlugins = new ArrayList<>();
+    private final boolean mIsDebuggable;
     private final PackageManager mPm;
-    private final PluginManagerImpl mManager;
-    private final ArraySet<String> mWhitelistedPlugins = new ArraySet<>();
+    private final PluginInitializer mInitializer;
+    private final Executor mMainExecutor;
+    private final Executor mBgExecutor;
 
-    PluginInstanceManager(Context context, String action, PluginListener<T> listener,
-            boolean allowMultiple, Looper looper, VersionInfo version, PluginManagerImpl manager) {
-        this(context, context.getPackageManager(), action, listener, allowMultiple, looper, version,
-                manager, manager.isDebuggable(), manager.getWhitelistedPlugins());
-    }
+    private PluginManagerImpl.ClassLoaderFilter mParentClassLoader;
 
-    @VisibleForTesting
-    PluginInstanceManager(Context context, PackageManager pm, String action,
-            PluginListener<T> listener, boolean allowMultiple, Looper looper, VersionInfo version,
-            PluginManagerImpl manager, boolean debuggable, String[] pluginWhitelist) {
-        mMainHandler = new MainHandler(Looper.getMainLooper());
-        mPluginHandler = new PluginHandler(looper);
-        mManager = manager;
+    private PluginInstanceManager(Context context, PackageManager pm, String action,
+            PluginListener<T> listener, boolean allowMultiple, Executor mainExecutor,
+            Executor bgExecutor, VersionInfo version, boolean debuggable,
+            PluginInitializer initializer, NotificationManager notificationManager,
+            PluginEnabler pluginEnabler, List<String> privilegedPlugins,
+            InstanceFactory<T> instanceFactory) {
+        mInitializer = initializer;
+        mMainExecutor = mainExecutor;
+        mBgExecutor = bgExecutor;
         mContext = context;
         mPm = pm;
         mAction = action;
         mListener = listener;
         mAllowMultiple = allowMultiple;
         mVersion = version;
-        mWhitelistedPlugins.addAll(Arrays.asList(pluginWhitelist));
-        isDebuggable = debuggable;
-    }
-
-    public PluginInfo<T> getPlugin() {
-        if (Looper.myLooper() != Looper.getMainLooper()) {
-            throw new RuntimeException("Must be called from UI thread");
-        }
-        mPluginHandler.handleQueryPlugins(null /* All packages */);
-        if (mPluginHandler.mPlugins.size() > 0) {
-            mMainHandler.removeMessages(MainHandler.PLUGIN_CONNECTED);
-            PluginInfo<T> info = mPluginHandler.mPlugins.get(0);
-            PluginPrefs.setHasPlugins(mContext);
-            info.mPlugin.onCreate(mContext, info.mPluginContext);
-            return info;
-        }
-        return null;
+        mNotificationManager = notificationManager;
+        mPluginEnabler = pluginEnabler;
+        mInstanceFactory = instanceFactory;
+        mPrivilegedPlugins.addAll(privilegedPlugins);
+        mIsDebuggable = debuggable;
     }
 
     public void loadAll() {
         if (DEBUG) Log.d(TAG, "startListening");
-        mPluginHandler.sendEmptyMessage(PluginHandler.QUERY_ALL);
+        mBgExecutor.execute(this::queryAll);
     }
 
     public void destroy() {
         if (DEBUG) Log.d(TAG, "stopListening");
-        ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
-        for (PluginInfo plugin : plugins) {
-            mMainHandler.obtainMessage(MainHandler.PLUGIN_DISCONNECTED,
-                    plugin.mPlugin).sendToTarget();
+        ArrayList<PluginInfo<T>> plugins = new ArrayList<>(mPlugins);
+        for (PluginInfo<T> pluginInfo : plugins) {
+            mMainExecutor.execute(() -> onPluginDisconnected(pluginInfo.mPlugin));
         }
     }
 
     public void onPackageRemoved(String pkg) {
-        mPluginHandler.obtainMessage(PluginHandler.REMOVE_PKG, pkg).sendToTarget();
+        mBgExecutor.execute(() -> removePkg(pkg));
     }
 
     public void onPackageChange(String pkg) {
-        mPluginHandler.obtainMessage(PluginHandler.REMOVE_PKG, pkg).sendToTarget();
-        mPluginHandler.obtainMessage(PluginHandler.QUERY_PKG, pkg).sendToTarget();
+        mBgExecutor.execute(() -> removePkg(pkg));
+        mBgExecutor.execute(() -> queryPkg(pkg));
     }
 
     public boolean checkAndDisable(String className) {
         boolean disableAny = false;
-        ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
-        for (PluginInfo info : plugins) {
+        ArrayList<PluginInfo<T>> plugins = new ArrayList<>(mPlugins);
+        for (PluginInfo<T> info : plugins) {
             if (className.startsWith(info.mPackage)) {
                 disableAny |= disable(info, PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH);
             }
@@ -141,7 +134,7 @@
     }
 
     public boolean disableAll() {
-        ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
+        ArrayList<PluginInfo<T>> plugins = new ArrayList<>(mPlugins);
         boolean disabledAny = false;
         for (int i = 0; i < plugins.size(); i++) {
             disabledAny |= disable(plugins.get(i), PluginEnabler.DISABLED_FROM_SYSTEM_CRASH);
@@ -149,8 +142,22 @@
         return disabledAny;
     }
 
-    private boolean isPluginWhitelisted(ComponentName pluginName) {
-        for (String componentNameOrPackage : mWhitelistedPlugins) {
+    private boolean isPluginPackagePrivileged(String packageName) {
+        for (String componentNameOrPackage : mPrivilegedPlugins) {
+            ComponentName componentName = ComponentName.unflattenFromString(componentNameOrPackage);
+            if (componentName != null) {
+                if (componentName.getPackageName().equals(packageName)) {
+                    return true;
+                }
+            } else if (componentNameOrPackage.equals(packageName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isPluginPrivileged(ComponentName pluginName) {
+        for (String componentNameOrPackage : mPrivilegedPlugins) {
             ComponentName componentName = ComponentName.unflattenFromString(componentNameOrPackage);
             if (componentName == null) {
                 if (componentNameOrPackage.equals(pluginName.getPackageName())) {
@@ -165,7 +172,7 @@
         return false;
     }
 
-    private boolean disable(PluginInfo info, @PluginEnabler.DisableReason int reason) {
+    private boolean disable(PluginInfo<T> info, @PluginEnabler.DisableReason int reason) {
         // Live by the sword, die by the sword.
         // Misbehaving plugins get disabled and won't come back until uninstall/reinstall.
 
@@ -173,19 +180,19 @@
         // If a plugin is detected in the stack of a crash then this will be called for that
         // plugin, if the plugin causing a crash cannot be identified, they are all disabled
         // assuming one of them must be bad.
-        if (isPluginWhitelisted(pluginComponent)) {
+        if (isPluginPrivileged(pluginComponent)) {
             // Don't disable whitelisted plugins as they are a part of the OS.
             return false;
         }
         Log.w(TAG, "Disabling plugin " + pluginComponent.flattenToShortString());
-        mManager.getPluginEnabler().setDisabled(pluginComponent, reason);
+        mPluginEnabler.setDisabled(pluginComponent, reason);
 
         return true;
     }
 
-    public <T> boolean dependsOn(Plugin p, Class<T> cls) {
-        ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
-        for (PluginInfo info : plugins) {
+    <C> boolean dependsOn(Plugin p, Class<C> cls) {
+        ArrayList<PluginInfo<T>> plugins = new ArrayList<>(mPlugins);
+        for (PluginInfo<T> info : plugins) {
             if (info.mPlugin.getClass().getName().equals(p.getClass().getName())) {
                 return info.mVersion != null && info.mVersion.hasClass(cls);
             }
@@ -199,221 +206,259 @@
                 getClass().getSimpleName(), hashCode(), mAction);
     }
 
-    private class MainHandler extends Handler {
-        private static final int PLUGIN_CONNECTED = 1;
-        private static final int PLUGIN_DISCONNECTED = 2;
-
-        public MainHandler(Looper looper) {
-            super(looper);
+    private void onPluginConnected(PluginInfo<T> pluginInfo) {
+        if (DEBUG) Log.d(TAG, "onPluginConnected");
+        PluginPrefs.setHasPlugins(mContext);
+        mInitializer.handleWtfs();
+        if (!(pluginInfo.mPlugin instanceof PluginFragment)) {
+            // Only call onCreate for plugins that aren't fragments, as fragments
+            // will get the onCreate as part of the fragment lifecycle.
+            pluginInfo.mPlugin.onCreate(mContext, pluginInfo.mPluginContext);
         }
+        mListener.onPluginConnected(pluginInfo.mPlugin, pluginInfo.mPluginContext);
+    }
 
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case PLUGIN_CONNECTED:
-                    if (DEBUG) Log.d(TAG, "onPluginConnected");
-                    PluginPrefs.setHasPlugins(mContext);
-                    PluginInfo<T> info = (PluginInfo<T>) msg.obj;
-                    mManager.handleWtfs();
-                    if (!(msg.obj instanceof PluginFragment)) {
-                        // Only call onDestroy for plugins that aren't fragments, as fragments
-                        // will get the onCreate as part of the fragment lifecycle.
-                        info.mPlugin.onCreate(mContext, info.mPluginContext);
-                    }
-                    mListener.onPluginConnected(info.mPlugin, info.mPluginContext);
-                    break;
-                case PLUGIN_DISCONNECTED:
-                    if (DEBUG) Log.d(TAG, "onPluginDisconnected");
-                    mListener.onPluginDisconnected((T) msg.obj);
-                    if (!(msg.obj instanceof PluginFragment)) {
-                        // Only call onDestroy for plugins that aren't fragments, as fragments
-                        // will get the onDestroy as part of the fragment lifecycle.
-                        ((T) msg.obj).onDestroy();
-                    }
-                    break;
-                default:
-                    super.handleMessage(msg);
-                    break;
+    private void onPluginDisconnected(T plugin) {
+        if (DEBUG) Log.d(TAG, "onPluginDisconnected");
+        mListener.onPluginDisconnected(plugin);
+        if (!(plugin instanceof PluginFragment)) {
+            // Only call onDestroy for plugins that aren't fragments, as fragments
+            // will get the onDestroy as part of the fragment lifecycle.
+            plugin.onDestroy();
+        }
+    }
+
+    private void queryAll() {
+        if (DEBUG) Log.d(TAG, "queryAll " + mAction);
+        for (int i = mPlugins.size() - 1; i >= 0; i--) {
+            PluginInfo<T> pluginInfo = mPlugins.get(i);
+            mMainExecutor.execute(() -> onPluginDisconnected(pluginInfo.mPlugin));
+        }
+        mPlugins.clear();
+        handleQueryPlugins(null);
+    }
+
+    private void removePkg(String pkg) {
+        for (int i = mPlugins.size() - 1; i >= 0; i--) {
+            final PluginInfo<T> pluginInfo = mPlugins.get(i);
+            if (pluginInfo.mPackage.equals(pkg)) {
+                mMainExecutor.execute(() -> onPluginDisconnected(pluginInfo.mPlugin));
+                mPlugins.remove(i);
             }
         }
     }
 
-    private class PluginHandler extends Handler {
-        private static final int QUERY_ALL = 1;
-        private static final int QUERY_PKG = 2;
-        private static final int REMOVE_PKG = 3;
-
-        private final ArrayList<PluginInfo<T>> mPlugins = new ArrayList<>();
-
-        public PluginHandler(Looper looper) {
-            super(looper);
+    private void queryPkg(String pkg) {
+        if (DEBUG) Log.d(TAG, "queryPkg " + pkg + " " + mAction);
+        if (mAllowMultiple || (mPlugins.size() == 0)) {
+            handleQueryPlugins(pkg);
+        } else {
+            if (DEBUG) Log.d(TAG, "Too many of " + mAction);
         }
+    }
 
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case QUERY_ALL:
-                    if (DEBUG) Log.d(TAG, "queryAll " + mAction);
-                    for (int i = mPlugins.size() - 1; i >= 0; i--) {
-                        PluginInfo<T> pluginInfo = mPlugins.get(i);
-                        mMainHandler.obtainMessage(
-                                MainHandler.PLUGIN_DISCONNECTED, pluginInfo.mPlugin).sendToTarget();
-                    }
-                    mPlugins.clear();
-                    handleQueryPlugins(null);
-                    break;
-                case REMOVE_PKG:
-                    String pkg = (String) msg.obj;
-                    for (int i = mPlugins.size() - 1; i >= 0; i--) {
-                        final PluginInfo<T> plugin = mPlugins.get(i);
-                        if (plugin.mPackage.equals(pkg)) {
-                            mMainHandler.obtainMessage(MainHandler.PLUGIN_DISCONNECTED,
-                                    plugin.mPlugin).sendToTarget();
-                            mPlugins.remove(i);
-                        }
-                    }
-                    break;
-                case QUERY_PKG:
-                    String p = (String) msg.obj;
-                    if (DEBUG) Log.d(TAG, "queryPkg " + p + " " + mAction);
-                    if (mAllowMultiple || (mPlugins.size() == 0)) {
-                        handleQueryPlugins(p);
-                    } else {
-                        if (DEBUG) Log.d(TAG, "Too many of " + mAction);
-                    }
-                    break;
-                default:
-                    super.handleMessage(msg);
-            }
+    private void handleQueryPlugins(String pkgName) {
+        // This isn't actually a service and shouldn't ever be started, but is
+        // a convenient PM based way to manage our plugins.
+        Intent intent = new Intent(mAction);
+        if (pkgName != null) {
+            intent.setPackage(pkgName);
         }
-
-        private void handleQueryPlugins(String pkgName) {
-            // This isn't actually a service and shouldn't ever be started, but is
-            // a convenient PM based way to manage our plugins.
-            Intent intent = new Intent(mAction);
-            if (pkgName != null) {
-                intent.setPackage(pkgName);
-            }
-            List<ResolveInfo> result = mPm.queryIntentServices(intent, 0);
-            if (DEBUG) Log.d(TAG, "Found " + result.size() + " plugins");
-            if (result.size() > 1 && !mAllowMultiple) {
-                // TODO: Show warning.
-                Log.w(TAG, "Multiple plugins found for " + mAction);
-                if (DEBUG) {
-                    for (ResolveInfo info : result) {
-                        ComponentName name = new ComponentName(info.serviceInfo.packageName,
-                                info.serviceInfo.name);
-                        Log.w(TAG, "  " + name);
-                    }
+        List<ResolveInfo> result = mPm.queryIntentServices(intent, 0);
+        if (DEBUG) Log.d(TAG, "Found " + result.size() + " plugins");
+        if (result.size() > 1 && !mAllowMultiple) {
+            // TODO: Show warning.
+            Log.w(TAG, "Multiple plugins found for " + mAction);
+            if (DEBUG) {
+                for (ResolveInfo info : result) {
+                    ComponentName name = new ComponentName(info.serviceInfo.packageName,
+                            info.serviceInfo.name);
+                    Log.w(TAG, "  " + name);
                 }
-                return;
             }
-            for (ResolveInfo info : result) {
-                ComponentName name = new ComponentName(info.serviceInfo.packageName,
-                        info.serviceInfo.name);
-                PluginInfo<T> t = handleLoadPlugin(name);
-                if (t == null) continue;
-
-                // add plugin before sending PLUGIN_CONNECTED message
-                mPlugins.add(t);
-                mMainHandler.obtainMessage(mMainHandler.PLUGIN_CONNECTED, t).sendToTarget();
-            }
+            return;
         }
+        for (ResolveInfo info : result) {
+            ComponentName name = new ComponentName(info.serviceInfo.packageName,
+                    info.serviceInfo.name);
+            PluginInfo<T> pluginInfo = handleLoadPlugin(name);
+            if (pluginInfo == null) continue;
 
-        protected PluginInfo<T> handleLoadPlugin(ComponentName component) {
-            // This was already checked, but do it again here to make extra extra sure, we don't
-            // use these on production builds.
-            if (!isDebuggable && !isPluginWhitelisted(component)) {
-                // Never ever ever allow these on production builds, they are only for prototyping.
-                Log.w(TAG, "Plugin cannot be loaded on production build: " + component);
+            // add plugin before sending PLUGIN_CONNECTED message
+            mPlugins.add(pluginInfo);
+            mMainExecutor.execute(() -> onPluginConnected(pluginInfo));
+        }
+    }
+
+    protected PluginInfo<T> handleLoadPlugin(ComponentName component) {
+        // This was already checked, but do it again here to make extra extra sure, we don't
+        // use these on production builds.
+        if (!mIsDebuggable && !isPluginPrivileged(component)) {
+            // Never ever ever allow these on production builds, they are only for prototyping.
+            Log.w(TAG, "Plugin cannot be loaded on production build: " + component);
+            return null;
+        }
+        if (!mPluginEnabler.isEnabled(component)) {
+            if (DEBUG) Log.d(TAG, "Plugin is not enabled, aborting load: " + component);
+            return null;
+        }
+        String pkg = component.getPackageName();
+        String cls = component.getClassName();
+        try {
+            ApplicationInfo info = mPm.getApplicationInfo(pkg, 0);
+            // TODO: This probably isn't needed given that we don't have IGNORE_SECURITY on
+            if (mPm.checkPermission(PLUGIN_PERMISSION, pkg)
+                    != PackageManager.PERMISSION_GRANTED) {
+                Log.d(TAG, "Plugin doesn't have permission: " + pkg);
                 return null;
             }
-            if (!mManager.getPluginEnabler().isEnabled(component)) {
-                if (DEBUG) Log.d(TAG, "Plugin is not enabled, aborting load: " + component);
-                return null;
-            }
-            String pkg = component.getPackageName();
-            String cls = component.getClassName();
+            // Create our own ClassLoader so we can use our own code as the parent.
+            ClassLoader classLoader = getClassLoader(info);
+            Context pluginContext = new PluginContextWrapper(
+                    mContext.createApplicationContext(info, 0), classLoader);
+            Class<?> pluginClass = Class.forName(cls, true, classLoader);
+            // TODO: Only create the plugin before version check if we need it for
+            // legacy version check.
+            T plugin = mInstanceFactory.create(pluginClass);
             try {
-                ApplicationInfo info = mPm.getApplicationInfo(pkg, 0);
-                // TODO: This probably isn't needed given that we don't have IGNORE_SECURITY on
-                if (mPm.checkPermission(PLUGIN_PERMISSION, pkg)
-                        != PackageManager.PERMISSION_GRANTED) {
-                    Log.d(TAG, "Plugin doesn't have permission: " + pkg);
-                    return null;
-                }
-                // Create our own ClassLoader so we can use our own code as the parent.
-                ClassLoader classLoader = mManager.getClassLoader(info);
-                Context pluginContext = new PluginContextWrapper(
-                        mContext.createApplicationContext(info, 0), classLoader);
-                Class<?> pluginClass = Class.forName(cls, true, classLoader);
-                // TODO: Only create the plugin before version check if we need it for
-                // legacy version check.
-                T plugin = (T) pluginClass.newInstance();
+                VersionInfo version = checkVersion(pluginClass, plugin, mVersion);
+                if (DEBUG) Log.d(TAG, "createPlugin");
+                return new PluginInfo<>(pkg, cls, plugin, pluginContext, version);
+            } catch (InvalidVersionException e) {
+                final int icon = Resources.getSystem().getIdentifier(
+                        "stat_sys_warning", "drawable", "android");
+                final int color = Resources.getSystem().getIdentifier(
+                        "system_notification_accent_color", "color", "android");
+                final Notification.Builder nb = new Notification.Builder(mContext,
+                        PluginManager.NOTIFICATION_CHANNEL_ID)
+                                .setStyle(new Notification.BigTextStyle())
+                                .setSmallIcon(icon)
+                                .setWhen(0)
+                                .setShowWhen(false)
+                                .setVisibility(Notification.VISIBILITY_PUBLIC)
+                                .setColor(mContext.getColor(color));
+                String label = cls;
                 try {
-                    VersionInfo version = checkVersion(pluginClass, plugin, mVersion);
-                    if (DEBUG) Log.d(TAG, "createPlugin");
-                    return new PluginInfo(pkg, cls, plugin, pluginContext, version);
-                } catch (InvalidVersionException e) {
-                    final int icon = Resources.getSystem().getIdentifier(
-                            "stat_sys_warning", "drawable", "android");
-                    final int color = Resources.getSystem().getIdentifier(
-                            "system_notification_accent_color", "color", "android");
-                    final Notification.Builder nb = new Notification.Builder(mContext,
-                            PluginManager.NOTIFICATION_CHANNEL_ID)
-                                    .setStyle(new Notification.BigTextStyle())
-                                    .setSmallIcon(icon)
-                                    .setWhen(0)
-                                    .setShowWhen(false)
-                                    .setVisibility(Notification.VISIBILITY_PUBLIC)
-                                    .setColor(mContext.getColor(color));
-                    String label = cls;
-                    try {
-                        label = mPm.getServiceInfo(component, 0).loadLabel(mPm).toString();
-                    } catch (NameNotFoundException e2) {
-                    }
-                    if (!e.isTooNew()) {
-                        // Localization not required as this will never ever appear in a user build.
-                        nb.setContentTitle("Plugin \"" + label + "\" is too old")
-                                .setContentText("Contact plugin developer to get an updated"
-                                        + " version.\n" + e.getMessage());
-                    } else {
-                        // Localization not required as this will never ever appear in a user build.
-                        nb.setContentTitle("Plugin \"" + label + "\" is too new")
-                                .setContentText("Check to see if an OTA is available.\n"
-                                        + e.getMessage());
-                    }
-                    Intent i = new Intent(PluginManagerImpl.DISABLE_PLUGIN).setData(
-                            Uri.parse("package://" + component.flattenToString()));
-                    PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i,
-                            PendingIntent.FLAG_IMMUTABLE);
-                    nb.addAction(new Action.Builder(null, "Disable plugin", pi).build());
-                    mContext.getSystemService(NotificationManager.class)
-                            .notify(SystemMessage.NOTE_PLUGIN, nb.build());
-                    // TODO: Warn user.
-                    Log.w(TAG, "Plugin has invalid interface version " + plugin.getVersion()
-                            + ", expected " + mVersion);
-                    return null;
+                    label = mPm.getServiceInfo(component, 0).loadLabel(mPm).toString();
+                } catch (NameNotFoundException e2) {
                 }
-            } catch (Throwable e) {
-                Log.w(TAG, "Couldn't load plugin: " + pkg, e);
+                if (!e.isTooNew()) {
+                    // Localization not required as this will never ever appear in a user build.
+                    nb.setContentTitle("Plugin \"" + label + "\" is too old")
+                            .setContentText("Contact plugin developer to get an updated"
+                                    + " version.\n" + e.getMessage());
+                } else {
+                    // Localization not required as this will never ever appear in a user build.
+                    nb.setContentTitle("Plugin \"" + label + "\" is too new")
+                            .setContentText("Check to see if an OTA is available.\n"
+                                    + e.getMessage());
+                }
+                Intent i = new Intent(PluginManagerImpl.DISABLE_PLUGIN).setData(
+                        Uri.parse("package://" + component.flattenToString()));
+                PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i,
+                        PendingIntent.FLAG_IMMUTABLE);
+                nb.addAction(new Action.Builder(null, "Disable plugin", pi).build());
+                mNotificationManager.notify(SystemMessage.NOTE_PLUGIN, nb.build());
+                // TODO: Warn user.
+                Log.w(TAG, "Plugin has invalid interface version " + plugin.getVersion()
+                        + ", expected " + mVersion);
                 return null;
             }
+        } catch (Throwable e) {
+            Log.w(TAG, "Couldn't load plugin: " + pkg, e);
+            return null;
+        }
+    }
+
+    private VersionInfo checkVersion(Class<?> pluginClass, T plugin, VersionInfo version)
+            throws InvalidVersionException {
+        VersionInfo pv = new VersionInfo().addClass(pluginClass);
+        if (pv.hasVersionInfo()) {
+            version.checkVersion(pv);
+        } else {
+            int fallbackVersion = plugin.getVersion();
+            if (fallbackVersion != version.getDefaultVersion()) {
+                throw new InvalidVersionException("Invalid legacy version", false);
+            }
+            return null;
+        }
+        return pv;
+    }
+
+    /** Returns class loader specific for the given plugin. */
+    public ClassLoader getClassLoader(ApplicationInfo appInfo) {
+        if (!mIsDebuggable && !isPluginPackagePrivileged(appInfo.packageName)) {
+            Log.w(TAG, "Cannot get class loader for non-privileged plugin. Src:"
+                    + appInfo.sourceDir + ", pkg: " + appInfo.packageName);
+            return null;
+        }
+        if (mClassLoaders.containsKey(appInfo.packageName)) {
+            return mClassLoaders.get(appInfo.packageName);
         }
 
-        private VersionInfo checkVersion(Class<?> pluginClass, T plugin, VersionInfo version)
-                throws InvalidVersionException {
-            VersionInfo pv = new VersionInfo().addClass(pluginClass);
-            if (pv.hasVersionInfo()) {
-                version.checkVersion(pv);
-            } else {
-                int fallbackVersion = plugin.getVersion();
-                if (fallbackVersion != version.getDefaultVersion()) {
-                    throw new InvalidVersionException("Invalid legacy version", false);
-                }
-                return null;
-            }
-            return pv;
+        List<String> zipPaths = new ArrayList<>();
+        List<String> libPaths = new ArrayList<>();
+        LoadedApk.makePaths(null, true, appInfo, zipPaths, libPaths);
+        ClassLoader classLoader = new PathClassLoader(
+                TextUtils.join(File.pathSeparator, zipPaths),
+                TextUtils.join(File.pathSeparator, libPaths),
+                getParentClassLoader());
+        mClassLoaders.put(appInfo.packageName, classLoader);
+        return classLoader;
+    }
+
+    private ClassLoader getParentClassLoader() {
+        if (mParentClassLoader == null) {
+            // Lazily load this so it doesn't have any effect on devices without plugins.
+            mParentClassLoader = new PluginManagerImpl.ClassLoaderFilter(
+                    getClass().getClassLoader(), "com.android.systemui.plugin");
+        }
+        return mParentClassLoader;
+    }
+
+    /**
+     * Construct a {@link PluginInstanceManager}
+     */
+    public static class Factory {
+        private final Context mContext;
+        private final PackageManager mPackageManager;
+        private final Executor mMainExecutor;
+        private final Executor mBgExecutor;
+        private final PluginInitializer mInitializer;
+        private final NotificationManager mNotificationManager;
+        private final PluginEnabler mPluginEnabler;
+        private final List<String> mPrivilegedPlugins;
+        private InstanceFactory<?> mInstanceFactory;
+
+        public Factory(Context context, PackageManager packageManager,
+                Executor mainExecutor, Executor bgExecutor, PluginInitializer initializer,
+                NotificationManager notificationManager, PluginEnabler pluginEnabler,
+                List<String> privilegedPlugins) {
+            mContext = context;
+            mPackageManager = packageManager;
+            mMainExecutor = mainExecutor;
+            mBgExecutor = bgExecutor;
+            mInitializer = initializer;
+            mNotificationManager = notificationManager;
+            mPluginEnabler = pluginEnabler;
+            mPrivilegedPlugins = privilegedPlugins;
+
+            mInstanceFactory = new InstanceFactory<>();
+        }
+
+        @VisibleForTesting
+        <T extends Plugin> Factory setInstanceFactory(InstanceFactory<T> instanceFactory) {
+            mInstanceFactory = instanceFactory;
+            return this;
+        }
+
+        <T extends Plugin> PluginInstanceManager<T> create(
+                String action, PluginListener<T> listener, boolean allowMultiple,
+                VersionInfo version, boolean debuggable) {
+            return new PluginInstanceManager<T>(mContext, mPackageManager, action, listener,
+                    allowMultiple, mMainExecutor, mBgExecutor, version, debuggable,
+                    mInitializer, mNotificationManager, mPluginEnabler,
+                    mPrivilegedPlugins, (InstanceFactory<T>) mInstanceFactory);
         }
     }
 
@@ -443,10 +488,10 @@
         }
     }
 
-    static class PluginInfo<T> {
+    static class PluginInfo<T extends Plugin> {
         private final Context mPluginContext;
         private final VersionInfo mVersion;
-        private String mClass;
+        private final String mClass;
         T mPlugin;
         String mPackage;
 
@@ -459,4 +504,10 @@
             mVersion = info;
         }
     }
+
+    static class InstanceFactory<T extends Plugin> {
+        T create(Class cls) throws IllegalAccessException, InstantiationException {
+            return (T) cls.newInstance();
+        }
+    }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java
index 3f907a8..d264bf2 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java
@@ -27,10 +27,8 @@
     // must be one of the channels created in NotificationChannels.java
     String NOTIFICATION_CHANNEL_ID = "ALR";
 
-    String[] getWhitelistedPlugins();
-
-    <T extends Plugin> T getOneShotPlugin(Class<T> cls);
-    <T extends Plugin> T getOneShotPlugin(String action, Class<?> cls);
+    /** Returns plugins that don't get disabled when an exceptoin occurs. */
+    String[] getPrivilegedPlugins();
 
     <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls);
     <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls,
@@ -38,7 +36,7 @@
     <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
             Class<?> cls);
     <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
-            Class cls, boolean allowMultiple);
+            Class<?> cls, boolean allowMultiple);
 
     void removePluginListener(PluginListener<?> listener);
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
index 2b4cdd6..ea7b0c3 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
@@ -14,48 +14,31 @@
 
 package com.android.systemui.shared.plugins;
 
-import android.app.LoadedApk;
-import android.app.Notification;
-import android.app.Notification.Action;
 import android.app.NotificationManager;
-import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
 import android.net.Uri;
 import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
 import android.os.SystemProperties;
-import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
 import android.widget.Toast;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.systemui.plugins.Plugin;
 import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.annotations.ProvidesInterface;
-import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
 
-import dalvik.system.PathClassLoader;
-
-import java.io.File;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.Thread.UncaughtExceptionHandler;
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
+
 /**
  * @see Plugin
  */
@@ -64,92 +47,42 @@
     private static final String TAG = PluginManagerImpl.class.getSimpleName();
     static final String DISABLE_PLUGIN = "com.android.systemui.action.DISABLE_PLUGIN";
 
-    private final ArrayMap<PluginListener<?>, PluginInstanceManager> mPluginMap
+    private final ArrayMap<PluginListener<?>, PluginInstanceManager<?>> mPluginMap
             = new ArrayMap<>();
     private final Map<String, ClassLoader> mClassLoaders = new ArrayMap<>();
-    private final ArraySet<String> mOneShotPackages = new ArraySet<>();
-    private final ArraySet<String> mWhitelistedPlugins = new ArraySet<>();
+    private final ArraySet<String> mPrivilegedPlugins = new ArraySet<>();
     private final Context mContext;
-    private final PluginInstanceManagerFactory mFactory;
+    private final PluginInstanceManager.Factory mInstanceManagerFactory;
     private final boolean mIsDebuggable;
     private final PluginPrefs mPluginPrefs;
     private final PluginEnabler mPluginEnabler;
-    private final PluginInitializer mPluginInitializer;
-    private ClassLoaderFilter mParentClassLoader;
     private boolean mListening;
-    private boolean mHasOneShot;
-    private Looper mLooper;
 
-    public PluginManagerImpl(Context context, PluginInitializer initializer) {
-        this(context, new PluginInstanceManagerFactory(), initializer.isDebuggable(),
-                Thread.getUncaughtExceptionPreHandler(), initializer);
-    }
-
-    @VisibleForTesting
-    PluginManagerImpl(Context context, PluginInstanceManagerFactory factory, boolean debuggable,
-            UncaughtExceptionHandler defaultHandler, final PluginInitializer initializer) {
+    public PluginManagerImpl(Context context,
+            PluginInstanceManager.Factory instanceManagerFactory,
+            boolean debuggable,
+            Optional<UncaughtExceptionHandler> defaultHandlerOptional,
+            PluginEnabler pluginEnabler,
+            PluginPrefs pluginPrefs,
+            List<String> privilegedPlugins) {
         mContext = context;
-        mFactory = factory;
-        mLooper = initializer.getBgLooper();
+        mInstanceManagerFactory = instanceManagerFactory;
         mIsDebuggable = debuggable;
-        mWhitelistedPlugins.addAll(Arrays.asList(initializer.getWhitelistedPlugins(mContext)));
-        mPluginPrefs = new PluginPrefs(mContext);
-        mPluginEnabler = initializer.getPluginEnabler(mContext);
-        mPluginInitializer = initializer;
+        mPrivilegedPlugins.addAll(privilegedPlugins);
+        mPluginPrefs = pluginPrefs;
+        mPluginEnabler = pluginEnabler;
 
         PluginExceptionHandler uncaughtExceptionHandler = new PluginExceptionHandler(
-                defaultHandler);
+                defaultHandlerOptional);
         Thread.setUncaughtExceptionPreHandler(uncaughtExceptionHandler);
-
-        new Handler(mLooper).post(new Runnable() {
-            @Override
-            public void run() {
-                initializer.onPluginManagerInit();
-            }
-        });
     }
 
     public boolean isDebuggable() {
         return mIsDebuggable;
     }
 
-    public String[] getWhitelistedPlugins() {
-        return mWhitelistedPlugins.toArray(new String[0]);
-    }
-
-    public PluginEnabler getPluginEnabler() {
-        return mPluginEnabler;
-    }
-
-    // TODO(mankoff): This appears to be only called from tests. Remove?
-    public <T extends Plugin> T getOneShotPlugin(Class<T> cls) {
-        ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
-        if (info == null) {
-            throw new RuntimeException(cls + " doesn't provide an interface");
-        }
-        if (TextUtils.isEmpty(info.action())) {
-            throw new RuntimeException(cls + " doesn't provide an action");
-        }
-        return getOneShotPlugin(info.action(), cls);
-    }
-
-    public <T extends Plugin> T getOneShotPlugin(String action, Class<?> cls) {
-        if (Looper.myLooper() != Looper.getMainLooper()) {
-            throw new RuntimeException("Must be called from UI thread");
-        }
-        // Passing null causes compiler to complain about incompatible (generic) types.
-        PluginListener<Plugin> dummy = null;
-        PluginInstanceManager<T> p = mFactory.createPluginInstanceManager(mContext, action, dummy,
-                false, mLooper, cls, this);
-        mPluginPrefs.addAction(action);
-        PluginInfo<T> info = p.getPlugin();
-        if (info != null) {
-            mOneShotPackages.add(info.mPackage);
-            mHasOneShot = true;
-            startListening();
-            return info.mPlugin;
-        }
-        return null;
+    public String[] getPrivilegedPlugins() {
+        return mPrivilegedPlugins.toArray(new String[0]);
     }
 
     public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls) {
@@ -167,10 +100,10 @@
     }
 
     public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
-            Class cls, boolean allowMultiple) {
+            Class<?> cls, boolean allowMultiple) {
         mPluginPrefs.addAction(action);
-        PluginInstanceManager p = mFactory.createPluginInstanceManager(mContext, action, listener,
-                allowMultiple, mLooper, cls, this);
+        PluginInstanceManager<T> p = mInstanceManagerFactory.create(action, listener, allowMultiple,
+                new VersionInfo().addClass(cls), isDebuggable());
         p.loadAll();
         synchronized (this) {
             mPluginMap.put(listener, p);
@@ -208,8 +141,7 @@
     }
 
     private void stopListening() {
-        // Never stop listening if a one-shot is present.
-        if (!mListening || mHasOneShot) return;
+        if (!mListening) return;
         mListening = false;
         mContext.unregisterReceiver(this);
     }
@@ -218,7 +150,7 @@
     public void onReceive(Context context, Intent intent) {
         if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
             synchronized (this) {
-                for (PluginInstanceManager manager : mPluginMap.values()) {
+                for (PluginInstanceManager<?> manager : mPluginMap.values()) {
                     manager.loadAll();
                 }
             }
@@ -226,46 +158,17 @@
             Uri uri = intent.getData();
             ComponentName component = ComponentName.unflattenFromString(
                     uri.toString().substring(10));
-            if (isPluginWhitelisted(component)) {
-                // Don't disable whitelisted plugins as they are a part of the OS.
+            if (isPluginPrivileged(component)) {
+                // Don't disable privileged plugins as they are a part of the OS.
                 return;
             }
-            getPluginEnabler().setDisabled(component, PluginEnabler.DISABLED_INVALID_VERSION);
+            mPluginEnabler.setDisabled(component, PluginEnabler.DISABLED_INVALID_VERSION);
             mContext.getSystemService(NotificationManager.class).cancel(component.getClassName(),
                     SystemMessage.NOTE_PLUGIN);
         } else {
             Uri data = intent.getData();
             String pkg = data.getEncodedSchemeSpecificPart();
             ComponentName componentName = ComponentName.unflattenFromString(pkg);
-            if (mOneShotPackages.contains(pkg)) {
-                int icon = Resources.getSystem().getIdentifier(
-                        "stat_sys_warning", "drawable", "android");
-                int color = Resources.getSystem().getIdentifier(
-                        "system_notification_accent_color", "color", "android");
-                String label = pkg;
-                try {
-                    PackageManager pm = mContext.getPackageManager();
-                    label = pm.getApplicationInfo(pkg, 0).loadLabel(pm).toString();
-                } catch (NameNotFoundException e) {
-                }
-                // Localization not required as this will never ever appear in a user build.
-                final Notification.Builder nb =
-                        new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
-                                .setSmallIcon(icon)
-                                .setWhen(0)
-                                .setShowWhen(false)
-                                .setPriority(Notification.PRIORITY_MAX)
-                                .setVisibility(Notification.VISIBILITY_PUBLIC)
-                                .setColor(mContext.getColor(color))
-                                .setContentTitle("Plugin \"" + label + "\" has updated")
-                                .setContentText("Restart SysUI for changes to take effect.");
-                Intent i = new Intent("com.android.systemui.action.RESTART").setData(
-                            Uri.parse("package://" + pkg));
-                PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i, PendingIntent.FLAG_MUTABLE_UNAUDITED);
-                nb.addAction(new Action.Builder(null, "Restart SysUI", pi).build());
-                mContext.getSystemService(NotificationManager.class)
-                        .notify(SystemMessage.NOTE_PLUGIN, nb.build());
-            }
             if (clearClassLoader(pkg)) {
                 if (Build.IS_ENG) {
                     Toast.makeText(mContext, "Reloading " + pkg, Toast.LENGTH_LONG).show();
@@ -276,22 +179,22 @@
             if (Intent.ACTION_PACKAGE_REPLACED.equals(intent.getAction())
                     && componentName != null) {
                 @PluginEnabler.DisableReason int disableReason =
-                        getPluginEnabler().getDisableReason(componentName);
+                        mPluginEnabler.getDisableReason(componentName);
                 if (disableReason == PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH
                         || disableReason == PluginEnabler.DISABLED_FROM_SYSTEM_CRASH
                         || disableReason == PluginEnabler.DISABLED_INVALID_VERSION) {
                     Log.i(TAG, "Re-enabling previously disabled plugin that has been "
                             + "updated: " + componentName.flattenToShortString());
-                    getPluginEnabler().setEnabled(componentName);
+                    mPluginEnabler.setEnabled(componentName);
                 }
             }
             synchronized (this) {
                 if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
-                    for (PluginInstanceManager manager : mPluginMap.values()) {
+                    for (PluginInstanceManager<?> manager : mPluginMap.values()) {
                         manager.onPackageChange(pkg);
                     }
                 } else {
-                    for (PluginInstanceManager manager : mPluginMap.values()) {
+                    for (PluginInstanceManager<?> manager : mPluginMap.values()) {
                         manager.onPackageRemoved(pkg);
                     }
                 }
@@ -299,41 +202,10 @@
         }
     }
 
-    /** Returns class loader specific for the given plugin. */
-    public ClassLoader getClassLoader(ApplicationInfo appInfo) {
-        if (!mIsDebuggable && !isPluginPackageWhitelisted(appInfo.packageName)) {
-            Log.w(TAG, "Cannot get class loader for non-whitelisted plugin. Src:"
-                    + appInfo.sourceDir + ", pkg: " + appInfo.packageName);
-            return null;
-        }
-        if (mClassLoaders.containsKey(appInfo.packageName)) {
-            return mClassLoaders.get(appInfo.packageName);
-        }
-
-        List<String> zipPaths = new ArrayList<>();
-        List<String> libPaths = new ArrayList<>();
-        LoadedApk.makePaths(null, true, appInfo, zipPaths, libPaths);
-        ClassLoader classLoader = new PathClassLoader(
-                TextUtils.join(File.pathSeparator, zipPaths),
-                TextUtils.join(File.pathSeparator, libPaths),
-                getParentClassLoader());
-        mClassLoaders.put(appInfo.packageName, classLoader);
-        return classLoader;
-    }
-
     private boolean clearClassLoader(String pkg) {
         return mClassLoaders.remove(pkg) != null;
     }
 
-    ClassLoader getParentClassLoader() {
-        if (mParentClassLoader == null) {
-            // Lazily load this so it doesn't have any effect on devices without plugins.
-            mParentClassLoader = new ClassLoaderFilter(getClass().getClassLoader(),
-                    "com.android.systemui.plugin");
-        }
-        return mParentClassLoader;
-    }
-
     public <T> boolean dependsOn(Plugin p, Class<T> cls) {
         synchronized (this) {
             for (int i = 0; i < mPluginMap.size(); i++) {
@@ -345,46 +217,18 @@
         return false;
     }
 
-    public void handleWtfs() {
-        mPluginInitializer.handleWtfs();
-    }
-
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         synchronized (this) {
             pw.println(String.format("  plugin map (%d):", mPluginMap.size()));
-            for (PluginListener listener : mPluginMap.keySet()) {
+            for (PluginListener<?> listener : mPluginMap.keySet()) {
                 pw.println(String.format("    %s -> %s",
                         listener, mPluginMap.get(listener)));
             }
         }
     }
 
-    @VisibleForTesting
-    public static class PluginInstanceManagerFactory {
-        public <T extends Plugin> PluginInstanceManager createPluginInstanceManager(Context context,
-                String action, PluginListener<T> listener, boolean allowMultiple, Looper looper,
-                Class<?> cls, PluginManagerImpl manager) {
-            return new PluginInstanceManager(context, action, listener, allowMultiple, looper,
-                    new VersionInfo().addClass(cls), manager);
-        }
-    }
-
-    private boolean isPluginPackageWhitelisted(String packageName) {
-        for (String componentNameOrPackage : mWhitelistedPlugins) {
-            ComponentName componentName = ComponentName.unflattenFromString(componentNameOrPackage);
-            if (componentName != null) {
-                if (componentName.getPackageName().equals(packageName)) {
-                    return true;
-                }
-            } else if (componentNameOrPackage.equals(packageName)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private boolean isPluginWhitelisted(ComponentName pluginName) {
-        for (String componentNameOrPackage : mWhitelistedPlugins) {
+    private boolean isPluginPrivileged(ComponentName pluginName) {
+        for (String componentNameOrPackage : mPrivilegedPlugins) {
             ComponentName componentName = ComponentName.unflattenFromString(componentNameOrPackage);
             if (componentName != null) {
                 if (componentName.equals(pluginName)) {
@@ -399,7 +243,7 @@
 
     // This allows plugins to include any libraries or copied code they want by only including
     // classes from the plugin library.
-    private static class ClassLoaderFilter extends ClassLoader {
+    static class ClassLoaderFilter extends ClassLoader {
         private final String mPackage;
         private final ClassLoader mBase;
 
@@ -417,16 +261,20 @@
     }
 
     private class PluginExceptionHandler implements UncaughtExceptionHandler {
-        private final UncaughtExceptionHandler mHandler;
+        private final Optional<UncaughtExceptionHandler> mExceptionHandlerOptional;
 
-        private PluginExceptionHandler(UncaughtExceptionHandler handler) {
-            mHandler = handler;
+        private PluginExceptionHandler(
+                Optional<UncaughtExceptionHandler> exceptionHandlerOptional) {
+            mExceptionHandlerOptional = exceptionHandlerOptional;
         }
 
         @Override
         public void uncaughtException(Thread thread, Throwable throwable) {
             if (SystemProperties.getBoolean("plugin.debugging", false)) {
-                mHandler.uncaughtException(thread, throwable);
+                Throwable finalThrowable = throwable;
+                mExceptionHandlerOptional.ifPresent(
+                        handler -> handler.uncaughtException(thread, finalThrowable));
+
                 return;
             }
             // Search for and disable plugins that may have been involved in this crash.
@@ -436,7 +284,7 @@
                 // disable all the plugins, so we can be sure that SysUI is running as
                 // best as possible.
                 synchronized (this) {
-                    for (PluginInstanceManager manager : mPluginMap.values()) {
+                    for (PluginInstanceManager<?> manager : mPluginMap.values()) {
                         disabledAny |= manager.disableAll();
                     }
                 }
@@ -446,7 +294,9 @@
             }
 
             // Run the normal exception handler so we can crash and cleanup our state.
-            mHandler.uncaughtException(thread, throwable);
+            Throwable finalThrowable = throwable;
+            mExceptionHandlerOptional.ifPresent(
+                    handler -> handler.uncaughtException(thread, finalThrowable));
         }
 
         private boolean checkStack(Throwable throwable) {
@@ -454,7 +304,7 @@
             boolean disabledAny = false;
             synchronized (this) {
                 for (StackTraceElement element : throwable.getStackTrace()) {
-                    for (PluginInstanceManager manager : mPluginMap.values()) {
+                    for (PluginInstanceManager<?> manager : mPluginMap.values()) {
                         disabledAny |= manager.checkAndDisable(element.getClassName());
                     }
                 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index de9558e..8bd0f91 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -90,4 +90,9 @@
      * Sent when behavior changes. See WindowInsetsController#@Behavior
      */
     void onSystemBarAttributesChanged(int displayId, int behavior) = 20;
+
+    /**
+     * Sent when screen turned on and ready to use (blocker scrim is hidden)
+     */
+    void onScreenTurnedOn() = 21;
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
index 44271687..b82d896 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
@@ -80,8 +80,7 @@
     public static void begin(View v, @CujType int cujType, long timeout) {
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return;
         Configuration.Builder builder =
-                new Configuration.Builder(cujType)
-                        .setView(v)
+                Configuration.Builder.withView(cujType, v)
                         .setTimeout(timeout);
         InteractionJankMonitor.getInstance().begin(builder);
     }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 4663a9a..b827356 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -116,6 +116,8 @@
     public static final int SYSUI_STATE_IME_SWITCHER_SHOWING = 1 << 20;
     // Device dozing/AOD state
     public static final int SYSUI_STATE_DEVICE_DOZING = 1 << 21;
+    // The home feature is disabled (either by SUW/SysUI/device policy)
+    public static final int SYSUI_STATE_BACK_DISABLED = 1 << 22;
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({SYSUI_STATE_SCREEN_PINNING,
@@ -139,7 +141,8 @@
             SYSUI_STATE_IME_SHOWING,
             SYSUI_STATE_MAGNIFICATION_OVERLAP,
             SYSUI_STATE_IME_SWITCHER_SHOWING,
-            SYSUI_STATE_DEVICE_DOZING
+            SYSUI_STATE_DEVICE_DOZING,
+            SYSUI_STATE_BACK_DISABLED
     })
     public @interface SystemUiStateFlags {}
 
@@ -170,6 +173,7 @@
         str.add((flags & SYSUI_STATE_MAGNIFICATION_OVERLAP) != 0 ? "magnification_overlap" : "");
         str.add((flags & SYSUI_STATE_IME_SWITCHER_SHOWING) != 0 ? "ime_switcher_showing" : "");
         str.add((flags & SYSUI_STATE_DEVICE_DOZING) != 0 ? "device_dozing" : "");
+        str.add((flags & SYSUI_STATE_BACK_DISABLED) != 0 ? "back_disabled" : "");
         return str.toString();
     }
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
index 2407d21..0a14657 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -68,11 +68,16 @@
     public final boolean isNotInRecents;
     public final Rect contentInsets;
     public final ActivityManager.RunningTaskInfo taskInfo;
+    public final boolean allowEnterPip;
     public final int rotationChange;
     public final int windowType;
 
     private final SurfaceControl mStartLeash;
 
+    // Fields used only to unrap into RemoteAnimationTarget
+    private final WindowConfiguration windowConfiguration;
+    private final Rect startBounds;
+
     public RemoteAnimationTargetCompat(RemoteAnimationTarget app) {
         taskId = app.taskId;
         mode = app.mode;
@@ -88,10 +93,13 @@
         contentInsets = app.contentInsets;
         activityType = app.windowConfiguration.getActivityType();
         taskInfo = app.taskInfo;
+        allowEnterPip = app.allowEnterPip;
         rotationChange = 0;
 
         mStartLeash = app.startLeash;
         windowType = app.windowType;
+        windowConfiguration = app.windowConfiguration;
+        startBounds = app.startBounds;
     }
 
     private static int newModeToLegacyMode(int newMode) {
@@ -107,6 +115,14 @@
         }
     }
 
+    public RemoteAnimationTarget unwrap() {
+        return new RemoteAnimationTarget(
+                taskId, mode, leash.getSurfaceControl(), isTranslucent, clipRect, contentInsets,
+                prefixOrderIndex, position, localBounds, screenSpaceBounds, windowConfiguration,
+                isNotInRecents, mStartLeash, startBounds, taskInfo, allowEnterPip, windowType
+        );
+    }
+
 
     /**
      * Almost a copy of Transitions#setupStartState.
@@ -214,9 +230,14 @@
             activityType = ACTIVITY_TYPE_UNDEFINED;
         }
         taskInfo = change.getTaskInfo();
+        allowEnterPip = change.getAllowEnterPip();
         mStartLeash = null;
         rotationChange = change.getEndRotation() - change.getStartRotation();
         windowType = INVALID_WINDOW_TYPE;
+
+        // TODO this probably isn't right but it's unused for now /shrug
+        windowConfiguration = new WindowConfiguration();
+        startBounds = change.getStartAbsBounds();
     }
 
     public static RemoteAnimationTargetCompat[] wrap(RemoteAnimationTarget[] apps) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index 2519284..aac5235 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -19,6 +19,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -172,6 +173,8 @@
         if (mFilter == null) {
             mFilter = new TransitionFilter();
         }
+        // No need to handle the transition that also dismisses keyguard.
+        mFilter.mNotFlags = TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
         mFilter.mRequirements =
                 new TransitionFilter.Requirement[]{new TransitionFilter.Requirement(),
                         new TransitionFilter.Requirement()};
diff --git a/packages/SystemUI/shared/src/com/android/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
similarity index 75%
rename from packages/SystemUI/shared/src/com/android/unfold/UnfoldTransitionFactory.kt
rename to packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index 7594f50..9b5eae8 100644
--- a/packages/SystemUI/shared/src/com/android/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -15,21 +15,21 @@
  */
 @file:JvmName("UnfoldTransitionFactory")
 
-package com.android.unfold
+package com.android.systemui.unfold
 
 import android.content.Context
 import android.hardware.SensorManager
 import android.hardware.devicestate.DeviceStateManager
 import android.os.Handler
-import com.android.unfold.updates.screen.ScreenStatusProvider
-import com.android.unfold.config.ANIMATION_MODE_HINGE_ANGLE
-import com.android.unfold.config.ResourceUnfoldTransitionConfig
-import com.android.unfold.config.UnfoldTransitionConfig
-import com.android.unfold.progress.FixedTimingTransitionProgressProvider
-import com.android.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
-import com.android.unfold.updates.DeviceFoldStateProvider
-import com.android.unfold.updates.hinge.EmptyHingeAngleProvider
-import com.android.unfold.updates.hinge.RotationSensorHingeAngleProvider
+import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
+import com.android.systemui.unfold.config.ANIMATION_MODE_HINGE_ANGLE
+import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig
+import com.android.systemui.unfold.config.UnfoldTransitionConfig
+import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider
+import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
+import com.android.systemui.unfold.updates.DeviceFoldStateProvider
+import com.android.systemui.unfold.updates.hinge.EmptyHingeAngleProvider
+import com.android.systemui.unfold.updates.hinge.RotationSensorHingeAngleProvider
 import java.lang.IllegalStateException
 import java.util.concurrent.Executor
 
diff --git a/packages/SystemUI/shared/src/com/android/unfold/UnfoldTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
similarity index 84%
rename from packages/SystemUI/shared/src/com/android/unfold/UnfoldTransitionProgressProvider.kt
rename to packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
index 2ddb49c..e17f43e 100644
--- a/packages/SystemUI/shared/src/com/android/unfold/UnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
@@ -13,10 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.unfold
+package com.android.systemui.unfold
 
 import android.annotation.FloatRange
-import com.android.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
 import com.android.systemui.statusbar.policy.CallbackController
 
 /**
@@ -31,8 +31,8 @@
     fun destroy()
 
     interface TransitionProgressListener {
-        fun onTransitionStarted()
-        fun onTransitionFinished()
-        fun onTransitionProgress(@FloatRange(from = 0.0, to = 1.0) progress: Float)
+        fun onTransitionStarted() {}
+        fun onTransitionFinished() {}
+        fun onTransitionProgress(@FloatRange(from = 0.0, to = 1.0) progress: Float) {}
     }
 }
diff --git a/packages/SystemUI/shared/src/com/android/unfold/config/ResourceUnfoldTransitionConfig.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
similarity index 96%
rename from packages/SystemUI/shared/src/com/android/unfold/config/ResourceUnfoldTransitionConfig.kt
rename to packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
index bde87a5..fa6b5de 100644
--- a/packages/SystemUI/shared/src/com/android/unfold/config/ResourceUnfoldTransitionConfig.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.unfold.config
+package com.android.systemui.unfold.config
 
 import android.content.Context
 import android.os.SystemProperties
diff --git a/packages/SystemUI/shared/src/com/android/unfold/config/UnfoldTransitionConfig.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt
similarity index 95%
rename from packages/SystemUI/shared/src/com/android/unfold/config/UnfoldTransitionConfig.kt
rename to packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt
index f000c69..75d9dc3 100644
--- a/packages/SystemUI/shared/src/com/android/unfold/config/UnfoldTransitionConfig.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.unfold.config
+package com.android.systemui.unfold.config
 
 import android.annotation.IntDef
 
diff --git a/packages/SystemUI/shared/src/com/android/unfold/progress/FixedTimingTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt
similarity index 86%
rename from packages/SystemUI/shared/src/com/android/unfold/progress/FixedTimingTransitionProgressProvider.kt
rename to packages/SystemUI/shared/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt
index acfe073..732882e 100644
--- a/packages/SystemUI/shared/src/com/android/unfold/progress/FixedTimingTransitionProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt
@@ -13,17 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.unfold.progress
+package com.android.systemui.unfold.progress
 
 import android.animation.Animator
 import android.animation.ObjectAnimator
 import android.util.FloatProperty
-import com.android.unfold.UnfoldTransitionProgressProvider
-import com.android.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
-import com.android.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
-import com.android.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE
-import com.android.unfold.updates.FoldStateProvider
-import com.android.unfold.updates.FoldStateProvider.FoldUpdate
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
+import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE
+import com.android.systemui.unfold.updates.FoldStateProvider
+import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
 
 /**
  * Emits animation progress with fixed timing after unfolding
diff --git a/packages/SystemUI/shared/src/com/android/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
similarity index 89%
rename from packages/SystemUI/shared/src/com/android/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
rename to packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
index d9d037f..b111892 100644
--- a/packages/SystemUI/shared/src/com/android/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
@@ -13,21 +13,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.unfold.progress
+package com.android.systemui.unfold.progress
 
 import android.os.Handler
 import androidx.dynamicanimation.animation.DynamicAnimation
 import androidx.dynamicanimation.animation.FloatPropertyCompat
 import androidx.dynamicanimation.animation.SpringAnimation
 import androidx.dynamicanimation.animation.SpringForce
-import com.android.unfold.UnfoldTransitionProgressProvider
-import com.android.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
-import com.android.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
-import com.android.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
-import com.android.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE
-import com.android.unfold.updates.FoldStateProvider
-import com.android.unfold.updates.FoldStateProvider.FoldUpdate
-import com.android.unfold.updates.FoldStateProvider.FoldUpdatesListener
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
+import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
+import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE
+import com.android.systemui.unfold.updates.FoldStateProvider
+import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
+import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
 
 /**
  * Maps fold updates to unfold transition progress using DynamicAnimation.
diff --git a/packages/SystemUI/shared/src/com/android/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
similarity index 90%
rename from packages/SystemUI/shared/src/com/android/unfold/updates/DeviceFoldStateProvider.kt
rename to packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index 3a21b80..949652b 100644
--- a/packages/SystemUI/shared/src/com/android/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -13,16 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.unfold.updates
+package com.android.systemui.unfold.updates
 
 import android.content.Context
 import android.hardware.devicestate.DeviceStateManager
 import androidx.core.util.Consumer
-import com.android.unfold.updates.screen.ScreenStatusProvider
-import com.android.unfold.updates.FoldStateProvider.FoldUpdate
-import com.android.unfold.updates.FoldStateProvider.FoldUpdatesListener
-import com.android.unfold.updates.hinge.FULLY_OPEN_DEGREES
-import com.android.unfold.updates.hinge.HingeAngleProvider
+import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
+import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
+import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
+import com.android.systemui.unfold.updates.hinge.FULLY_OPEN_DEGREES
+import com.android.systemui.unfold.updates.hinge.HingeAngleProvider
 import java.util.concurrent.Executor
 
 internal class DeviceFoldStateProvider(
diff --git a/packages/SystemUI/shared/src/com/android/unfold/updates/FoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
similarity index 93%
rename from packages/SystemUI/shared/src/com/android/unfold/updates/FoldStateProvider.kt
rename to packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
index 2c3a6ec..4c6d241 100644
--- a/packages/SystemUI/shared/src/com/android/unfold/updates/FoldStateProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
@@ -13,11 +13,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.unfold.updates
+package com.android.systemui.unfold.updates
 
 import android.annotation.FloatRange
 import android.annotation.IntDef
-import com.android.unfold.updates.FoldStateProvider.FoldUpdatesListener
+import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
 import com.android.systemui.statusbar.policy.CallbackController
 
 /**
diff --git a/packages/SystemUI/shared/src/com/android/unfold/updates/hinge/EmptyHingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt
similarity index 85%
rename from packages/SystemUI/shared/src/com/android/unfold/updates/hinge/EmptyHingeAngleProvider.kt
rename to packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt
index 905b086..9b58b1f 100644
--- a/packages/SystemUI/shared/src/com/android/unfold/updates/hinge/EmptyHingeAngleProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt
@@ -1,4 +1,4 @@
-package com.android.unfold.updates.hinge
+package com.android.systemui.unfold.updates.hinge
 
 import androidx.core.util.Consumer
 
diff --git a/packages/SystemUI/shared/src/com/android/unfold/updates/hinge/HingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt
similarity index 84%
rename from packages/SystemUI/shared/src/com/android/unfold/updates/hinge/HingeAngleProvider.kt
rename to packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt
index 4196f60..8549913 100644
--- a/packages/SystemUI/shared/src/com/android/unfold/updates/hinge/HingeAngleProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt
@@ -1,4 +1,4 @@
-package com.android.unfold.updates.hinge
+package com.android.systemui.unfold.updates.hinge
 
 import androidx.core.util.Consumer
 import com.android.systemui.statusbar.policy.CallbackController
diff --git a/packages/SystemUI/shared/src/com/android/unfold/updates/hinge/RotationSensorHingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/RotationSensorHingeAngleProvider.kt
similarity index 97%
rename from packages/SystemUI/shared/src/com/android/unfold/updates/hinge/RotationSensorHingeAngleProvider.kt
rename to packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/RotationSensorHingeAngleProvider.kt
index 011582e..8b6eecf 100644
--- a/packages/SystemUI/shared/src/com/android/unfold/updates/hinge/RotationSensorHingeAngleProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/RotationSensorHingeAngleProvider.kt
@@ -1,4 +1,4 @@
-package com.android.unfold.updates.hinge
+package com.android.systemui.unfold.updates.hinge
 
 import android.hardware.Sensor
 import android.hardware.SensorEvent
diff --git a/packages/SystemUI/shared/src/com/android/unfold/updates/screen/ScreenStatusProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt
similarity index 87%
rename from packages/SystemUI/shared/src/com/android/unfold/updates/screen/ScreenStatusProvider.kt
rename to packages/SystemUI/shared/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt
index a65e888..1eec803 100644
--- a/packages/SystemUI/shared/src/com/android/unfold/updates/screen/ScreenStatusProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt
@@ -13,9 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.unfold.updates.screen
+package com.android.systemui.unfold.updates.screen
 
-import com.android.unfold.updates.screen.ScreenStatusProvider.ScreenListener
+import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener
 import com.android.systemui.statusbar.policy.CallbackController
 
 interface ScreenStatusProvider : CallbackController<ScreenListener> {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 9e456cf..11c4b6a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -99,6 +99,8 @@
     private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
     private SmartspaceTransitionController mSmartspaceTransitionController;
 
+    private boolean mOnlyClock = false;
+
     @Inject
     public KeyguardClockSwitchController(
             KeyguardClockSwitch keyguardClockSwitch,
@@ -131,6 +133,13 @@
     }
 
     /**
+     * Mostly used for alternate displays, limit the information shown
+     */
+    public void setOnlyClock(boolean onlyClock) {
+        mOnlyClock = onlyClock;
+    }
+
+    /**
      * Attach the controller to the view it relates to.
      */
     @Override
@@ -168,6 +177,16 @@
         }
         mColorExtractor.addOnColorsChangedListener(mColorsListener);
         mView.updateColors(getGradientColors());
+
+        if (mOnlyClock) {
+            View ksa = mView.findViewById(R.id.keyguard_status_area);
+            ksa.setVisibility(View.GONE);
+
+            View nic = mView.findViewById(
+                    R.id.left_aligned_notification_icon_container);
+            nic.setVisibility(View.GONE);
+            return;
+        }
         updateAodIcons();
 
         if (mSmartspaceController.isEnabled()) {
@@ -215,16 +234,6 @@
         mView.setClockPlugin(null, mStatusBarStateController.getState());
 
         mSmartspaceController.disconnect();
-
-        // TODO: This is an unfortunate necessity since smartspace plugin retains a single instance
-        // of the smartspace view -- if we don't remove the view, it can't be reused by a later
-        // instance of this class. In order to fix this, we need to modify the plugin so that
-        // (a) we get a new view each time and (b) we can properly clean up an old view by making
-        // it unregister itself as a plugin listener.
-        if (mSmartspaceView != null) {
-            mView.removeView(mSmartspaceView);
-            mSmartspaceView = null;
-        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 76a7473..cac90ea 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -332,6 +332,7 @@
                     .build(findViewById(R.id.clock))
                     .getKeyguardClockSwitchController();
 
+            mKeyguardClockSwitchController.setOnlyClock(true);
             mKeyguardClockSwitchController.init();
         }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
deleted file mode 100644
index 0785cc3..0000000
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardFaceListenModel.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.android.keyguard
-
-import android.annotation.CurrentTimeMillisLong
-
-/**
- * Data class for tracking information associated with [KeyguardUpdateMonitor.shouldListenForFace]
- * method calls.
- */
-data class KeyguardFaceListenModel(
-    @CurrentTimeMillisLong val timeMillis: Long,
-    val userId: Int,
-    val isListeningForFace: Boolean,
-    val isBouncer: Boolean,
-    val isAuthInterruptActive: Boolean,
-    val isOccludingAppRequestingFaceAuth: Boolean,
-    val isKeyguardAwake: Boolean,
-    val isListeningForFaceAssistant: Boolean,
-    val isSwitchingUser: Boolean,
-    val isFaceDisabled: Boolean,
-    val isBecauseCannotSkipBouncer: Boolean,
-    val isKeyguardGoingAway: Boolean,
-    val isBiometricSettingEnabledForUser: Boolean,
-    val isLockIconPressed: Boolean,
-    val isScanningAllowedByStrongAuth: Boolean,
-    val isPrimaryUser: Boolean,
-    val isSecureCameraLaunched: Boolean,
-    val isFaceAuthenticated: Boolean
-)
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 97d3a5a..75425e1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -28,6 +28,7 @@
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.statusbar.policy.DevicePostureController;
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
@@ -166,6 +167,7 @@
         private final TelephonyManager mTelephonyManager;
         private final EmergencyButtonController.Factory mEmergencyButtonControllerFactory;
         private final FalsingCollector mFalsingCollector;
+        private final DevicePostureController mDevicePostureController;
 
         @Inject
         public Factory(KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -175,7 +177,8 @@
                 InputMethodManager inputMethodManager, @Main DelayableExecutor mainExecutor,
                 @Main Resources resources, LiftToActivateListener liftToActivateListener,
                 TelephonyManager telephonyManager, FalsingCollector falsingCollector,
-                EmergencyButtonController.Factory emergencyButtonControllerFactory) {
+                EmergencyButtonController.Factory emergencyButtonControllerFactory,
+                DevicePostureController devicePostureController) {
             mKeyguardUpdateMonitor = keyguardUpdateMonitor;
             mLockPatternUtils = lockPatternUtils;
             mLatencyTracker = latencyTracker;
@@ -187,6 +190,7 @@
             mTelephonyManager = telephonyManager;
             mEmergencyButtonControllerFactory = emergencyButtonControllerFactory;
             mFalsingCollector = falsingCollector;
+            mDevicePostureController = devicePostureController;
         }
 
         /** Create a new {@link KeyguardInputViewController}. */
@@ -200,7 +204,8 @@
                 return new KeyguardPatternViewController((KeyguardPatternView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
                         keyguardSecurityCallback, mLatencyTracker, mFalsingCollector,
-                        emergencyButtonController, mMessageAreaControllerFactory);
+                        emergencyButtonController, mMessageAreaControllerFactory,
+                        mDevicePostureController);
             } else if (keyguardInputView instanceof KeyguardPasswordView) {
                 return new KeyguardPasswordViewController((KeyguardPasswordView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
@@ -212,7 +217,8 @@
                 return new KeyguardPinViewController((KeyguardPINView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
                         keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
-                        mLiftToActivateListener, emergencyButtonController, mFalsingCollector);
+                        mLiftToActivateListener, emergencyButtonController, mFalsingCollector,
+                        mDevicePostureController);
             } else if (keyguardInputView instanceof KeyguardSimPinView) {
                 return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
new file mode 100644
index 0000000..9286175
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
@@ -0,0 +1,77 @@
+package com.android.keyguard
+
+import android.annotation.CurrentTimeMillisLong
+import android.hardware.biometrics.BiometricAuthenticator.Modality
+import android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE
+import android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT
+
+/** Verbose logging for various keyguard listening states. */
+sealed class KeyguardListenModel {
+    /** Timestamp of the state change. */
+    abstract val timeMillis: Long
+    /** Current user */
+    abstract val userId: Int
+    /** If keyguard is listening for the given [modality]. */
+    abstract val listening: Boolean
+    /** Sensor type */
+    @Modality abstract val modality: Int
+}
+
+/**
+ * Verbose debug information associated with [KeyguardUpdateMonitor.shouldListenForFingerprint].
+ */
+data class KeyguardFingerprintListenModel(
+    @CurrentTimeMillisLong override val timeMillis: Long,
+    override val userId: Int,
+    override val listening: Boolean,
+    // keep sorted
+    val biometricEnabledForUser: Boolean,
+    val bouncer: Boolean,
+    val canSkipBouncer: Boolean,
+    val credentialAttempted: Boolean,
+    val deviceInteractive: Boolean,
+    val dreaming: Boolean,
+    val encryptedOrLockdown: Boolean,
+    val fingerprintDisabled: Boolean,
+    val fingerprintLockedOut: Boolean,
+    val goingToSleep: Boolean,
+    val keyguardGoingAway: Boolean,
+    val keyguardIsVisible: Boolean,
+    val keyguardOccluded: Boolean,
+    val occludingAppRequestingFp: Boolean,
+    val primaryUser: Boolean,
+    val shouldListenForFingerprintAssistant: Boolean,
+    val switchingUser: Boolean,
+    val udfps: Boolean,
+    val userDoesNotHaveTrust: Boolean,
+    val userNeedsStrongAuth: Boolean
+) : KeyguardListenModel() {
+    override val modality: Int = TYPE_FACE
+}
+
+/**
+ * Verbose debug information associated with [KeyguardUpdateMonitor.shouldListenForFace].
+ */
+data class KeyguardFaceListenModel(
+    @CurrentTimeMillisLong override val timeMillis: Long,
+    override val userId: Int,
+    override val listening: Boolean,
+    // keep sorted
+    val authInterruptActive: Boolean,
+    val becauseCannotSkipBouncer: Boolean,
+    val biometricSettingEnabledForUser: Boolean,
+    val bouncer: Boolean,
+    val faceAuthenticated: Boolean,
+    val faceDisabled: Boolean,
+    val keyguardAwake: Boolean,
+    val keyguardGoingAway: Boolean,
+    val listeningForFaceAssistant: Boolean,
+    val lockIconPressed: Boolean,
+    val occludingAppRequestingFaceAuth: Boolean,
+    val primaryUser: Boolean,
+    val scanningAllowedByStrongAuth: Boolean,
+    val secureCameraLaunched: Boolean,
+    val switchingUser: Boolean
+) : KeyguardListenModel() {
+    override val modality: Int = TYPE_FINGERPRINT
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenQueue.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenQueue.kt
new file mode 100644
index 0000000..f13a59a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenQueue.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.keyguard
+
+import androidx.annotation.VisibleForTesting
+import java.io.PrintWriter
+import java.text.DateFormat
+import java.text.SimpleDateFormat
+import java.util.Date
+import java.util.Locale
+import kotlin.collections.ArrayDeque
+
+private val DEFAULT_FORMATTING = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US)
+
+/** Queue for verbose logging checks for the listening state. */
+class KeyguardListenQueue(
+    val sizePerModality: Int = 20
+) {
+    private val faceQueue = ArrayDeque<KeyguardFaceListenModel>()
+    private val fingerprintQueue = ArrayDeque<KeyguardFingerprintListenModel>()
+
+    @get:VisibleForTesting val models: List<KeyguardListenModel>
+        get() = faceQueue + fingerprintQueue
+
+    /** Push a [model] to the queue (will be logged until the queue exceeds [sizePerModality]). */
+    fun add(model: KeyguardListenModel) {
+        val queue = when (model) {
+            is KeyguardFaceListenModel -> faceQueue.apply { add(model) }
+            is KeyguardFingerprintListenModel -> fingerprintQueue.apply { add(model) }
+        }
+
+        if (queue.size > sizePerModality) {
+            queue.removeFirstOrNull()
+        }
+    }
+
+    /** Print verbose logs via the [writer]. */
+    @JvmOverloads
+    fun print(writer: PrintWriter, dateFormat: DateFormat = DEFAULT_FORMATTING) {
+        val stringify: (KeyguardListenModel) -> String = { model ->
+            "    ${dateFormat.format(Date(model.timeMillis))} $model"
+        }
+
+        writer.println("  Face listen results (last ${faceQueue.size} calls):")
+        for (model in faceQueue) {
+            writer.println(stringify(model))
+        }
+        writer.println("  Fingerprint listen results (last ${fingerprintQueue.size} calls):")
+        for (model in fingerprintQueue) {
+            writer.println(stringify(model))
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
index 82ade7a..1efda7e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
@@ -16,19 +16,23 @@
 
 package com.android.keyguard;
 
+import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED;
+import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN;
+
 import android.content.Context;
 import android.content.res.Configuration;
 import android.util.AttributeSet;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.animation.AnimationUtils;
 
 import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.constraintlayout.widget.ConstraintSet;
 
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.settingslib.animation.AppearAnimationUtils;
 import com.android.settingslib.animation.DisappearAnimationUtils;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.DevicePostureController.DevicePostureInt;
 
 /**
  * Displays a PIN pad for unlocking.
@@ -38,9 +42,10 @@
     private final AppearAnimationUtils mAppearAnimationUtils;
     private final DisappearAnimationUtils mDisappearAnimationUtils;
     private final DisappearAnimationUtils mDisappearAnimationUtilsLocked;
-    private ViewGroup mContainer;
+    private ConstraintLayout mContainer;
     private int mDisappearYTranslation;
     private View[][] mViews;
+    @DevicePostureInt private int mLastDevicePosture = DEVICE_POSTURE_UNKNOWN;
 
     public KeyguardPINView(Context context) {
         this(context, null);
@@ -67,6 +72,11 @@
         updateMargins();
     }
 
+    void onDevicePostureChanged(@DevicePostureInt int posture) {
+        mLastDevicePosture = posture;
+        updateMargins();
+    }
+
     @Override
     protected void resetState() {
     }
@@ -109,6 +119,16 @@
                 key.setLayoutParams(lp);
             }
         }
+
+        // Update the guideline based on the device posture...
+        float halfOpenPercentage =
+                mContext.getResources().getFloat(R.dimen.half_opened_bouncer_height_ratio);
+
+        ConstraintSet cs = new ConstraintSet();
+        cs.clone(mContainer);
+        cs.setGuidelinePercent(R.id.pin_pad_top_guideline,
+                mLastDevicePosture == DEVICE_POSTURE_HALF_OPENED ? halfOpenPercentage : 0.0f);
+        cs.applyTo(mContainer);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 90c1e40..3a3d308 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -209,6 +209,11 @@
                     @Override
                     public void onCancelled(
                             @Nullable WindowInsetsAnimationController controller) {
+                        // It is possible to be denied control of ime insets, which means onReady
+                        // is never called. We still need to notify the runnables in order to
+                        // complete the bouncer disappearing
+                        runOnFinishImeAnimationRunnable();
+                        finishRunnable.run();
                     }
                 });
         return true;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index 98e7fb4..a35aedf 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -15,6 +15,8 @@
  */
 package com.android.keyguard;
 
+import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED;
+
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.SystemClock;
@@ -22,16 +24,19 @@
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.constraintlayout.widget.ConstraintSet;
+
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.widget.LockPatternView;
 import com.android.settingslib.animation.AppearAnimationCreator;
 import com.android.settingslib.animation.AppearAnimationUtils;
 import com.android.settingslib.animation.DisappearAnimationUtils;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.DevicePostureController.DevicePostureInt;
 
 public class KeyguardPatternView extends KeyguardInputView
         implements AppearAnimationCreator<LockPatternView.CellState> {
@@ -68,7 +73,7 @@
 
     KeyguardMessageArea mSecurityMessageDisplay;
     private View mEcaView;
-    private ViewGroup mContainer;
+    private ConstraintLayout mContainer;
 
     public KeyguardPatternView(Context context) {
         this(context, null);
@@ -90,6 +95,18 @@
                 mContext, android.R.interpolator.fast_out_linear_in));
     }
 
+    void onDevicePostureChanged(@DevicePostureInt int posture) {
+        // Update the guideline based on the device posture...
+        float halfOpenPercentage =
+                mContext.getResources().getFloat(R.dimen.half_opened_bouncer_height_ratio);
+
+        ConstraintSet cs = new ConstraintSet();
+        cs.clone(mContainer);
+        cs.setGuidelinePercent(R.id.pin_pad_top_guideline, posture == DEVICE_POSTURE_HALF_OPENED
+                ? halfOpenPercentage : 0.0f);
+        cs.applyTo(mContainer);
+    }
+
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index d5be7ba..94e07b7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -38,6 +38,7 @@
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingClassifier;
 import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.statusbar.policy.DevicePostureController;
 
 import java.util.List;
 
@@ -56,6 +57,9 @@
     private final FalsingCollector mFalsingCollector;
     private final EmergencyButtonController mEmergencyButtonController;
     private final KeyguardMessageAreaController.Factory mMessageAreaControllerFactory;
+    private final DevicePostureController mPostureController;
+    private final DevicePostureController.Callback mPostureCallback =
+            posture -> mView.onDevicePostureChanged(posture);
 
     private KeyguardMessageAreaController mMessageAreaController;
     private LockPatternView mLockPatternView;
@@ -192,7 +196,8 @@
             LatencyTracker latencyTracker,
             FalsingCollector falsingCollector,
             EmergencyButtonController emergencyButtonController,
-            KeyguardMessageAreaController.Factory messageAreaControllerFactory) {
+            KeyguardMessageAreaController.Factory messageAreaControllerFactory,
+            DevicePostureController postureController) {
         super(view, securityMode, keyguardSecurityCallback, emergencyButtonController);
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mLockPatternUtils = lockPatternUtils;
@@ -203,6 +208,7 @@
         KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView);
         mMessageAreaController = mMessageAreaControllerFactory.create(kma);
         mLockPatternView = mView.findViewById(R.id.lockPatternView);
+        mPostureController = postureController;
     }
 
     @Override
@@ -235,6 +241,7 @@
                 getKeyguardSecurityCallback().onCancelClicked();
             });
         }
+        mPostureController.addCallback(mPostureCallback);
     }
 
     @Override
@@ -247,6 +254,7 @@
         if (cancelBtn != null) {
             cancelBtn.setOnClickListener(null);
         }
+        mPostureController.removeCallback(mPostureCallback);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index 262bed3..9f4585f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -23,10 +23,14 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.statusbar.policy.DevicePostureController;
 
 public class KeyguardPinViewController
         extends KeyguardPinBasedInputViewController<KeyguardPINView> {
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private final DevicePostureController mPostureController;
+    private final DevicePostureController.Callback mPostureCallback = posture ->
+            mView.onDevicePostureChanged(posture);
 
     protected KeyguardPinViewController(KeyguardPINView view,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -35,11 +39,13 @@
             KeyguardMessageAreaController.Factory messageAreaControllerFactory,
             LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
             EmergencyButtonController emergencyButtonController,
-            FalsingCollector falsingCollector) {
+            FalsingCollector falsingCollector,
+            DevicePostureController postureController) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
                 messageAreaControllerFactory, latencyTracker, liftToActivateListener,
                 emergencyButtonController, falsingCollector);
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        mPostureController = postureController;
     }
 
     @Override
@@ -53,6 +59,14 @@
                 getKeyguardSecurityCallback().onCancelClicked();
             });
         }
+
+        mPostureController.addCallback(mPostureCallback);
+    }
+
+    @Override
+    protected void onViewDetached() {
+        super.onViewDetached();
+        mPostureController.removeCallback(mPostureCallback);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 840e8c8..26059068 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -317,7 +317,8 @@
             mRunningOneHandedAnimator = null;
         }
 
-        int targetTranslation = mIsSecurityViewLeftAligned ? 0 : (int) (getMeasuredWidth() / 2f);
+        int targetTranslation = mIsSecurityViewLeftAligned
+                ? 0 : (int) (getMeasuredWidth() - mSecurityViewFlipper.getWidth());
 
         if (animate) {
             mRunningOneHandedAnimator =
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index cb5c6c3..0df2d65 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -162,7 +162,9 @@
                     try {
                         Thread.sleep(5000);
                     } catch (InterruptedException ignored) { }
-                    Runtime.getRuntime().gc();
+                    System.gc();
+                    System.runFinalization();
+                    System.gc();
                 });
             } else {
                 SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 5ef7143..92d1bc4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -103,10 +103,10 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.telephony.TelephonyListenerManager;
@@ -118,15 +118,11 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
-import java.text.SimpleDateFormat;
-import java.util.ArrayDeque;
 import java.util.ArrayList;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Locale;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
@@ -148,8 +144,9 @@
     private static final boolean DEBUG = KeyguardConstants.DEBUG;
     private static final boolean DEBUG_SIM_STATES = KeyguardConstants.DEBUG_SIM_STATES;
     private static final boolean DEBUG_FACE = Build.IS_DEBUGGABLE;
+    private static final boolean DEBUG_FINGERPRINT = Build.IS_DEBUGGABLE;
     private static final boolean DEBUG_SPEW = false;
-    private static final int LOW_BATTERY_THRESHOLD = 20;
+    private static final int FINGERPRINT_LOCKOUT_RESET_DELAY_MS = 600;
 
     private static final String ACTION_FACE_UNLOCK_STARTED
             = "com.android.facelock.FACE_UNLOCK_STARTED";
@@ -347,13 +344,16 @@
     private static final int HAL_ERROR_RETRY_TIMEOUT = 500; // ms
     private static final int HAL_ERROR_RETRY_MAX = 20;
 
-    private final Runnable mCancelNotReceived = new Runnable() {
-        @Override
-        public void run() {
-            Log.w(TAG, "Cancel not received, transitioning to STOPPED");
-            mFingerprintRunningState = mFaceRunningState = BIOMETRIC_STATE_STOPPED;
-            updateBiometricListeningState();
-        }
+    private final Runnable mFpCancelNotReceived = () -> {
+        Log.e(TAG, "Fp cancellation not received, transitioning to STOPPED");
+        mFingerprintRunningState = BIOMETRIC_STATE_STOPPED;
+        updateFingerprintListeningState();
+    };
+
+    private final Runnable mFaceCancelNotReceived = () -> {
+        Log.e(TAG, "Face cancellation not received, transitioning to STOPPED");
+        mFaceRunningState = BIOMETRIC_STATE_STOPPED;
+        updateFaceListeningState();
     };
 
     private final Handler mHandler;
@@ -419,9 +419,8 @@
     @VisibleForTesting
     SparseArray<BiometricAuthenticated> mUserFaceAuthenticated = new SparseArray<>();
 
-    // Keep track of recent calls to shouldListenForFace() for debugging.
-    private static final int FACE_LISTEN_CALLS_QUEUE_SIZE = 20;
-    private ArrayDeque<KeyguardFaceListenModel> mFaceListenModels;
+    // Keep track of recent calls to shouldListenFor*() for debugging.
+    private final KeyguardListenQueue mListenModels = new KeyguardListenQueue();
 
     private static int sCurrentUser;
     private Runnable mUpdateBiometricListeningState = this::updateBiometricListeningState;
@@ -791,19 +790,19 @@
 
     private void handleFingerprintError(int msgId, String errString) {
         Assert.isMainThread();
-        if (msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED && mHandler.hasCallbacks(
-                mCancelNotReceived)) {
-            mHandler.removeCallbacks(mCancelNotReceived);
+        if (mHandler.hasCallbacks(mFpCancelNotReceived)) {
+            mHandler.removeCallbacks(mFpCancelNotReceived);
         }
 
+        // Error is always the end of authentication lifecycle.
+        mFingerprintCancelSignal = null;
+
         if (msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED
                 && mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
             setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
             updateFingerprintListeningState();
         } else {
             setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
-            mFingerprintCancelSignal = null;
-            mFaceCancelSignal = null;
         }
 
         if (msgId == FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE) {
@@ -834,7 +833,18 @@
     private void handleFingerprintLockoutReset() {
         mFingerprintLockedOut = false;
         mFingerprintLockedOutPermanent = false;
-        updateFingerprintListeningState();
+
+        if (isUdfpsEnrolled()) {
+            // TODO(b/194825098): update the reset signal(s)
+            // A successful unlock will trigger a lockout reset, but there is no guarantee
+            // that the events will arrive in a particular order. Add a delay here in case
+            // an unlock is in progress. In this is a normal unlock the extra delay won't
+            // be noticeable.
+            mHandler.postDelayed(this::updateFingerprintListeningState,
+                    FINGERPRINT_LOCKOUT_RESET_DELAY_MS);
+        } else {
+            updateFingerprintListeningState();
+        }
     }
 
     private void setFingerprintRunningState(int fingerprintRunningState) {
@@ -894,6 +904,7 @@
 
     private void handleFaceAuthFailed() {
         Assert.isMainThread();
+        mFaceCancelSignal = null;
         setFaceRunningState(BIOMETRIC_STATE_STOPPED);
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -972,10 +983,13 @@
     private void handleFaceError(int msgId, String errString) {
         Assert.isMainThread();
         if (DEBUG_FACE) Log.d(TAG, "Face error received: " + errString);
-        if (msgId == FaceManager.FACE_ERROR_CANCELED && mHandler.hasCallbacks(mCancelNotReceived)) {
-            mHandler.removeCallbacks(mCancelNotReceived);
+        if (mHandler.hasCallbacks(mFaceCancelNotReceived)) {
+            mHandler.removeCallbacks(mFaceCancelNotReceived);
         }
 
+        // Error is always the end of authentication lifecycle
+        mFaceCancelSignal = null;
+
         if (msgId == FaceManager.FACE_ERROR_CANCELED
                 && mFaceRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
             setFaceRunningState(BIOMETRIC_STATE_STOPPED);
@@ -2211,37 +2225,75 @@
 
     @VisibleForTesting
     protected boolean shouldListenForFingerprint(boolean isUdfps) {
-        final boolean userDoesNotHaveTrust = !getUserHasTrust(getCurrentUser());
+        final int user = getCurrentUser();
+        final boolean userDoesNotHaveTrust = !getUserHasTrust(user);
+        final boolean shouldListenForFingerprintAssistant = shouldListenForFingerprintAssistant();
         final boolean shouldListenKeyguardState =
                 mKeyguardIsVisible
                         || !mDeviceInteractive
                         || (mBouncer && !mKeyguardGoingAway)
                         || mGoingToSleep
-                        || shouldListenForFingerprintAssistant()
+                        || shouldListenForFingerprintAssistant
                         || (mKeyguardOccluded && mIsDreaming)
                         || (mKeyguardOccluded && userDoesNotHaveTrust
                             && (mOccludingAppRequestingFp || isUdfps));
 
         // Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
         // instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
+        final boolean biometricEnabledForUser = mBiometricEnabledForUser.get(user);
+        final boolean userCanSkipBouncer = getUserCanSkipBouncer(user);
+        final boolean fingerprintDisabledForUser = isFingerprintDisabled(user);
         final boolean shouldListenUserState =
                 !mSwitchingUser
-                        && !isFingerprintDisabled(getCurrentUser())
+                        && !fingerprintDisabledForUser
                         && (!mKeyguardGoingAway || !mDeviceInteractive)
                         && mIsPrimaryUser
-                        && mBiometricEnabledForUser.get(getCurrentUser());
+                        && biometricEnabledForUser;
 
         final boolean shouldListenBouncerState =
                 !(mFingerprintLockedOut && mBouncer && mCredentialAttempted);
 
+        final boolean isEncryptedOrLockdownForUser = isEncryptedOrLockdown(user);
+        final boolean userNeedsStrongAuth = userNeedsStrongAuth();
         final boolean shouldListenUdfpsState = !isUdfps
-                || (!getUserCanSkipBouncer(getCurrentUser())
-                    && !isEncryptedOrLockdown(getCurrentUser())
-                    && !userNeedsStrongAuth()
+                || (!userCanSkipBouncer
+                    && !isEncryptedOrLockdownForUser
+                    && !userNeedsStrongAuth
                     && userDoesNotHaveTrust
                     && !mFingerprintLockedOut);
-        return shouldListenKeyguardState && shouldListenUserState && shouldListenBouncerState
-                && shouldListenUdfpsState;
+
+        final boolean shouldListen = shouldListenKeyguardState && shouldListenUserState
+                && shouldListenBouncerState && shouldListenUdfpsState;
+
+        if (DEBUG_FINGERPRINT || DEBUG_SPEW) {
+            maybeLogListenerModelData(
+                    new KeyguardFingerprintListenModel(
+                        System.currentTimeMillis(),
+                        user,
+                        shouldListen,
+                        biometricEnabledForUser,
+                        mBouncer,
+                        userCanSkipBouncer,
+                        mCredentialAttempted,
+                        mDeviceInteractive,
+                        mIsDreaming,
+                        isEncryptedOrLockdownForUser,
+                        fingerprintDisabledForUser,
+                        mFingerprintLockedOut,
+                        mGoingToSleep,
+                        mKeyguardGoingAway,
+                        mKeyguardIsVisible,
+                        mKeyguardOccluded,
+                        mOccludingAppRequestingFp,
+                        mIsPrimaryUser,
+                        shouldListenForFingerprintAssistant,
+                        mSwitchingUser,
+                        isUdfps,
+                        userDoesNotHaveTrust,
+                        userNeedsStrongAuth));
+        }
+
+        return shouldListen;
     }
 
     /**
@@ -2265,12 +2317,12 @@
                 containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT)
                         || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
 
-        boolean canBypass = mKeyguardBypassController != null
+        final boolean canBypass = mKeyguardBypassController != null
                 && mKeyguardBypassController.canBypass();
         // There's no reason to ask the HAL for authentication when the user can dismiss the
         // bouncer, unless we're bypassing and need to auto-dismiss the lock screen even when
         // TrustAgents or biometrics are keeping the device unlocked.
-        boolean becauseCannotSkipBouncer = !getUserCanSkipBouncer(user) || canBypass;
+        final boolean becauseCannotSkipBouncer = !getUserCanSkipBouncer(user) || canBypass;
 
         // Scan even when encrypted or timeout to show a preemptive bouncer when bypassing.
         // Lock-down mode shouldn't scan, since it is more explicit.
@@ -2278,69 +2330,72 @@
 
         // If the device supports face detection (without authentication), allow it to happen
         // if the device is in lockdown mode. Otherwise, prevent scanning.
-        boolean supportsDetectOnly = !mFaceSensorProperties.isEmpty()
+        final boolean supportsDetectOnly = !mFaceSensorProperties.isEmpty()
                 && mFaceSensorProperties.get(0).supportsFaceDetection;
         if (isLockDown && !supportsDetectOnly) {
             strongAuthAllowsScanning = false;
         }
 
         // If the face has recently been authenticated do not attempt to authenticate again.
-        boolean faceAuthenticated = getIsFaceAuthenticated();
+        final boolean faceAuthenticated = getIsFaceAuthenticated();
+        final boolean faceDisabledForUser = isFaceDisabled(user);
+        final boolean biometricEnabledForUser = mBiometricEnabledForUser.get(user);
+        final boolean shouldListenForFaceAssistant = shouldListenForFaceAssistant();
 
         // Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
         // instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
         final boolean shouldListen =
                 (mBouncer || mAuthInterruptActive || mOccludingAppRequestingFace || awakeKeyguard
-                        || shouldListenForFaceAssistant())
-                && !mSwitchingUser && !isFaceDisabled(user) && becauseCannotSkipBouncer
-                && !mKeyguardGoingAway && mBiometricEnabledForUser.get(user) && !mLockIconPressed
+                        || shouldListenForFaceAssistant)
+                && !mSwitchingUser && !faceDisabledForUser && becauseCannotSkipBouncer
+                && !mKeyguardGoingAway && biometricEnabledForUser && !mLockIconPressed
                 && strongAuthAllowsScanning && mIsPrimaryUser
                 && (!mSecureCameraLaunched || mOccludingAppRequestingFace)
                 && !faceAuthenticated;
 
         // Aggregate relevant fields for debug logging.
         if (DEBUG_FACE || DEBUG_SPEW) {
-            final KeyguardFaceListenModel model = new KeyguardFaceListenModel(
-                    System.currentTimeMillis(),
-                    user,
-                    shouldListen,
-                    mBouncer,
-                    mAuthInterruptActive,
-                    mOccludingAppRequestingFace,
-                    awakeKeyguard,
-                    shouldListenForFaceAssistant(),
-                    mSwitchingUser,
-                    isFaceDisabled(user),
-                    becauseCannotSkipBouncer,
-                    mKeyguardGoingAway,
-                    mBiometricEnabledForUser.get(user),
-                    mLockIconPressed,
-                    strongAuthAllowsScanning,
-                    mIsPrimaryUser,
-                    mSecureCameraLaunched,
-                    faceAuthenticated);
-            maybeLogFaceListenerModelData(model);
+            maybeLogListenerModelData(
+                    new KeyguardFaceListenModel(
+                        System.currentTimeMillis(),
+                        user,
+                        shouldListen,
+                        mAuthInterruptActive,
+                        becauseCannotSkipBouncer,
+                        biometricEnabledForUser,
+                        mBouncer,
+                        faceAuthenticated,
+                        faceDisabledForUser,
+                        awakeKeyguard,
+                        mKeyguardGoingAway,
+                        shouldListenForFaceAssistant,
+                        mLockIconPressed,
+                        mOccludingAppRequestingFace,
+                        mIsPrimaryUser,
+                        strongAuthAllowsScanning,
+                        mSecureCameraLaunched,
+                        mSwitchingUser));
         }
 
         return shouldListen;
     }
 
-    private void maybeLogFaceListenerModelData(KeyguardFaceListenModel model) {
+    private void maybeLogListenerModelData(KeyguardListenModel model) {
         // Too chatty, but very useful when debugging issues.
         if (DEBUG_SPEW) {
             Log.v(TAG, model.toString());
         }
 
         // Add model data to the historical buffer.
-        if (DEBUG_FACE && mFaceRunningState != BIOMETRIC_STATE_RUNNING
-                && model.isListeningForFace()) {
-            if (mFaceListenModels == null) {
-                mFaceListenModels = new ArrayDeque<>(FACE_LISTEN_CALLS_QUEUE_SIZE);
-            }
-            if (mFaceListenModels.size() >= FACE_LISTEN_CALLS_QUEUE_SIZE) {
-                mFaceListenModels.remove();
-            }
-            mFaceListenModels.add(model);
+        final boolean notYetRunning =
+                (DEBUG_FACE
+                    && model instanceof KeyguardFaceListenModel
+                    && mFaceRunningState != BIOMETRIC_STATE_RUNNING)
+                || (DEBUG_FINGERPRINT
+                    && model instanceof KeyguardFingerprintListenModel
+                    && mFingerprintRunningState != BIOMETRIC_STATE_RUNNING);
+        if (notYetRunning && model.getListening()) {
+            mListenModels.add(model);
         }
     }
 
@@ -2357,6 +2412,14 @@
     }
 
     private void startListeningForFingerprint() {
+        final int userId = getCurrentUser();
+        final boolean unlockPossible = isUnlockWithFingerprintPossible(userId);
+        if (mFingerprintCancelSignal != null) {
+            Log.e(TAG, "Cancellation signal is not null, high chance of bug in fp auth lifecycle"
+                    + " management. FP state: " + mFingerprintRunningState
+                    + ", unlockPossible: " + unlockPossible);
+        }
+
         if (mFingerprintRunningState == BIOMETRIC_STATE_CANCELLING) {
             setFingerprintRunningState(BIOMETRIC_STATE_CANCELLING_RESTARTING);
             return;
@@ -2366,11 +2429,8 @@
             return;
         }
         if (DEBUG) Log.v(TAG, "startListeningForFingerprint()");
-        int userId = getCurrentUser();
-        if (isUnlockWithFingerprintPossible(userId)) {
-            if (mFingerprintCancelSignal != null) {
-                mFingerprintCancelSignal.cancel();
-            }
+
+        if (unlockPossible) {
             mFingerprintCancelSignal = new CancellationSignal();
 
             if (isEncryptedOrLockdown(userId)) {
@@ -2386,6 +2446,14 @@
     }
 
     private void startListeningForFace() {
+        final int userId = getCurrentUser();
+        final boolean unlockPossible = isUnlockWithFacePossible(userId);
+        if (mFaceCancelSignal != null) {
+            Log.e(TAG, "Cancellation signal is not null, high chance of bug in face auth lifecycle"
+                    + " management. Face state: " + mFaceRunningState
+                    + ", unlockPossible: " + unlockPossible);
+        }
+
         if (mFaceRunningState == BIOMETRIC_STATE_CANCELLING) {
             setFaceRunningState(BIOMETRIC_STATE_CANCELLING_RESTARTING);
             return;
@@ -2394,11 +2462,8 @@
             return;
         }
         if (DEBUG) Log.v(TAG, "startListeningForFace(): " + mFaceRunningState);
-        int userId = getCurrentUser();
-        if (isUnlockWithFacePossible(userId)) {
-            if (mFaceCancelSignal != null) {
-                mFaceCancelSignal.cancel();
-            }
+
+        if (unlockPossible) {
             mFaceCancelSignal = new CancellationSignal();
 
             // This would need to be updated for multi-sensor devices
@@ -2450,9 +2515,8 @@
             if (mFingerprintCancelSignal != null) {
                 mFingerprintCancelSignal.cancel();
                 mFingerprintCancelSignal = null;
-                if (!mHandler.hasCallbacks(mCancelNotReceived)) {
-                    mHandler.postDelayed(mCancelNotReceived, DEFAULT_CANCEL_SIGNAL_TIMEOUT);
-                }
+                mHandler.removeCallbacks(mFpCancelNotReceived);
+                mHandler.postDelayed(mFpCancelNotReceived, DEFAULT_CANCEL_SIGNAL_TIMEOUT);
             }
             setFingerprintRunningState(BIOMETRIC_STATE_CANCELLING);
         }
@@ -2467,9 +2531,8 @@
             if (mFaceCancelSignal != null) {
                 mFaceCancelSignal.cancel();
                 mFaceCancelSignal = null;
-                if (!mHandler.hasCallbacks(mCancelNotReceived)) {
-                    mHandler.postDelayed(mCancelNotReceived, DEFAULT_CANCEL_SIGNAL_TIMEOUT);
-                }
+                mHandler.removeCallbacks(mFaceCancelNotReceived);
+                mHandler.postDelayed(mFaceCancelNotReceived, DEFAULT_CANCEL_SIGNAL_TIMEOUT);
             }
             setFaceRunningState(BIOMETRIC_STATE_CANCELLING);
         }
@@ -2831,6 +2894,11 @@
             mSecureCameraLaunched = false;
         }
 
+        if (mKeyguardBypassController != null) {
+            // LS visibility has changed, so reset deviceEntryIntent
+            mKeyguardBypassController.setUserHasDeviceEntryIntent(false);
+        }
+
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
@@ -3421,15 +3489,8 @@
             pw.println("    enabledByUser=" + mBiometricEnabledForUser.get(userId));
             pw.println("    mSecureCameraLaunched=" + mSecureCameraLaunched);
         }
-        if (mFaceListenModels != null && !mFaceListenModels.isEmpty()) {
-            final SimpleDateFormat dateFormat =
-                    new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
-            pw.println("  Face listen results (last " + FACE_LISTEN_CALLS_QUEUE_SIZE + " calls):");
-            for (final KeyguardFaceListenModel model : mFaceListenModels) {
-                final String time = dateFormat.format(new Date(model.getTimeMillis()));
-                pw.println("    " + time + " " + model.toString());
-            }
-        }
+        mListenModels.print(pw);
+
         if (mIsAutomotive) {
             pw.println("  Running on Automotive build");
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index c1d448d..dd3bb89 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -17,15 +17,21 @@
 package com.android.keyguard;
 
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.graphics.PointF;
 import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.view.View;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
 
+import com.android.settingslib.Utils;
 import com.android.systemui.Dumpable;
+import com.android.systemui.R;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -33,17 +39,52 @@
 /**
  * A view positioned under the notification shade.
  */
-public class LockIconView extends ImageView implements Dumpable {
+public class LockIconView extends FrameLayout implements Dumpable {
     @NonNull private final RectF mSensorRect;
     @NonNull private PointF mLockIconCenter = new PointF(0f, 0f);
     private int mRadius;
 
+    private ImageView mLockIcon;
+    private ImageView mUnlockBgView;
+
+    private int mLockIconColor;
+
     public LockIconView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mSensorRect = new RectF();
     }
 
-    void setCenterLocation(@NonNull PointF center, int radius) {
+    @Override
+    public void onFinishInflate() {
+        super.onFinishInflate();
+        mLockIcon = findViewById(R.id.lock_icon);
+        mUnlockBgView = findViewById(R.id.lock_icon_bg);
+    }
+
+    void updateColorAndBackgroundVisibility(boolean useBackground) {
+        if (useBackground) {
+            mLockIconColor = Utils.getColorAttrDefaultColor(getContext(),
+                    android.R.attr.textColorPrimary);
+            mUnlockBgView.setBackground(getContext().getDrawable(R.drawable.fingerprint_bg));
+            mUnlockBgView.setVisibility(View.VISIBLE);
+        } else {
+            mLockIconColor = Utils.getColorAttrDefaultColor(getContext(),
+                    R.attr.wallpaperTextColorAccent);
+            mUnlockBgView.setVisibility(View.GONE);
+        }
+
+        mLockIcon.setImageTintList(ColorStateList.valueOf(mLockIconColor));
+    }
+
+    void setImageDrawable(Drawable drawable) {
+        mLockIcon.setImageDrawable(drawable);
+    }
+
+    /**
+     * Set the location of the lock icon.
+     */
+    @VisibleForTesting
+    public void setCenterLocation(@NonNull PointF center, int radius) {
         mLockIconCenter = center;
         mRadius = radius;
 
@@ -54,11 +95,12 @@
                 mLockIconCenter.x + mRadius,
                 mLockIconCenter.y + mRadius);
 
-        setX(mSensorRect.left);
-        setY(mSensorRect.top);
-        setLayoutParams(new FrameLayout.LayoutParams(
-                (int) (mSensorRect.right - mSensorRect.left),
-                (int) (mSensorRect.bottom - mSensorRect.top)));
+        final FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
+        lp.width = (int) (mSensorRect.right - mSensorRect.left);
+        lp.height = (int) (mSensorRect.bottom - mSensorRect.top);
+        lp.topMargin = (int) mSensorRect.top;
+        lp.setMarginStart((int) mSensorRect.left);
+        setLayoutParams(lp);
     }
 
     @Override
@@ -70,10 +112,10 @@
         return mLockIconCenter.y - mRadius;
     }
 
-
     @Override
     public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
         pw.println("Center in px (x, y)= (" + mLockIconCenter.x + ", " + mLockIconCenter.y + ")");
         pw.println("Radius in pixels: " + mRadius);
+        pw.println("topLeft= (" + getX() + ", " + getY() + ")");
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 9c8582fa..2a4022c 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -43,10 +43,10 @@
 import androidx.annotation.Nullable;
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
 
-import com.android.settingslib.Utils;
 import com.android.systemui.Dumpable;
 import com.android.systemui.R;
 import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.biometrics.AuthRippleController;
 import com.android.systemui.biometrics.UdfpsController;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
@@ -68,7 +68,8 @@
 /**
  * Controls when to show the LockIcon affordance (lock/unlocked icon or circle) on lock screen.
  *
- * This view will only be shown if the user has UDFPS or FaceAuth enrolled
+ * For devices with UDFPS, the lock icon will show at the sensor location. Else, the lock
+ * icon will show a set distance from the bottom of the device.
  */
 @StatusBarComponent.StatusBarScope
 public class LockIconViewController extends ViewController<LockIconView> implements Dumpable {
@@ -97,9 +98,10 @@
     @NonNull private final AnimatedVectorDrawable mLockToUnlockIcon;
     @NonNull private final Drawable mLockIcon;
     @NonNull private final Drawable mUnlockIcon;
-    @NonNull private final CharSequence mUnlockedLabel;
-    @NonNull private final CharSequence mLockedLabel;
+    @NonNull private CharSequence mUnlockedLabel;
+    @NonNull private CharSequence mLockedLabel;
     @Nullable private final Vibrator mVibrator;
+    @Nullable private final AuthRippleController mAuthRippleController;
 
     private boolean mIsDozing;
     private boolean mIsBouncerShowing;
@@ -111,7 +113,7 @@
     private boolean mUserUnlockedWithBiometric;
     private Runnable mCancelDelayedUpdateVisibilityRunnable;
 
-    private boolean mHasUdfps;
+    private boolean mUdfpsSupported;
     private float mHeightPixels;
     private float mWidthPixels;
     private int mBottomPadding; // in pixels
@@ -136,7 +138,8 @@
             @NonNull AccessibilityManager accessibilityManager,
             @NonNull ConfigurationController configurationController,
             @NonNull @Main DelayableExecutor executor,
-            @Nullable Vibrator vibrator
+            @Nullable Vibrator vibrator,
+            @Nullable AuthRippleController authRippleController
     ) {
         super(view);
         mStatusBarStateController = statusBarStateController;
@@ -149,12 +152,12 @@
         mConfigurationController = configurationController;
         mExecutor = executor;
         mVibrator = vibrator;
+        mAuthRippleController = authRippleController;
 
         final Context context = view.getContext();
         mUnlockIcon = mView.getContext().getResources().getDrawable(
-            R.anim.lock_to_unlock,
+            R.drawable.ic_unlock,
             mView.getContext().getTheme());
-        ((AnimatedVectorDrawable) mUnlockIcon).start();
         mLockIcon = mView.getContext().getResources().getDrawable(
                 R.anim.lock_to_unlock,
                 mView.getContext().getTheme());
@@ -170,15 +173,14 @@
 
     @Override
     protected void onInit() {
+        mAuthController.addCallback(mAuthControllerCallback);
+        mUdfpsSupported = mAuthController.getUdfpsSensorLocation() != null;
+
         mView.setAccessibilityDelegate(mAccessibilityDelegate);
     }
 
     @Override
     protected void onViewAttached() {
-        // we check this here instead of onInit since the FingerprintManager + FaceManager may not
-        // have started up yet onInit
-        mHasUdfps = mAuthController.getUdfpsSensorLocation() != null;
-
         updateConfiguration();
         updateKeyguardShowing();
         mUserUnlockedWithBiometric = false;
@@ -307,12 +309,7 @@
     }
 
     private void updateColors() {
-        final int color = Utils.getColorAttrDefaultColor(mView.getContext(),
-                R.attr.wallpaperTextColorAccent);
-        mFpToUnlockIcon.setTint(color);
-        mLockToUnlockIcon.setTint(color);
-        mLockIcon.setTint(color);
-        mUnlockIcon.setTint(color);
+        mView.updateColorAndBackgroundVisibility(mUdfpsSupported);
     }
 
     private void updateConfiguration() {
@@ -321,11 +318,17 @@
         mHeightPixels = metrics.heightPixels;
         mBottomPadding = mView.getContext().getResources().getDimensionPixelSize(
                 R.dimen.lock_icon_margin_bottom);
+
+        mUnlockedLabel = mView.getContext().getResources().getString(
+                R.string.accessibility_unlock_button);
+        mLockedLabel = mView.getContext()
+                .getResources().getString(R.string.accessibility_lock_icon);
+
         updateLockIconLocation();
     }
 
     private void updateLockIconLocation() {
-        if (mHasUdfps) {
+        if (mUdfpsSupported) {
             FingerprintSensorPropertiesInternal props = mAuthController.getUdfpsProps().get(0);
             mView.setCenterLocation(new PointF(props.sensorLocationX, props.sensorLocationY),
                     props.sensorRadius);
@@ -467,6 +470,7 @@
         @Override
         public void onConfigChanged(Configuration newConfig) {
             updateConfiguration();
+            updateColors();
         }
     };
 
@@ -497,8 +501,10 @@
                     if (!wasClickableOnDownEvent()) {
                         return;
                     }
+                    mDetectedLongPress = true;
 
-                    if (mVibrator != null) {
+                    if (onAffordanceClick() && mVibrator != null) {
+                        // only vibrate if the click went through and wasn't intercepted by falsing
                         mVibrator.vibrate(
                                 Process.myUid(),
                                 getContext().getOpPackageName(),
@@ -506,15 +512,21 @@
                                 "lockIcon-onLongPress",
                                 VIBRATION_SONIFICATION_ATTRIBUTES);
                     }
-                    mDetectedLongPress = true;
-                    onAffordanceClick();
                 }
 
                 public boolean onSingleTapUp(MotionEvent e) {
                     if (!wasClickableOnDownEvent()) {
                         return false;
                     }
+                    onAffordanceClick();
+                    return true;
+                }
 
+                public boolean onFling(MotionEvent e1, MotionEvent e2,
+                        float velocityX, float velocityY) {
+                    if (!wasClickableOnDownEvent()) {
+                        return false;
+                    }
                     onAffordanceClick();
                     return true;
                 }
@@ -523,15 +535,24 @@
                     return mDownDetected;
                 }
 
-                private void onAffordanceClick() {
+                /**
+                 * Whether we tried to launch the affordance.
+                 *
+                 * If falsing intercepts the click, returns false.
+                 */
+                private boolean onAffordanceClick() {
                     if (mFalsingManager.isFalseTouch(LOCK_ICON)) {
-                        return;
+                        return false;
                     }
 
                     // pre-emptively set to true to hide view
                     mIsBouncerShowing = true;
+                    if (mUdfpsSupported && mShowUnlockIcon && mAuthRippleController != null) {
+                        mAuthRippleController.showRipple(FINGERPRINT);
+                    }
                     updateVisibility();
                     mKeyguardViewController.showBouncer(/* scrim */ true);
+                    return true;
                 }
             });
 
@@ -560,7 +581,7 @@
     }
 
     private boolean isClickable() {
-        return mUdfpsEnrolled || mShowUnlockIcon;
+        return mUdfpsSupported || mShowUnlockIcon;
     }
 
     /**
@@ -569,4 +590,12 @@
     public void setAlpha(float alpha) {
         mView.setAlpha(alpha);
     }
+
+    private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() {
+        @Override
+        public void onAllAuthenticatorsRegistered() {
+            mUdfpsSupported = mAuthController.getUdfpsSensorLocation() != null;
+            updateConfiguration();
+        }
+    };
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
index 99f9558..c659bf7 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
@@ -26,7 +26,7 @@
 
 import androidx.annotation.Nullable;
 
-import com.android.settingslib.Utils;
+import com.android.systemui.R;
 
 /**
  * Similar to the {@link NumPadKey}, but displays an image.
@@ -92,8 +92,7 @@
     public void reloadColors() {
         if (mAnimator != null) mAnimator.reloadColors(getContext());
 
-        int textColor = Utils.getColorAttrDefaultColor(getContext(),
-                android.R.attr.colorBackground);
-        ((VectorDrawable) getDrawable()).setTintList(ColorStateList.valueOf(textColor));
+        int imageColor = getContext().getColor(R.color.keyguard_keypad_image_color);
+        ((VectorDrawable) getDrawable()).setTintList(ColorStateList.valueOf(imageColor));
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewComponent.java
index 49a617e..0b0e430 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewComponent.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewComponent.java
@@ -34,7 +34,10 @@
     /** Simple factory for {@link KeyguardStatusBarViewComponent}. */
     @Subcomponent.Factory
     interface Factory {
-        KeyguardStatusBarViewComponent build(@BindsInstance KeyguardStatusBarView view);
+        KeyguardStatusBarViewComponent build(
+                @BindsInstance KeyguardStatusBarView view,
+                @BindsInstance KeyguardStatusBarViewController.ViewStateProvider
+                        viewStateProvider);
     }
 
     /** Builds a {@link KeyguardStatusViewController}. */
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java
index a672523..fc14b6a 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java
@@ -18,6 +18,7 @@
 
 import com.android.keyguard.CarrierText;
 import com.android.systemui.R;
+import com.android.systemui.battery.BatteryMeterView;
 import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
 
 import dagger.Module;
@@ -31,4 +32,11 @@
     static CarrierText getCarrierText(KeyguardStatusBarView view) {
         return view.findViewById(R.id.keyguard_carrier_text);
     }
+
+    /** */
+    @Provides
+    @KeyguardStatusBarViewScope
+    static BatteryMeterView getBatteryMeterView(KeyguardStatusBarView view) {
+        return view.findViewById(R.id.battery);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
index 06fbe84..cc166c2 100644
--- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
@@ -92,9 +92,11 @@
 
     @Override
     public void startActivity(Intent intent, boolean dismissShade,
-            @Nullable ActivityLaunchAnimator.Controller animationController) {
+            @Nullable ActivityLaunchAnimator.Controller animationController,
+            boolean showOverLockscreenWhenLocked) {
         mActualStarterOptionalLazy.get().ifPresent(
-                starter -> starter.startActivity(intent, dismissShade, animationController));
+                starter -> starter.startActivity(intent, dismissShade, animationController,
+                    showOverLockscreenWhenLocked));
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index f8c57bf..7adf8b5 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -47,6 +47,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
@@ -65,6 +66,7 @@
 import com.android.systemui.power.PowerUI;
 import com.android.systemui.privacy.PrivacyItemController;
 import com.android.systemui.qs.ReduceBrightColorsController;
+import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.screenrecord.RecordingController;
 import com.android.systemui.shared.plugins.PluginManager;
@@ -72,7 +74,6 @@
 import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
 import com.android.systemui.shared.system.PackageManagerWrapper;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationMediaManager;
@@ -114,7 +115,6 @@
 import com.android.systemui.statusbar.policy.HotspotController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.NextAlarmController;
 import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
 import com.android.systemui.statusbar.policy.RotationLockController;
@@ -244,7 +244,6 @@
     @Inject Lazy<BluetoothController> mBluetoothController;
     @Inject Lazy<LocationController> mLocationController;
     @Inject Lazy<RotationLockController> mRotationLockController;
-    @Inject Lazy<NetworkController> mNetworkController;
     @Inject Lazy<ZenModeController> mZenModeController;
     @Inject Lazy<HotspotController> mHotspotController;
     @Inject Lazy<CastController> mCastController;
@@ -360,6 +359,7 @@
     @Inject Lazy<UiEventLogger> mUiEventLogger;
     @Inject Lazy<FeatureFlags> mFeatureFlagsLazy;
     @Inject Lazy<StatusBarContentInsetsProvider> mContentInsetsProviderLazy;
+    @Inject Lazy<InternetDialogFactory> mInternetDialogFactory;
 
     @Inject
     public Dependency() {
@@ -389,8 +389,6 @@
 
         mProviders.put(RotationLockController.class, mRotationLockController::get);
 
-        mProviders.put(NetworkController.class, mNetworkController::get);
-
         mProviders.put(ZenModeController.class, mZenModeController::get);
 
         mProviders.put(HotspotController.class, mHotspotController::get);
@@ -572,6 +570,7 @@
         mProviders.put(PrivacyDotViewController.class, mPrivacyDotViewControllerLazy::get);
         mProviders.put(EdgeBackGestureHandler.Factory.class,
                 mEdgeBackGestureHandlerFactoryLazy::get);
+        mProviders.put(InternetDialogFactory.class, mInternetDialogFactory::get);
         mProviders.put(UiEventLogger.class, mUiEventLogger::get);
         mProviders.put(FeatureFlags.class, mFeatureFlagsLazy::get);
         mProviders.put(StatusBarContentInsetsProvider.class, mContentInsetsProviderLazy::get);
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index affad7a..3555e8d 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -25,6 +25,8 @@
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.PendingIntent;
 import android.content.res.Resources;
 import android.graphics.RectF;
 import android.os.Handler;
@@ -73,6 +75,9 @@
     private final FlingAnimationUtils mFlingAnimationUtils;
     private float mPagingTouchSlop;
     private final float mSlopMultiplier;
+    private int mTouchSlop;
+    private float mTouchSlopMultiplier;
+
     private final Callback mCallback;
     private final int mSwipeDirection;
     private final VelocityTracker mVelocityTracker;
@@ -105,6 +110,10 @@
                     final int y = (int) mDownLocation[1] - mViewOffset[1];
                     mTouchedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
                     ((ExpandableNotificationRow) mTouchedView).doLongClickCallback(x, y);
+
+                    if (isAvailableToDragAndDrop(mTouchedView)) {
+                        mCallback.onLongPressSent(mTouchedView);
+                    }
                 }
             }
         }
@@ -126,6 +135,8 @@
         mVelocityTracker = VelocityTracker.obtain();
         mPagingTouchSlop = viewConfiguration.getScaledPagingTouchSlop();
         mSlopMultiplier = viewConfiguration.getScaledAmbiguousGestureMultiplier();
+        mTouchSlop = viewConfiguration.getScaledTouchSlop();
+        mTouchSlopMultiplier = viewConfiguration.getAmbiguousGestureMultiplier();
 
         // Extra long-press!
         mLongPressTimeout = (long) (ViewConfiguration.getLongPressTimeout() * 1.5f);
@@ -297,7 +308,9 @@
                 mIsSwiping = false;
                 mSnappingChild = false;
                 mLongPressSent = false;
+                mCallback.onLongPressSent(null);
                 mVelocityTracker.clear();
+                cancelLongPress();
                 mTouchedView = mCallback.getChildAtPosition(ev);
 
                 if (mTouchedView != null) {
@@ -349,6 +362,7 @@
                 mIsSwiping = false;
                 mTouchedView = null;
                 mLongPressSent = false;
+                mCallback.onLongPressSent(null);
                 mMenuRowIntercepting = false;
                 cancelLongPress();
                 if (captured) return true;
@@ -438,6 +452,12 @@
             private boolean mCancelled;
 
             @Override
+            public void onAnimationStart(Animator animation) {
+                super.onAnimationStart(animation);
+                mCallback.onBeginDrag(animView);
+            }
+
+            @Override
             public void onAnimationCancel(Animator animation) {
                 mCancelled = true;
             }
@@ -593,11 +613,7 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
-        if (mLongPressSent && !mMenuRowIntercepting) {
-            return true;
-        }
-
-        if (!mIsSwiping && !mMenuRowIntercepting) {
+        if (!mIsSwiping && !mMenuRowIntercepting && !mLongPressSent) {
             if (mCallback.getChildAtPosition(ev) != null) {
                 // We are dragging directly over a card, make sure that we also catch the gesture
                 // even if nobody else wants the touch event.
@@ -623,30 +639,40 @@
                     if (absDelta >= getFalsingThreshold()) {
                         mTouchAboveFalsingThreshold = true;
                     }
-                    // don't let items that can't be dismissed be dragged more than
-                    // maxScrollDistance
-                    if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissedInDirection(
-                            mTouchedView,
-                            delta > 0)) {
-                        float size = getSize(mTouchedView);
-                        float maxScrollDistance = MAX_SCROLL_SIZE_FRACTION * size;
-                        if (absDelta >= size) {
-                            delta = delta > 0 ? maxScrollDistance : -maxScrollDistance;
-                        } else {
-                            int startPosition = mCallback.getConstrainSwipeStartPosition();
-                            if (absDelta > startPosition) {
-                                int signedStartPosition =
-                                        (int) (startPosition * Math.signum(delta));
-                                delta = signedStartPosition
-                                        + maxScrollDistance * (float) Math.sin(
-                                        ((delta - signedStartPosition) / size) * (Math.PI / 2));
+
+                    if (mLongPressSent) {
+                        if (absDelta >= getTouchSlop(ev)) {
+                            if (mTouchedView instanceof ExpandableNotificationRow) {
+                                ((ExpandableNotificationRow) mTouchedView)
+                                        .doDragCallback(ev.getX(), ev.getY());
                             }
                         }
-                    }
+                    } else {
+                        // don't let items that can't be dismissed be dragged more than
+                        // maxScrollDistance
+                        if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissedInDirection(
+                                mTouchedView,
+                                delta > 0)) {
+                            float size = getSize(mTouchedView);
+                            float maxScrollDistance = MAX_SCROLL_SIZE_FRACTION * size;
+                            if (absDelta >= size) {
+                                delta = delta > 0 ? maxScrollDistance : -maxScrollDistance;
+                            } else {
+                                int startPosition = mCallback.getConstrainSwipeStartPosition();
+                                if (absDelta > startPosition) {
+                                    int signedStartPosition =
+                                            (int) (startPosition * Math.signum(delta));
+                                    delta = signedStartPosition
+                                            + maxScrollDistance * (float) Math.sin(
+                                            ((delta - signedStartPosition) / size) * (Math.PI / 2));
+                                }
+                            }
+                        }
 
-                    setTranslation(mTouchedView, mTranslation + delta);
-                    updateSwipeProgressFromOffset(mTouchedView, mCanCurrViewBeDimissed);
-                    onMoveUpdate(mTouchedView, ev, mTranslation + delta, delta);
+                        setTranslation(mTouchedView, mTranslation + delta);
+                        updateSwipeProgressFromOffset(mTouchedView, mCanCurrViewBeDimissed);
+                        onMoveUpdate(mTouchedView, ev, mTranslation + delta, delta);
+                    }
                 }
                 break;
             case MotionEvent.ACTION_UP:
@@ -747,6 +773,29 @@
         mIsSwiping = false;
     }
 
+    private float getTouchSlop(MotionEvent event) {
+        // Adjust the touch slop if another gesture may be being performed.
+        return event.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE
+                ? mTouchSlop * mTouchSlopMultiplier
+                : mTouchSlop;
+    }
+
+    private boolean isAvailableToDragAndDrop(View v) {
+        if (v.getResources().getBoolean(R.bool.config_notificationToContents)) {
+            if (v instanceof ExpandableNotificationRow) {
+                ExpandableNotificationRow enr = (ExpandableNotificationRow) v;
+                boolean canBubble = enr.getEntry().canBubble();
+                Notification notif = enr.getEntry().getSbn().getNotification();
+                PendingIntent dragIntent = notif.contentIntent != null ? notif.contentIntent
+                        : notif.fullScreenIntent;
+                if (dragIntent != null && dragIntent.isActivity() && !canBubble) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     public interface Callback {
         View getChildAtPosition(MotionEvent ev);
 
@@ -771,6 +820,13 @@
         void onDragCancelled(View v);
 
         /**
+         * Called when the child is long pressed and available to start drag and drop.
+         *
+         * @param v the view that was long pressed.
+         */
+        void onLongPressSent(View v);
+
+        /**
          * Called when the child is snapped to a position.
          *
          * @param animView the view that was snapped.
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbController.java
index 81a13a2..4082015 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistOrbController.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistOrbController.java
@@ -57,7 +57,9 @@
         public void run() {
             mView.removeCallbacks(this);
             mView.show(false /* show */, true /* animate */, () -> {
-                mWindowManager.removeView(mView);
+                if (mView.isAttachedToWindow()) {
+                    mWindowManager.removeView(mView);
+                }
             });
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt b/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
index c9e6771..5616a00 100644
--- a/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
@@ -40,10 +40,10 @@
  * After restoring is done, a [ACTION_RESTORE_FINISHED] intent will be send to SystemUI user 0,
  * indicating that restoring is finished for a given user.
  */
-class BackupHelper : BackupAgentHelper() {
+open class BackupHelper : BackupAgentHelper() {
 
     companion object {
-        private const val TAG = "BackupHelper"
+        const val TAG = "BackupHelper"
         internal const val CONTROLS = ControlsFavoritePersistenceWrapper.FILE_NAME
         private const val NO_OVERWRITE_FILES_BACKUP_KEY = "systemui.files_no_overwrite"
         private const val PEOPLE_TILES_BACKUP_KEY = "systemui.people.shared_preferences"
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
similarity index 68%
rename from packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
rename to packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
index 6fc2e41..66085ac0 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 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.
@@ -13,64 +13,50 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.systemui;
+package com.android.systemui.battery;
 
 import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;
 
 import static com.android.systemui.DejankUtils.whitelistIpcs;
-import static com.android.systemui.util.SysuiLifecycle.viewAttachLifecycle;
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 import android.animation.LayoutTransition;
 import android.animation.ObjectAnimator;
 import android.annotation.IntDef;
-import android.app.ActivityManager;
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.database.ContentObserver;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Handler;
+import android.os.UserHandle;
 import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.util.TypedValue;
 import android.view.Gravity;
 import android.view.LayoutInflater;
-import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import androidx.annotation.StyleRes;
+import androidx.annotation.VisibleForTesting;
 
 import com.android.settingslib.graph.ThemedBatteryDrawable;
+import com.android.systemui.DualToneHandler;
+import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
-import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.settings.CurrentUserTracker;
-import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.policy.BatteryController;
-import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
-import com.android.systemui.tuner.TunerService;
-import com.android.systemui.tuner.TunerService.Tunable;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.text.NumberFormat;
 
-public class BatteryMeterView extends LinearLayout implements
-        BatteryStateChangeCallback, Tunable, DarkReceiver, ConfigurationListener {
-
+public class BatteryMeterView extends LinearLayout implements DarkReceiver {
 
     @Retention(SOURCE)
     @IntDef({MODE_DEFAULT, MODE_ON, MODE_OFF, MODE_ESTIMATE})
@@ -81,21 +67,14 @@
     public static final int MODE_ESTIMATE = 3;
 
     private final ThemedBatteryDrawable mDrawable;
-    private final String mSlotBattery;
     private final ImageView mBatteryIconView;
-    private final CurrentUserTracker mUserTracker;
     private TextView mBatteryPercentView;
 
-    private BatteryController mBatteryController;
-    private SettingObserver mSettingObserver;
     private final @StyleRes int mPercentageStyleId;
     private int mTextColor;
     private int mLevel;
     private int mShowPercentMode = MODE_DEFAULT;
     private boolean mShowPercentAvailable;
-    // Some places may need to show the battery conditionally, and not obey the tuner
-    private boolean mIgnoreTunerUpdates;
-    private boolean mIsSubscribedForTunerUpdates;
     private boolean mCharging;
     // Error state where we know nothing about the current battery state
     private boolean mBatteryStateUnknown;
@@ -103,19 +82,19 @@
     private Drawable mUnknownStateDrawable;
 
     private DualToneHandler mDualToneHandler;
-    private int mUser;
 
     private int mNonAdaptedSingleToneColor;
     private int mNonAdaptedForegroundColor;
     private int mNonAdaptedBackgroundColor;
 
+    private BatteryEstimateFetcher mBatteryEstimateFetcher;
+
     public BatteryMeterView(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
     public BatteryMeterView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
-        BroadcastDispatcher broadcastDispatcher = Dependency.get(BroadcastDispatcher.class);
 
         setOrientation(LinearLayout.HORIZONTAL);
         setGravity(Gravity.CENTER_VERTICAL | Gravity.START);
@@ -128,14 +107,11 @@
         mDrawable = new ThemedBatteryDrawable(context, frameColor);
         atts.recycle();
 
-        mSettingObserver = new SettingObserver(new Handler(context.getMainLooper()));
         mShowPercentAvailable = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_battery_percentage_setting_available);
 
         setupLayoutTransition();
 
-        mSlotBattery = context.getString(
-                com.android.internal.R.string.status_bar_battery);
         mBatteryIconView = new ImageView(context);
         mBatteryIconView.setImageDrawable(mDrawable);
         final MarginLayoutParams mlp = new MarginLayoutParams(
@@ -150,21 +126,8 @@
         // Init to not dark at all.
         onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
 
-        mUserTracker = new CurrentUserTracker(broadcastDispatcher) {
-            @Override
-            public void onUserSwitched(int newUserId) {
-                mUser = newUserId;
-                getContext().getContentResolver().unregisterContentObserver(mSettingObserver);
-                getContext().getContentResolver().registerContentObserver(
-                        Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver,
-                        newUserId);
-                updateShowPercent();
-            }
-        };
-
         setClipChildren(false);
         setClipToPadding(false);
-        Dependency.get(ConfigurationController.class).observe(viewAttachLifecycle(this), this);
     }
 
     private void setupLayoutTransition() {
@@ -196,48 +159,11 @@
      * @param mode desired mode (none, on, off)
      */
     public void setPercentShowMode(@BatteryPercentMode int mode) {
+        if (mode == mShowPercentMode) return;
         mShowPercentMode = mode;
         updateShowPercent();
     }
 
-    /**
-     * Set {@code true} to turn off BatteryMeterView's subscribing to the tuner for updates, and
-     * thus avoid it controlling its own visibility
-     *
-     * @param ignore whether to ignore the tuner or not
-     */
-    public void setIgnoreTunerUpdates(boolean ignore) {
-        mIgnoreTunerUpdates = ignore;
-        updateTunerSubscription();
-    }
-
-    private void updateTunerSubscription() {
-        if (mIgnoreTunerUpdates) {
-            unsubscribeFromTunerUpdates();
-        } else {
-            subscribeForTunerUpdates();
-        }
-    }
-
-    private void subscribeForTunerUpdates() {
-        if (mIsSubscribedForTunerUpdates || mIgnoreTunerUpdates) {
-            return;
-        }
-
-        Dependency.get(TunerService.class)
-                .addTunable(this, StatusBarIconController.ICON_HIDE_LIST);
-        mIsSubscribedForTunerUpdates = true;
-    }
-
-    private void unsubscribeFromTunerUpdates() {
-        if (!mIsSubscribedForTunerUpdates) {
-            return;
-        }
-
-        Dependency.get(TunerService.class).removeTunable(this);
-        mIsSubscribedForTunerUpdates = false;
-    }
-
     public void setColorsFromContext(Context context) {
         if (context == null) {
             return;
@@ -251,42 +177,7 @@
         return false;
     }
 
-    @Override
-    public void onTuningChanged(String key, String newValue) {
-        if (StatusBarIconController.ICON_HIDE_LIST.equals(key)) {
-            ArraySet<String> icons = StatusBarIconController.getIconHideList(
-                    getContext(), newValue);
-            setVisibility(icons.contains(mSlotBattery) ? View.GONE : View.VISIBLE);
-        }
-    }
-
-    @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mBatteryController = Dependency.get(BatteryController.class);
-        mBatteryController.addCallback(this);
-        mUser = ActivityManager.getCurrentUser();
-        getContext().getContentResolver().registerContentObserver(
-                Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver, mUser);
-        getContext().getContentResolver().registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME),
-                false, mSettingObserver);
-        updateShowPercent();
-        subscribeForTunerUpdates();
-        mUserTracker.startTracking();
-    }
-
-    @Override
-    public void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        mUserTracker.stopTracking();
-        mBatteryController.removeCallback(this);
-        getContext().getContentResolver().unregisterContentObserver(mSettingObserver);
-        unsubscribeFromTunerUpdates();
-    }
-
-    @Override
-    public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+    void onBatteryLevelChanged(int level, boolean pluggedIn) {
         mDrawable.setCharging(pluggedIn);
         mDrawable.setBatteryLevel(level);
         mCharging = pluggedIn;
@@ -294,8 +185,7 @@
         updatePercentText();
     }
 
-    @Override
-    public void onPowerSaveChanged(boolean isPowerSave) {
+    void onPowerSaveChanged(boolean isPowerSave) {
         mDrawable.setPowerSaveEnabled(isPowerSave);
     }
 
@@ -315,23 +205,32 @@
         updateShowPercent();
     }
 
-    private void updatePercentText() {
+    /**
+     * Sets the fetcher that should be used to get the estimated time remaining for the user's
+     * battery.
+     */
+    void setBatteryEstimateFetcher(BatteryEstimateFetcher fetcher) {
+        mBatteryEstimateFetcher = fetcher;
+    }
+
+    void updatePercentText() {
         if (mBatteryStateUnknown) {
             setContentDescription(getContext().getString(R.string.accessibility_battery_unknown));
             return;
         }
 
-        if (mBatteryController == null) {
+        if (mBatteryEstimateFetcher == null) {
             return;
         }
 
         if (mBatteryPercentView != null) {
             if (mShowPercentMode == MODE_ESTIMATE && !mCharging) {
-                mBatteryController.getEstimatedTimeRemainingString((String estimate) -> {
+                mBatteryEstimateFetcher.fetchBatteryTimeRemainingEstimate(
+                        (String estimate) -> {
                     if (mBatteryPercentView == null) {
                         return;
                     }
-                    if (estimate != null) {
+                    if (estimate != null && mShowPercentMode == MODE_ESTIMATE) {
                         mBatteryPercentView.setText(estimate);
                         setContentDescription(getContext().getString(
                                 R.string.accessibility_battery_level_with_estimate,
@@ -361,12 +260,12 @@
                         : R.string.accessibility_battery_level, mLevel));
     }
 
-    private void updateShowPercent() {
+    void updateShowPercent() {
         final boolean showing = mBatteryPercentView != null;
         // TODO(b/140051051)
         final boolean systemSetting = 0 != whitelistIpcs(() -> Settings.System
                 .getIntForUser(getContext().getContentResolver(),
-                SHOW_BATTERY_PERCENT, 0, mUser));
+                SHOW_BATTERY_PERCENT, 0, UserHandle.USER_CURRENT));
         boolean shouldShow =
                 (mShowPercentAvailable && systemSetting && mShowPercentMode != MODE_OFF)
                 || mShowPercentMode == MODE_ON
@@ -394,11 +293,6 @@
         }
     }
 
-    @Override
-    public void onDensityOrFontScaleChanged() {
-        scaleBatteryMeterViews();
-    }
-
     private Drawable getUnknownStateDrawable() {
         if (mUnknownStateDrawable == null) {
             mUnknownStateDrawable = mContext.getDrawable(R.drawable.ic_battery_unknown);
@@ -408,8 +302,7 @@
         return mUnknownStateDrawable;
     }
 
-    @Override
-    public void onBatteryUnknownStateChanged(boolean isUnknown) {
+    void onBatteryUnknownStateChanged(boolean isUnknown) {
         if (mBatteryStateUnknown == isUnknown) {
             return;
         }
@@ -428,7 +321,7 @@
     /**
      * Looks up the scale factor for status bar icons and scales the battery view by that amount.
      */
-    private void scaleBatteryMeterViews() {
+    void scaleBatteryMeterViews() {
         Resources res = getContext().getResources();
         TypedValue typedValue = new TypedValue();
 
@@ -486,22 +379,18 @@
         pw.println("    mTextColor: #" + Integer.toHexString(mTextColor));
         pw.println("    mBatteryStateUnknown: " + mBatteryStateUnknown);
         pw.println("    mLevel: " + mLevel);
+        pw.println("    mMode: " + mShowPercentMode);
     }
 
-    private final class SettingObserver extends ContentObserver {
-        public SettingObserver(Handler handler) {
-            super(handler);
-        }
+    @VisibleForTesting
+    CharSequence getBatteryPercentViewText() {
+        return mBatteryPercentView.getText();
+    }
 
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            super.onChange(selfChange, uri);
-            updateShowPercent();
-            if (TextUtils.equals(uri.getLastPathSegment(),
-                    Settings.Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME)) {
-                // update the text for sure if the estimate in the cache was updated
-                updatePercentText();
-            }
-        }
+    /** An interface that will fetch the estimated time remaining for the user's battery. */
+    public interface BatteryEstimateFetcher {
+        void fetchBatteryTimeRemainingEstimate(
+                BatteryController.EstimateFetchCompletion completion);
     }
 }
+
diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java
new file mode 100644
index 0000000..ae9a323
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java
@@ -0,0 +1,203 @@
+/*
+ * 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.battery;
+
+import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;
+
+import android.app.ActivityManager;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.view.View;
+
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.settings.CurrentUserTracker;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.ViewController;
+
+import javax.inject.Inject;
+
+/** Controller for {@link BatteryMeterView}. **/
+public class BatteryMeterViewController extends ViewController<BatteryMeterView> {
+    private final ConfigurationController mConfigurationController;
+    private final TunerService mTunerService;
+    private final ContentResolver mContentResolver;
+    private final BatteryController mBatteryController;
+
+    private final String mSlotBattery;
+    private final SettingObserver mSettingObserver;
+    private final CurrentUserTracker mCurrentUserTracker;
+
+    private final ConfigurationController.ConfigurationListener mConfigurationListener =
+            new ConfigurationController.ConfigurationListener() {
+                @Override
+                public void onDensityOrFontScaleChanged() {
+                    mView.scaleBatteryMeterViews();
+                }
+            };
+
+    private final TunerService.Tunable mTunable = new TunerService.Tunable() {
+        @Override
+        public void onTuningChanged(String key, String newValue) {
+            if (StatusBarIconController.ICON_HIDE_LIST.equals(key)) {
+                ArraySet<String> icons = StatusBarIconController.getIconHideList(
+                        getContext(), newValue);
+                mView.setVisibility(icons.contains(mSlotBattery) ? View.GONE : View.VISIBLE);
+            }
+        }
+    };
+
+    private final BatteryController.BatteryStateChangeCallback mBatteryStateChangeCallback =
+            new BatteryController.BatteryStateChangeCallback() {
+                @Override
+                public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+                    mView.onBatteryLevelChanged(level, pluggedIn);
+                }
+
+                @Override
+                public void onPowerSaveChanged(boolean isPowerSave) {
+                    mView.onPowerSaveChanged(isPowerSave);
+                }
+
+                @Override
+                public void onBatteryUnknownStateChanged(boolean isUnknown) {
+                    mView.onBatteryUnknownStateChanged(isUnknown);
+                }
+            };
+
+    // Some places may need to show the battery conditionally, and not obey the tuner
+    private boolean mIgnoreTunerUpdates;
+    private boolean mIsSubscribedForTunerUpdates;
+
+    @Inject
+    public BatteryMeterViewController(
+            BatteryMeterView view,
+            ConfigurationController configurationController,
+            TunerService tunerService,
+            BroadcastDispatcher broadcastDispatcher,
+            @Main Handler mainHandler,
+            ContentResolver contentResolver,
+            BatteryController batteryController) {
+        super(view);
+        mConfigurationController = configurationController;
+        mTunerService = tunerService;
+        mContentResolver = contentResolver;
+        mBatteryController = batteryController;
+
+        mView.setBatteryEstimateFetcher(mBatteryController::getEstimatedTimeRemainingString);
+
+        mSlotBattery = getResources().getString(com.android.internal.R.string.status_bar_battery);
+        mSettingObserver = new SettingObserver(mainHandler);
+        mCurrentUserTracker = new CurrentUserTracker(broadcastDispatcher) {
+            @Override
+            public void onUserSwitched(int newUserId) {
+                contentResolver.unregisterContentObserver(mSettingObserver);
+                registerShowBatteryPercentObserver(newUserId);
+                mView.updateShowPercent();
+            }
+        };
+    }
+
+    @Override
+    protected void onViewAttached() {
+        mConfigurationController.addCallback(mConfigurationListener);
+        subscribeForTunerUpdates();
+        mBatteryController.addCallback(mBatteryStateChangeCallback);
+
+        registerShowBatteryPercentObserver(ActivityManager.getCurrentUser());
+        registerGlobalBatteryUpdateObserver();
+        mCurrentUserTracker.startTracking();
+
+        mView.updateShowPercent();
+    }
+
+    @Override
+    protected void onViewDetached() {
+        mConfigurationController.removeCallback(mConfigurationListener);
+        unsubscribeFromTunerUpdates();
+        mBatteryController.removeCallback(mBatteryStateChangeCallback);
+
+        mCurrentUserTracker.stopTracking();
+        mContentResolver.unregisterContentObserver(mSettingObserver);
+    }
+
+    /**
+     * Turn off {@link BatteryMeterView}'s subscribing to the tuner for updates, and thus avoid it
+     * controlling its own visibility.
+     */
+    public void ignoreTunerUpdates() {
+        mIgnoreTunerUpdates = true;
+        unsubscribeFromTunerUpdates();
+    }
+
+    private void subscribeForTunerUpdates() {
+        if (mIsSubscribedForTunerUpdates || mIgnoreTunerUpdates) {
+            return;
+        }
+
+        mTunerService.addTunable(mTunable, StatusBarIconController.ICON_HIDE_LIST);
+        mIsSubscribedForTunerUpdates = true;
+    }
+
+    private void unsubscribeFromTunerUpdates() {
+        if (!mIsSubscribedForTunerUpdates) {
+            return;
+        }
+
+        mTunerService.removeTunable(mTunable);
+        mIsSubscribedForTunerUpdates = false;
+    }
+
+    private void registerShowBatteryPercentObserver(int user) {
+        mContentResolver.registerContentObserver(
+                Settings.System.getUriFor(SHOW_BATTERY_PERCENT),
+                false,
+                mSettingObserver,
+                user);
+    }
+
+    private void registerGlobalBatteryUpdateObserver() {
+        mContentResolver.registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME),
+                false,
+                mSettingObserver);
+    }
+
+    private final class SettingObserver extends ContentObserver {
+        public SettingObserver(Handler handler) {
+            super(handler);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            super.onChange(selfChange, uri);
+            mView.updateShowPercent();
+            if (TextUtils.equals(uri.getLastPathSegment(),
+                    Settings.Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME)) {
+                // update the text for sure if the estimate in the cache was updated
+                mView.updatePercentText();
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintView.java
index 205054d..ae3e94b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintView.java
@@ -44,11 +44,17 @@
     private static final String TAG = "BiometricPrompt/AuthBiometricFaceToFingerprintView";
 
     protected static class UdfpsIconController extends IconController {
+        @BiometricState private int mIconState = STATE_IDLE;
+
         protected UdfpsIconController(
                 @NonNull Context context, @NonNull ImageView iconView, @NonNull TextView textView) {
             super(context, iconView, textView);
         }
 
+        void updateState(@BiometricState int newState) {
+            updateState(mIconState, newState);
+        }
+
         @Override
         protected void updateState(int lastState, int newState) {
             final boolean lastStateIsErrorIcon =
@@ -86,6 +92,7 @@
             }
 
             mState = newState;
+            mIconState = newState;
         }
     }
 
@@ -93,6 +100,8 @@
     @Nullable private ModalityListener mModalityListener;
     @Nullable private FingerprintSensorPropertiesInternal mFingerprintSensorProps;
     @Nullable private UdfpsDialogMeasureAdapter mUdfpsMeasureAdapter;
+    @Nullable @VisibleForTesting UdfpsIconController mUdfpsIconController;
+
 
     public AuthBiometricFaceToFingerprintView(Context context) {
         super(context);
@@ -107,6 +116,12 @@
         super(context, attrs, injector);
     }
 
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mUdfpsIconController = new UdfpsIconController(mContext, mIconView, mIndicatorView);
+    }
+
     @Modality
     int getActiveSensorType() {
         return mActiveSensorType;
@@ -168,35 +183,26 @@
     }
 
     @Override
-    @NonNull
-    protected IconController getIconController() {
-        if (mActiveSensorType == TYPE_FINGERPRINT) {
-            if (!(mIconController instanceof UdfpsIconController)) {
-                mIconController = createUdfpsIconController();
-            }
-            return mIconController;
-        }
-        return super.getIconController();
-    }
-
-    @NonNull
-    protected IconController createUdfpsIconController() {
-        return new UdfpsIconController(getContext(), mIconView, mIndicatorView);
-    }
-
-    @Override
     public void updateState(@BiometricState int newState) {
-        if (mState == STATE_HELP || mState == STATE_ERROR) {
-            @Modality final int currentType = mActiveSensorType;
-            mActiveSensorType = TYPE_FINGERPRINT;
+        if (mActiveSensorType == TYPE_FACE) {
+            if (newState == STATE_HELP || newState == STATE_ERROR) {
+                mActiveSensorType = TYPE_FINGERPRINT;
 
-            setRequireConfirmation(false);
-            mConfirmButton.setEnabled(false);
-            mConfirmButton.setVisibility(View.GONE);
+                setRequireConfirmation(false);
+                mConfirmButton.setEnabled(false);
+                mConfirmButton.setVisibility(View.GONE);
 
-            if (mModalityListener != null && currentType != mActiveSensorType) {
-                mModalityListener.onModalitySwitched(currentType, mActiveSensorType);
+                if (mModalityListener != null) {
+                    mModalityListener.onModalitySwitched(TYPE_FACE, mActiveSensorType);
+                }
+
+                // Deactivate the face icon controller so it stops drawing to the view
+                mFaceIconController.deactivate();
+                // Then, activate this icon controller. We need to start in the "idle" state
+                mUdfpsIconController.updateState(STATE_IDLE);
             }
+        } else { // Fingerprint
+            mUdfpsIconController.updateState(newState);
         }
 
         super.updateState(newState);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
index e8da7c5..c32c1a2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
@@ -49,6 +49,7 @@
         protected Handler mHandler;
         protected boolean mLastPulseLightToDark; // false = dark to light, true = light to dark
         protected @BiometricState int mState;
+        protected boolean mDeactivated;
 
         protected IconController(Context context, ImageView iconView, TextView textView) {
             mContext = context;
@@ -67,6 +68,11 @@
         }
 
         protected void animateIcon(int iconRes, boolean repeat) {
+            Log.d(TAG, "animateIcon, state: " + mState + ", deactivated: " + mDeactivated);
+            if (mDeactivated) {
+                return;
+            }
+
             final AnimatedVectorDrawable icon =
                     (AnimatedVectorDrawable) mContext.getDrawable(iconRes);
             mIconView.setImageDrawable(icon);
@@ -92,12 +98,26 @@
         @Override
         public void onAnimationEnd(Drawable drawable) {
             super.onAnimationEnd(drawable);
+            Log.d(TAG, "onAnimationEnd, mState: " + mState + ", deactivated: " + mDeactivated);
+            if (mDeactivated) {
+                return;
+            }
+
             if (mState == STATE_AUTHENTICATING || mState == STATE_HELP) {
                 pulseInNextDirection();
             }
         }
 
+        protected void deactivate() {
+            mDeactivated = true;
+        }
+
         protected void updateState(int lastState, int newState) {
+            if (mDeactivated) {
+                Log.w(TAG, "Ignoring updateState when deactivated: " + newState);
+                return;
+            }
+
             final boolean lastStateIsErrorIcon =
                     lastState == STATE_ERROR || lastState == STATE_HELP;
 
@@ -142,7 +162,7 @@
         }
     }
 
-    protected IconController mIconController;
+    @Nullable @VisibleForTesting IconController mFaceIconController;
 
     public AuthBiometricFaceView(Context context) {
         this(context, null);
@@ -158,6 +178,12 @@
     }
 
     @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mFaceIconController = new IconController(mContext, mIconView, mIndicatorView);
+    }
+
+    @Override
     protected int getDelayAfterAuthenticatedDurationMs() {
         return HIDE_DELAY_MS;
     }
@@ -187,17 +213,9 @@
         return true;
     }
 
-    @NonNull
-    protected IconController getIconController() {
-        if (mIconController == null) {
-            mIconController = new IconController(mContext, mIconView, mIndicatorView);
-        }
-        return mIconController;
-    }
-
     @Override
     public void updateState(@BiometricState int newState) {
-        getIconController().updateState(mState, newState);
+        mFaceIconController.updateState(mState, newState);
 
         if (newState == STATE_AUTHENTICATING_ANIMATING_IN ||
                 (newState == STATE_AUTHENTICATING && getSize() == AuthDialog.SIZE_MEDIUM)) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 0ce1846..ab5f2b828 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -786,7 +786,11 @@
                 .build(sensorIds, credentialAllowed, mFpProps, mFaceProps);
     }
 
-    interface Callback {
+    /**
+     * AuthController callback used to receive signal for when biometric authenticators are
+     * registered.
+     */
+    public interface Callback {
         /**
          * Called when authenticators are registered. If authenticators are already
          * registered before this call, this callback will never be triggered.
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 1df8ad5..45ca708 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -24,6 +24,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.settingslib.Utils
+import com.android.systemui.R
 import com.android.systemui.statusbar.CircleReveal
 import com.android.systemui.statusbar.LightRevealEffect
 import com.android.systemui.statusbar.NotificationShadeWindowController
@@ -59,6 +60,11 @@
     private var faceSensorLocation: PointF? = null
     private var circleReveal: LightRevealEffect? = null
 
+    override fun onInit() {
+        mView.setAlphaInDuration(sysuiContext.resources.getInteger(
+                R.integer.auth_ripple_alpha_in_duration).toLong())
+    }
+
     @VisibleForTesting
     public override fun onViewAttached() {
         updateRippleColor()
@@ -79,7 +85,7 @@
         notificationShadeWindowController.setForcePluginOpen(false, this)
     }
 
-    private fun showRipple(biometricSourceType: BiometricSourceType?) {
+    fun showRipple(biometricSourceType: BiometricSourceType?) {
         if (!keyguardUpdateMonitor.isKeyguardVisible ||
             keyguardUpdateMonitor.userNeedsStrongAuth()) {
             return
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
index 95ea8100..95d0afa 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
@@ -38,6 +38,7 @@
  * launcher.
  */
 class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
+    private var alphaInDuration: Long = 0
     private var rippleInProgress: Boolean = false
     private val rippleShader = RippleShader()
     private val ripplePaint = Paint()
@@ -66,47 +67,37 @@
             .toFloat()
     }
 
+    fun setAlphaInDuration(duration: Long) {
+        alphaInDuration = duration
+    }
+
     fun startRipple(onAnimationEnd: Runnable?, lightReveal: LightRevealScrim?) {
         if (rippleInProgress) {
             return // Ignore if ripple effect is already playing
         }
 
         val rippleAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
-            interpolator = PathInterpolator(0.4f, 0f, 0f, 1f)
+            interpolator = PathInterpolator(0f, 0f, .2f, 1f)
             duration = RIPPLE_ANIMATION_DURATION
             addUpdateListener { animator ->
                 val now = animator.currentPlayTime
                 rippleShader.progress = animator.animatedValue as Float
                 rippleShader.time = now.toFloat()
 
-                lightReveal?.revealAmount = animator.animatedValue as Float
                 invalidate()
             }
         }
 
-        val revealAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
+        val revealAnimator = ValueAnimator.ofFloat(.1f, 1f).apply {
             interpolator = rippleAnimator.interpolator
-            startDelay = 10
             duration = rippleAnimator.duration
             addUpdateListener { animator ->
                 lightReveal?.revealAmount = animator.animatedValue as Float
             }
         }
 
-        val alphaInAnimator = ValueAnimator.ofInt(0, 127).apply {
-            duration = 167
-            addUpdateListener { animator ->
-                rippleShader.color = ColorUtils.setAlphaComponent(
-                    rippleShader.color,
-                    animator.animatedValue as Int
-                )
-                invalidate()
-            }
-        }
-
-        val alphaOutAnimator = ValueAnimator.ofInt(127, 0).apply {
-            startDelay = 417
-            duration = 1116
+        val alphaInAnimator = ValueAnimator.ofInt(0, 255).apply {
+            duration = alphaInDuration
             addUpdateListener { animator ->
                 rippleShader.color = ColorUtils.setAlphaComponent(
                     rippleShader.color,
@@ -120,8 +111,7 @@
             playTogether(
                 rippleAnimator,
                 revealAnimator,
-                alphaInAnimator,
-                alphaOutAnimator
+                alphaInAnimator
             )
             addListener(object : AnimatorListenerAdapter() {
                 override fun onAnimationStart(animation: Animator?) {
@@ -148,7 +138,7 @@
         // the active effect area. Values here should be kept in sync with the
         // animation implementation in the ripple shader.
         val maskRadius = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
-            (1 - rippleShader.progress)) * radius * 1.5f
+            (1 - rippleShader.progress)) * radius * 2f
         canvas?.drawCircle(origin.x, origin.y, maskRadius, ripplePaint)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 2309d5f..e13ae4c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -43,7 +43,6 @@
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.SystemClock;
 import android.os.Trace;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
@@ -72,9 +71,11 @@
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.concurrency.Execution;
+import com.android.systemui.util.time.SystemClock;
 
 import java.util.Optional;
 
@@ -123,6 +124,8 @@
     @NonNull private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
     @Nullable private final UdfpsHbmProvider mHbmProvider;
     @NonNull private final KeyguardBypassController mKeyguardBypassController;
+    @NonNull private final ConfigurationController mConfigurationController;
+    @NonNull private final SystemClock mSystemClock;
     @VisibleForTesting @NonNull final BiometricOrientationEventListener mOrientationListener;
     // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
     // sensors, this, in addition to a lot of the code here, will be updated.
@@ -160,7 +163,8 @@
     public static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES =
             new AudioAttributes.Builder()
                     .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
-                    .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+                    // vibration will bypass battery saver mode:
+                    .setUsage(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY)
                     .build();
 
     public static final VibrationEffect EFFECT_CLICK =
@@ -447,19 +451,19 @@
                         final String touchInfo = String.format(
                                 "minor: %.1f, major: %.1f, v: %.1f, exceedsVelocityThreshold: %b",
                                 minor, major, v, exceedsVelocityThreshold);
-                        final long sinceLastLog = SystemClock.elapsedRealtime() - mTouchLogTime;
+                        final long sinceLastLog = mSystemClock.elapsedRealtime() - mTouchLogTime;
                         if (!isIlluminationRequested && !mGoodCaptureReceived &&
                                 !exceedsVelocityThreshold) {
                             onFingerDown((int) event.getRawX(), (int) event.getRawY(), minor,
                                     major);
                             Log.v(TAG, "onTouch | finger down: " + touchInfo);
-                            mTouchLogTime = SystemClock.elapsedRealtime();
-                            mPowerManager.userActivity(SystemClock.uptimeMillis(),
+                            mTouchLogTime = mSystemClock.elapsedRealtime();
+                            mPowerManager.userActivity(mSystemClock.uptimeMillis(),
                                     PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
                             handled = true;
                         } else if (sinceLastLog >= MIN_TOUCH_LOG_INTERVAL) {
                             Log.v(TAG, "onTouch | finger move: " + touchInfo);
-                            mTouchLogTime = SystemClock.elapsedRealtime();
+                            mTouchLogTime = mSystemClock.elapsedRealtime();
                         }
                     } else {
                         Log.v(TAG, "onTouch | finger outside");
@@ -522,7 +526,9 @@
             @NonNull KeyguardStateController keyguardStateController,
             @NonNull KeyguardBypassController keyguardBypassController,
             @NonNull DisplayManager displayManager,
-            @Main Handler mainHandler) {
+            @Main Handler mainHandler,
+            @NonNull ConfigurationController configurationController,
+            @NonNull SystemClock systemClock) {
         mContext = context;
         mExecution = execution;
         // TODO (b/185124905): inject main handler and vibrator once done prototyping
@@ -557,6 +563,8 @@
                 displayManager,
                 mainHandler);
         mKeyguardBypassController = keyguardBypassController;
+        mConfigurationController = configurationController;
+        mSystemClock = systemClock;
 
         mSensorProps = findFirstUdfps();
         // At least one UDFPS sensor exists
@@ -699,15 +707,21 @@
         return mCoreLayoutParams;
     }
 
+
     private void onOrientationChanged() {
         // When the configuration changes it's almost always necessary to destroy and re-create
         // the overlay's window to pass it the new LayoutParams.
         // Hiding the overlay will destroy its window. It's safe to hide the overlay regardless
         // of whether it is already hidden.
+        final boolean wasShowingAltAuth = mKeyguardViewManager.isShowingAlternateAuth();
         hideUdfpsOverlay();
+
         // If the overlay needs to be shown, this will re-create and show the overlay with the
         // updated LayoutParams. Otherwise, the overlay will remain hidden.
         updateOverlay();
+        if (wasShowingAltAuth) {
+            mKeyguardViewManager.showGenericBouncer(true);
+        }
     }
 
     private void showUdfpsOverlay(@NonNull ServerRequest request) {
@@ -776,6 +790,9 @@
                         mDumpManager,
                         mKeyguardViewMediator,
                         mLockscreenShadeTransitionController,
+                        mConfigurationController,
+                        mSystemClock,
+                        mKeyguardStateController,
                         this
                 );
             case IUdfpsOverlayController.REASON_AUTH_BP:
@@ -811,10 +828,14 @@
             Log.v(TAG, "hideUdfpsOverlay | removing window");
             // Reset the controller back to its starting state.
             onFingerUp();
+            boolean wasShowingAltAuth = mKeyguardViewManager.isShowingAlternateAuth();
             mWindowManager.removeView(mView);
             mView.setOnTouchListener(null);
             mView.setOnHoverListener(null);
             mView.setAnimationViewController(null);
+            if (wasShowingAltAuth) {
+                mKeyguardViewManager.resetAlternateAuth(true);
+            }
             mAccessibilityManager.removeTouchExplorationStateChangeListener(
                     mTouchExplorationStateChangeListener);
             mView = null;
@@ -883,11 +904,16 @@
 
     private void onFingerDown(int x, int y, float minor, float major) {
         mExecution.assertIsMainThread();
-        mKeyguardBypassController.setUserHasDeviceEntryIntent(true);
         if (mView == null) {
             Log.w(TAG, "Null view in onFingerDown");
             return;
         }
+
+        if (mView.getAnimationViewController() instanceof UdfpsKeyguardViewController
+                && !mStatusBarStateController.isDozing()) {
+            mKeyguardBypassController.setUserHasDeviceEntryIntent(true);
+        }
+
         if (!mOnFingerDown) {
             playStartHaptic();
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
index eb02aa0..d46426a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
@@ -22,9 +22,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
-import android.animation.ArgbEvaluator;
 import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
@@ -51,17 +49,14 @@
     private UdfpsDrawable mFingerprintDrawable; // placeholder
     private LottieAnimationView mAodFp;
     private LottieAnimationView mLockScreenFp;
-    private int mUdfpsBouncerColor;
-    private int mWallpaperTextColor;
     private int mStatusBarState;
 
     // used when highlighting fp icon:
     private int mTextColorPrimary;
     private ImageView mBgProtection;
     boolean mUdfpsRequested;
-    int mUdfpsRequestedColor;
 
-    private AnimatorSet mAnimatorSet;
+    private AnimatorSet mBackgroundInAnimator = new AnimatorSet();
     private int mAlpha; // 0-255
 
     // AOD anti-burn-in offsets
@@ -72,8 +67,6 @@
     private float mBurnInProgress;
     private float mInterpolatedDarkAmount;
 
-    private ValueAnimator mHintAnimator;
-
     public UdfpsKeyguardView(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
         mFingerprintDrawable = new UdfpsFpDrawable(context);
@@ -89,23 +82,15 @@
         super.onFinishInflate();
         mAodFp = findViewById(R.id.udfps_aod_fp);
         mLockScreenFp = findViewById(R.id.udfps_lockscreen_fp);
-
         mBgProtection = findViewById(R.id.udfps_keyguard_fp_bg);
 
-        mWallpaperTextColor = Utils.getColorAttrDefaultColor(mContext,
-                R.attr.wallpaperTextColorAccent);
-        mTextColorPrimary = Utils.getColorAttrDefaultColor(mContext,
-                android.R.attr.textColorPrimary);
+        updateColor();
 
-        // requires call to invalidate to update the color (see #updateColor)
+        // requires call to invalidate to update the color
         mLockScreenFp.addValueCallback(
                 new KeyPath("**"), LottieProperty.COLOR_FILTER,
-                frameInfo -> new PorterDuffColorFilter(getColor(), PorterDuff.Mode.SRC_ATOP)
+                frameInfo -> new PorterDuffColorFilter(mTextColorPrimary, PorterDuff.Mode.SRC_ATOP)
         );
-        mUdfpsRequested = false;
-
-        mHintAnimator = ObjectAnimator.ofFloat(mLockScreenFp, "progress", 1f, 0f, 1f);
-        mHintAnimator.setDuration(4000);
     }
 
     @Override
@@ -148,13 +133,7 @@
     }
 
     void requestUdfps(boolean request, int color) {
-        if (request) {
-            mUdfpsRequestedColor = color;
-        } else {
-            mUdfpsRequestedColor = -1;
-        }
         mUdfpsRequested = request;
-        updateColor();
     }
 
     void setStatusBarState(int statusBarState) {
@@ -162,31 +141,10 @@
     }
 
     void updateColor() {
-        mWallpaperTextColor = Utils.getColorAttrDefaultColor(mContext,
-            R.attr.wallpaperTextColorAccent);
         mTextColorPrimary = Utils.getColorAttrDefaultColor(mContext,
             android.R.attr.textColorPrimary);
-        mLockScreenFp.invalidate();
-        mBgProtection.setBackground(getContext().getDrawable(R.drawable.fingerprint_bg));
-    }
-
-    private boolean showingUdfpsBouncer() {
-        return mBgProtection.getVisibility() == View.VISIBLE;
-    }
-
-
-    private int getColor() {
-        if (isUdfpsColorRequested()) {
-            return mUdfpsRequestedColor;
-        } else if (showingUdfpsBouncer()) {
-            return mUdfpsBouncerColor;
-        } else {
-            return mWallpaperTextColor;
-        }
-    }
-
-    private boolean isUdfpsColorRequested() {
-        return mUdfpsRequested && mUdfpsRequestedColor != -1;
+        mBgProtection.setImageDrawable(getContext().getDrawable(R.drawable.fingerprint_bg));
+        mLockScreenFp.invalidate(); // updated with a valueCallback
     }
 
     /**
@@ -200,7 +158,13 @@
     @Override
     protected int updateAlpha() {
         int alpha = super.updateAlpha();
-        mLockScreenFp.setImageAlpha(alpha);
+        mLockScreenFp.setAlpha(alpha / 255f);
+        if (mInterpolatedDarkAmount != 0f) {
+            mBgProtection.setAlpha(1f - mInterpolatedDarkAmount);
+        } else {
+            mBgProtection.setAlpha(alpha / 255f);
+        }
+
         return alpha;
     }
 
@@ -213,67 +177,29 @@
     }
 
     void onDozeAmountChanged(float linear, float eased) {
-        mHintAnimator.cancel();
         mInterpolatedDarkAmount = eased;
+        updateAlpha();
         updateBurnInOffsets();
     }
 
-    void animateHint() {
-        if (!isShadeLocked() && !mUdfpsRequested && mAlpha == 255
-                && mLockScreenFp.isVisibleToUser()) {
-            mHintAnimator.start();
-        }
-    }
-
     /**
      * Animates in the bg protection circle behind the fp icon to highlight the icon.
      */
-    void animateUdfpsBouncer(Runnable onEndAnimation) {
-        if (showingUdfpsBouncer() && mBgProtection.getAlpha() == 1f) {
-            // already fully highlighted, don't re-animate
+    void animateInUdfpsBouncer(Runnable onEndAnimation) {
+        if (mBackgroundInAnimator.isRunning()) {
+            // already animating in
             return;
         }
 
-        if (mAnimatorSet != null) {
-            mAnimatorSet.cancel();
-        }
-
-        mAnimatorSet = new AnimatorSet();
-        mAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-        mAnimatorSet.setDuration(500);
-        mAnimatorSet.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                mBgProtection.setVisibility(View.VISIBLE);
-            }
-        });
-
-        ValueAnimator fpIconColorAnim;
-        if (isShadeLocked()) {
-            // set color and fade in since we weren't showing before
-            mUdfpsBouncerColor = mTextColorPrimary;
-            fpIconColorAnim = ValueAnimator.ofInt(0, 255);
-            fpIconColorAnim.addUpdateListener(valueAnimator ->
-                    mLockScreenFp.setImageAlpha((int) valueAnimator.getAnimatedValue()));
-        } else {
-            // update icon color
-            fpIconColorAnim = new ValueAnimator();
-            fpIconColorAnim.setIntValues(
-                    isUdfpsColorRequested() ? mUdfpsRequestedColor : mWallpaperTextColor,
-                    mTextColorPrimary);
-            fpIconColorAnim.setEvaluator(ArgbEvaluator.getInstance());
-            fpIconColorAnim.addUpdateListener(valueAnimator -> {
-                mUdfpsBouncerColor = (int) valueAnimator.getAnimatedValue();
-                updateColor();
-            });
-        }
-
-        mAnimatorSet.playTogether(
+        // fade in and scale up
+        mBackgroundInAnimator = new AnimatorSet();
+        mBackgroundInAnimator.playTogether(
                 ObjectAnimator.ofFloat(mBgProtection, View.ALPHA, 0f, 1f),
                 ObjectAnimator.ofFloat(mBgProtection, View.SCALE_X, 0f, 1f),
-                ObjectAnimator.ofFloat(mBgProtection, View.SCALE_Y, 0f, 1f),
-                fpIconColorAnim);
-        mAnimatorSet.addListener(new AnimatorListenerAdapter() {
+                ObjectAnimator.ofFloat(mBgProtection, View.SCALE_Y, 0f, 1f));
+        mBackgroundInAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        mBackgroundInAnimator.setDuration(500);
+        mBackgroundInAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
                 if (onEndAnimation != null) {
@@ -281,66 +207,7 @@
                 }
             }
         });
-        mAnimatorSet.start();
-    }
-
-    /**
-     * Animates out the bg protection circle behind the fp icon to unhighlight the icon.
-     */
-    void animateAwayUdfpsBouncer(@Nullable Runnable onEndAnimation) {
-        if (!showingUdfpsBouncer()) {
-            // already hidden
-            return;
-        }
-
-        if (mAnimatorSet != null) {
-            mAnimatorSet.cancel();
-        }
-
-        ValueAnimator fpIconColorAnim;
-        if (isShadeLocked()) {
-            // fade out
-            mUdfpsBouncerColor = mTextColorPrimary;
-            fpIconColorAnim = ValueAnimator.ofInt(255, 0);
-            fpIconColorAnim.addUpdateListener(valueAnimator ->
-                    mLockScreenFp.setImageAlpha((int) valueAnimator.getAnimatedValue()));
-        } else {
-            // update icon color
-            fpIconColorAnim = new ValueAnimator();
-            fpIconColorAnim.setIntValues(
-                    mTextColorPrimary,
-                    isUdfpsColorRequested() ? mUdfpsRequestedColor : mWallpaperTextColor);
-            fpIconColorAnim.setEvaluator(ArgbEvaluator.getInstance());
-            fpIconColorAnim.addUpdateListener(valueAnimator -> {
-                mUdfpsBouncerColor = (int) valueAnimator.getAnimatedValue();
-                updateColor();
-            });
-        }
-
-        mAnimatorSet = new AnimatorSet();
-        mAnimatorSet.playTogether(
-                ObjectAnimator.ofFloat(mBgProtection, View.ALPHA, 1f, 0f),
-                ObjectAnimator.ofFloat(mBgProtection, View.SCALE_X, 1f, 0f),
-                ObjectAnimator.ofFloat(mBgProtection, View.SCALE_Y, 1f, 0f),
-                fpIconColorAnim);
-        mAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-        mAnimatorSet.setDuration(500);
-
-        mAnimatorSet.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mBgProtection.setVisibility(View.GONE);
-                if (onEndAnimation != null) {
-                    onEndAnimation.run();
-                }
-            }
-        });
-
-        mAnimatorSet.start();
-    }
-
-    boolean isAnimating() {
-        return mAnimatorSet != null && mAnimatorSet.isRunning();
+        mBackgroundInAnimator.start();
     }
 
     private boolean isShadeLocked() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 9c95720..4d9675c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -19,14 +19,11 @@
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
 
 import android.annotation.NonNull;
-import android.hardware.biometrics.BiometricSourceType;
+import android.content.res.Configuration;
 import android.util.MathUtils;
 import android.view.MotionEvent;
 
-import androidx.annotation.Nullable;
-
 import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.R;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -36,7 +33,10 @@
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.time.SystemClock;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -45,30 +45,28 @@
 
 /**
  * Class that coordinates non-HBM animations during keyguard authentication.
- *
- * Highlights the udfps icon when:
- * - Face authentication has failed
- * - Face authentication has been run for > 2 seconds
  */
 public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<UdfpsKeyguardView> {
-    private static final long AFTER_FACE_AUTH_HINT_DELAY = 2000;
-
     @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager;
     @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @NonNull private final DelayableExecutor mExecutor;
     @NonNull private final KeyguardViewMediator mKeyguardViewMediator;
     @NonNull private final LockscreenShadeTransitionController mLockScreenShadeTransitionController;
+    @NonNull private final ConfigurationController mConfigurationController;
+    @NonNull private final SystemClock mSystemClock;
+    @NonNull private final KeyguardStateController mKeyguardStateController;
     @NonNull private final UdfpsController mUdfpsController;
 
-    @Nullable private Runnable mCancelDelayedHintRunnable;
     private boolean mShowingUdfpsBouncer;
     private boolean mUdfpsRequested;
     private boolean mQsExpanded;
     private boolean mFaceDetectRunning;
-    private boolean mHintShown;
     private int mStatusBarState;
     private float mTransitionToFullShadeProgress;
     private float mLastDozeAmount;
+    private long mLastUdfpsBouncerShowTime = -1;
+    private float mStatusBarExpansion;
+    private boolean mLaunchTransitionFadingAway;
 
     /**
      * hidden amount of pin/pattern/password bouncer
@@ -88,6 +86,9 @@
             @NonNull DumpManager dumpManager,
             @NonNull KeyguardViewMediator keyguardViewMediator,
             @NonNull LockscreenShadeTransitionController transitionController,
+            @NonNull ConfigurationController configurationController,
+            @NonNull SystemClock systemClock,
+            @NonNull KeyguardStateController keyguardStateController,
             @NonNull UdfpsController udfpsController) {
         super(view, statusBarStateController, statusBarOptional, dumpManager);
         mKeyguardViewManager = statusBarKeyguardViewManager;
@@ -95,6 +96,9 @@
         mExecutor = mainDelayableExecutor;
         mKeyguardViewMediator = keyguardViewMediator;
         mLockScreenShadeTransitionController = transitionController;
+        mConfigurationController = configurationController;
+        mSystemClock = systemClock;
+        mKeyguardStateController = keyguardStateController;
         mUdfpsController = udfpsController;
     }
 
@@ -104,12 +108,14 @@
     }
 
     @Override
+    public void onInit() {
+        super.onInit();
+        mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
+    }
+
+    @Override
     protected void onViewAttached() {
         super.onViewAttached();
-        mHintShown = false;
-        mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
-        updateFaceDetectRunning(mKeyguardUpdateMonitor.isFaceDetectionRunning());
-
         final float dozeAmount = mStatusBarStateController.getDozeAmount();
         mLastDozeAmount = dozeAmount;
         mStateListener.onDozeAmountChanged(dozeAmount, dozeAmount);
@@ -117,10 +123,16 @@
 
         mUdfpsRequested = false;
 
+        mLaunchTransitionFadingAway = mKeyguardStateController.isLaunchTransitionFadingAway();
+        mKeyguardStateController.addCallback(mKeyguardStateControllerCallback);
         mStatusBarState = mStatusBarStateController.getState();
         mQsExpanded = mKeyguardViewManager.isQsExpanded();
         mInputBouncerHiddenAmount = KeyguardBouncer.EXPANSION_HIDDEN;
         mIsBouncerVisible = mKeyguardViewManager.bouncerIsOrWillBeShowing();
+        mConfigurationController.addCallback(mConfigurationListener);
+        mStatusBarOptional.ifPresent(
+                statusBar -> statusBar.addExpansionChangedListener(
+                        mStatusBarExpansionChangedListener));
         updateAlpha();
         updatePauseAuth();
 
@@ -131,20 +143,19 @@
     @Override
     protected void onViewDetached() {
         super.onViewDetached();
-        mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
         mFaceDetectRunning = false;
 
+        mKeyguardStateController.removeCallback(mKeyguardStateControllerCallback);
         mStatusBarStateController.removeCallback(mStateListener);
         mKeyguardViewManager.removeAlternateAuthInterceptor(mAlternateAuthInterceptor);
         mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false);
+        mConfigurationController.removeCallback(mConfigurationListener);
+        mStatusBarOptional.ifPresent(
+                statusBar -> statusBar.removeExpansionChangedListener(
+                        mStatusBarExpansionChangedListener));
         if (mLockScreenShadeTransitionController.getUdfpsKeyguardViewController() == this) {
             mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(null);
         }
-
-        if (mCancelDelayedHintRunnable != null) {
-            mCancelDelayedHintRunnable.run();
-            mCancelDelayedHintRunnable = null;
-        }
     }
 
     @Override
@@ -156,10 +167,11 @@
         pw.println("mQsExpanded=" + mQsExpanded);
         pw.println("mIsBouncerVisible=" + mIsBouncerVisible);
         pw.println("mInputBouncerHiddenAmount=" + mInputBouncerHiddenAmount);
+        pw.println("mStatusBarExpansion=" + mStatusBarExpansion);
         pw.println("mAlpha=" + mView.getAlpha());
         pw.println("mUdfpsRequested=" + mUdfpsRequested);
         pw.println("mView.mUdfpsRequested=" + mView.mUdfpsRequested);
-        pw.println("mView.mUdfpsRequestedColor=" + mView.mUdfpsRequestedColor);
+        pw.println("mLaunchTransitionFadingAway=" + mLaunchTransitionFadingAway);
     }
 
     /**
@@ -172,14 +184,22 @@
         }
 
         mShowingUdfpsBouncer = show;
+        if (mShowingUdfpsBouncer) {
+            mLastUdfpsBouncerShowTime = mSystemClock.uptimeMillis();
+        }
         updatePauseAuth();
         if (mShowingUdfpsBouncer) {
-            mView.animateUdfpsBouncer(() ->
-                    mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true));
+            if (mStatusBarState == StatusBarState.SHADE_LOCKED) {
+                mView.animateInUdfpsBouncer(null);
+            }
+
+            if (mKeyguardViewManager.isOccluded()) {
+                mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true);
+            }
+
             mView.announceForAccessibility(mView.getContext().getString(
                     R.string.accessibility_fingerprint_bouncer));
         } else {
-            mView.animateAwayUdfpsBouncer(null);
             mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false);
         }
         return true;
@@ -201,6 +221,10 @@
             return false;
         }
 
+        if (mLaunchTransitionFadingAway) {
+            return true;
+        }
+
         if (mStatusBarState != KEYGUARD) {
             return true;
         }
@@ -230,43 +254,22 @@
      * If we were previously showing the udfps bouncer, hide it and instead show the regular
      * (pin/pattern/password) bouncer.
      *
-     * Does nothing if we weren't previously showing the udfps bouncer.
+     * Does nothing if we weren't previously showing the UDFPS bouncer.
      */
     private void maybeShowInputBouncer() {
-        if (mShowingUdfpsBouncer) {
+        if (mShowingUdfpsBouncer && hasUdfpsBouncerShownWithMinTime()) {
             mKeyguardViewManager.showBouncer(true);
             mKeyguardViewManager.resetAlternateAuth(false);
         }
     }
 
-    private void cancelDelayedHint() {
-        if (mCancelDelayedHintRunnable != null) {
-            mCancelDelayedHintRunnable.run();
-            mCancelDelayedHintRunnable = null;
-        }
-    }
-
-    private void updateFaceDetectRunning(boolean running) {
-        if (mFaceDetectRunning == running) {
-            return;
-        }
-
-        // show udfps hint a few seconds after face auth started running
-        if (!mFaceDetectRunning && running && !mHintShown && mCancelDelayedHintRunnable == null) {
-            // Face detect started running, show udfps hint after a delay
-            mCancelDelayedHintRunnable = mExecutor.executeDelayed(() -> showHint(false),
-                    AFTER_FACE_AUTH_HINT_DELAY);
-        }
-
-        mFaceDetectRunning = running;
-    }
-
-    private void showHint(boolean forceShow) {
-        cancelDelayedHint();
-        if (!mHintShown || forceShow) {
-            mHintShown = true;
-            mView.animateHint();
-        }
+    /**
+     * Whether the udfps bouncer has shown for at least 200ms before allowing touches outside
+     * of the udfps icon area to dismiss the udfps bouncer and show the pin/pattern/password
+     * bouncer.
+     */
+    private boolean hasUdfpsBouncerShownWithMinTime() {
+        return (mSystemClock.uptimeMillis() - mLastUdfpsBouncerShowTime) > 200;
     }
 
     /**
@@ -279,10 +282,13 @@
     }
 
     private void updateAlpha() {
-        // fade icon on transition to showing bouncer
+        // fade icon on transitions to showing the status bar, but if mUdfpsRequested, then
+        // the keyguard is occluded by some application - so instead use the input bouncer
+        // hidden amount to determine the fade
+        float expansion = mUdfpsRequested ? mInputBouncerHiddenAmount : mStatusBarExpansion;
         int alpha = mShowingUdfpsBouncer ? 255
                 : (int) MathUtils.constrain(
-                    MathUtils.map(.5f, .9f, 0f, 255f, mInputBouncerHiddenAmount),
+                    MathUtils.map(.5f, .9f, 0f, 255f, expansion),
                     0f, 255f);
         alpha *= (1.0f - mTransitionToFullShadeProgress);
         mView.setUnpausedAlpha(alpha);
@@ -308,39 +314,6 @@
         }
     };
 
-    private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
-            new KeyguardUpdateMonitorCallback() {
-                public void onBiometricRunningStateChanged(boolean running,
-                        BiometricSourceType biometricSourceType) {
-                    if (biometricSourceType == BiometricSourceType.FACE) {
-                        updateFaceDetectRunning(running);
-                    }
-                }
-
-                public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) {
-                    if (biometricSourceType == BiometricSourceType.FACE) {
-                        // show udfps hint when face auth fails
-                        showHint(true);
-                    }
-                }
-
-                public void onBiometricError(int msgId, String errString,
-                        BiometricSourceType biometricSourceType) {
-                    if (biometricSourceType == BiometricSourceType.FACE) {
-                        // show udfps hint when face auth fails
-                        showHint(true);
-                    }
-                }
-
-                public void onBiometricAuthenticated(int userId,
-                        BiometricSourceType biometricSourceType, boolean isStrongBiometric) {
-                    if (biometricSourceType == BiometricSourceType.FACE) {
-                        // cancel delayed hint if face auth succeeded
-                        cancelDelayedHint();
-                    }
-                }
-            };
-
     private final StatusBarKeyguardViewManager.AlternateAuthInterceptor mAlternateAuthInterceptor =
             new StatusBarKeyguardViewManager.AlternateAuthInterceptor() {
                 @Override
@@ -367,7 +340,7 @@
 
                 @Override
                 public boolean isAnimating() {
-                    return mView.isAnimating();
+                    return false;
                 }
 
                 @Override
@@ -408,4 +381,46 @@
                     pw.println(getTag());
                 }
             };
+
+    private final ConfigurationController.ConfigurationListener mConfigurationListener =
+            new ConfigurationController.ConfigurationListener() {
+                @Override
+                public void onUiModeChanged() {
+                    mView.updateColor();
+                }
+
+                @Override
+                public void onThemeChanged() {
+                    mView.updateColor();
+                }
+
+                @Override
+                public void onOverlayChanged() {
+                    mView.updateColor();
+                }
+
+                @Override
+                public void onConfigChanged(Configuration newConfig) {
+                    mView.updateColor();
+                }
+            };
+
+    private final StatusBar.ExpansionChangedListener mStatusBarExpansionChangedListener =
+            new StatusBar.ExpansionChangedListener() {
+                @Override
+                public void onExpansionChanged(float expansion, boolean expanded) {
+                    mStatusBarExpansion = expansion;
+                    updateAlpha();
+                }
+            };
+
+    private final KeyguardStateController.Callback mKeyguardStateControllerCallback =
+            new KeyguardStateController.Callback() {
+                @Override
+                public void onLaunchTransitionFadingAwayChanged() {
+                    mLaunchTransitionFadingAway =
+                            mKeyguardStateController.isLaunchTransitionFadingAway();
+                    updatePauseAuth();
+                }
+            };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
index e1349f2..40c28fa 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
@@ -21,6 +21,7 @@
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_PRIMARY_DEVIANCE;
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_SECONDARY_DEVIANCE;
 import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
+import static com.android.systemui.classifier.Classifier.LOCK_ICON;
 import static com.android.systemui.classifier.Classifier.SHADE_DRAG;
 
 import android.graphics.Point;
@@ -89,7 +90,9 @@
     Result calculateFalsingResult(
             @Classifier.InteractionType int interactionType,
             double historyBelief, double historyConfidence) {
-        if (interactionType == BRIGHTNESS_SLIDER || interactionType == SHADE_DRAG) {
+        if (interactionType == BRIGHTNESS_SLIDER
+                || interactionType == SHADE_DRAG
+                || interactionType == LOCK_ICON) {
             return Result.passed(0);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 2d200e3..c093219 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -68,21 +68,15 @@
 import com.android.systemui.navigationbar.NavigationBarOverlayController;
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.navigationbar.TaskbarDelegate;
-import com.android.systemui.plugins.PluginInitializerImpl;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.ReduceBrightColorsController;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.settings.UserTracker;
-import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.shared.plugins.PluginManagerImpl;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.shared.system.WindowManagerWrapper;
-import com.android.unfold.UnfoldTransitionFactory;
-import com.android.unfold.UnfoldTransitionProgressProvider;
-import com.android.unfold.config.UnfoldTransitionConfig;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShadeDepthController;
@@ -98,6 +92,9 @@
 import com.android.systemui.theme.ThemeOverlayApplier;
 import com.android.systemui.util.leak.LeakDetector;
 import com.android.systemui.util.settings.SecureSettings;
+import com.android.systemui.unfold.UnfoldTransitionFactory;
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
+import com.android.systemui.unfold.config.UnfoldTransitionConfig;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
 import com.android.wm.shell.pip.Pip;
 
@@ -196,13 +193,6 @@
     }
 
     /** */
-    @Provides
-    @SysUISingleton
-    public PluginManager providePluginManager(Context context) {
-        return new PluginManagerImpl(context, new PluginInitializerImpl());
-    }
-
-    /** */
     @SysUISingleton
     @Provides
     static ThemeOverlayApplier provideThemeOverlayManager(Context context,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
index a89c7ac..648f345 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
@@ -23,6 +23,7 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.UiEventLoggerImpl;
 import com.android.systemui.dagger.qualifiers.TestHarness;
+import com.android.systemui.plugins.PluginsModule;
 import com.android.systemui.util.concurrency.GlobalConcurrencyModule;
 
 import javax.inject.Singleton;
@@ -47,7 +48,9 @@
  */
 @Module(includes = {
         FrameworkServicesModule.class,
-        GlobalConcurrencyModule.class})
+        GlobalConcurrencyModule.class,
+        PluginsModule.class,
+})
 public class GlobalModule {
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
index d648c94..a3a45fe 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
@@ -18,8 +18,6 @@
 
 import android.content.Context;
 
-import com.android.systemui.util.concurrency.ThreadFactory;
-
 import javax.inject.Singleton;
 
 import dagger.BindsInstance;
@@ -55,9 +53,4 @@
      * Builder for a SysUIComponent.
      */
     SysUIComponent.Builder getSysUIComponent();
-
-    /**
-     * Build a {@link ThreadFactory}.
-     */
-    ThreadFactory createThreadFactory();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index 239a77e..0923caaf 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -39,6 +39,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.EnhancedEstimates;
 import com.android.systemui.power.EnhancedEstimatesImpl;
+import com.android.systemui.power.dagger.PowerModule;
 import com.android.systemui.qs.dagger.QSModule;
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
 import com.android.systemui.recents.Recents;
@@ -67,6 +68,7 @@
 import com.android.systemui.statusbar.policy.IndividualSensorPrivacyControllerImpl;
 import com.android.systemui.statusbar.policy.SensorPrivacyController;
 import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl;
+import com.android.systemui.volume.dagger.VolumeModule;
 
 import javax.inject.Named;
 
@@ -80,7 +82,9 @@
  */
 @Module(includes = {
         MediaModule.class,
-        QSModule.class
+        PowerModule.class,
+        QSModule.class,
+        VolumeModule.class
 })
 public abstract class SystemUIDefaultModule {
 
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 556f956..d333341 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -18,6 +18,7 @@
 
 import android.app.INotificationManager;
 import android.content.Context;
+import android.view.LayoutInflater;
 
 import androidx.annotation.Nullable;
 
@@ -26,6 +27,7 @@
 import com.android.keyguard.dagger.KeyguardBouncerComponent;
 import com.android.systemui.BootCompleteCache;
 import com.android.systemui.BootCompleteCacheImpl;
+import com.android.systemui.R;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.appops.dagger.AppOpsModule;
 import com.android.systemui.assist.AssistModule;
@@ -36,18 +38,17 @@
 import com.android.systemui.demomode.dagger.DemoModeModule;
 import com.android.systemui.doze.dagger.DozeComponent;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.log.dagger.LogModule;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.BcSmartspaceDataPlugin;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.power.dagger.PowerModule;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.screenshot.dagger.ScreenshotModule;
 import com.android.systemui.settings.dagger.SettingsModule;
 import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -62,6 +63,7 @@
 import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarWindowView;
 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -70,13 +72,13 @@
 import com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule;
 import com.android.systemui.tuner.dagger.TunerModule;
 import com.android.systemui.user.UserModule;
+import com.android.systemui.util.InjectionInflationController;
 import com.android.systemui.util.concurrency.SysUIConcurrencyModule;
 import com.android.systemui.util.dagger.UtilModule;
 import com.android.systemui.util.sensors.SensorModule;
 import com.android.systemui.util.settings.SettingsUtilModule;
 import com.android.systemui.util.time.SystemClock;
 import com.android.systemui.util.time.SystemClockImpl;
-import com.android.systemui.volume.dagger.VolumeModule;
 import com.android.systemui.wallet.dagger.WalletModule;
 import com.android.systemui.wmshell.BubblesManager;
 import com.android.wm.shell.bubbles.Bubbles;
@@ -102,7 +104,6 @@
             FalsingModule.class,
             LogModule.class,
             PeopleHubModule.class,
-            PowerModule.class,
             PluginModule.class,
             ScreenshotModule.class,
             SensorModule.class,
@@ -114,7 +115,6 @@
             TunerModule.class,
             UserModule.class,
             UtilModule.class,
-            VolumeModule.class,
             WalletModule.class
         },
         subcomponents = {
@@ -202,4 +202,19 @@
                 groupManager, entryManager, notifPipeline, sysUiState, featureFlags, dumpManager,
                 sysuiMainExecutor));
     }
+
+    @Provides
+    @SysUISingleton
+    static StatusBarWindowView providesStatusBarWindowView(Context context,
+            InjectionInflationController injectionInflationController) {
+        StatusBarWindowView view =
+                (StatusBarWindowView) injectionInflationController.injectable(
+                        LayoutInflater.from(context)).inflate(R.layout.super_status_bar,
+                        /* root= */ null);
+        if (view == null) {
+            throw new IllegalStateException(
+                    "R.layout.super_status_bar could not be properly inflated");
+        }
+        return view;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 470d2f3..98d2739 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -31,6 +31,7 @@
 import android.provider.Settings;
 import android.view.Display;
 
+import com.android.systemui.dock.DockManager;
 import com.android.systemui.doze.dagger.BrightnessSensor;
 import com.android.systemui.doze.dagger.DozeScope;
 import com.android.systemui.doze.dagger.WrappedService;
@@ -63,6 +64,7 @@
     private final Optional<Sensor> mLightSensorOptional;
     private final WakefulnessLifecycle mWakefulnessLifecycle;
     private final DozeParameters mDozeParameters;
+    private final DockManager mDockManager;
     private final int[] mSensorToBrightness;
     private final int[] mSensorToScrimOpacity;
     private final int mScreenBrightnessDim;
@@ -87,7 +89,8 @@
             @BrightnessSensor Optional<Sensor> lightSensorOptional, DozeHost host, Handler handler,
             AlwaysOnDisplayPolicy alwaysOnDisplayPolicy,
             WakefulnessLifecycle wakefulnessLifecycle,
-            DozeParameters dozeParameters) {
+            DozeParameters dozeParameters,
+            DockManager dockManager) {
         mContext = context;
         mDozeService = service;
         mSensorManager = sensorManager;
@@ -96,6 +99,7 @@
         mDozeParameters = dozeParameters;
         mDozeHost = host;
         mHandler = handler;
+        mDockManager = dockManager;
 
         mDefaultDozeBrightness = alwaysOnDisplayPolicy.defaultDozeBrightness;
         mScreenBrightnessDim = alwaysOnDisplayPolicy.dimBrightness;
@@ -122,13 +126,20 @@
 
     @Override
     public void onScreenState(int state) {
-        if (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND) {
+        boolean isDockedScreenOn = state == Display.STATE_ON && mDockManager.isDocked();
+        if (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND
+                || (isDockedScreenOn && shouldRegisterLightSensorWhenScreenOnDocked())) {
             setLightSensorEnabled(true);
         } else {
             setLightSensorEnabled(false);
         }
     }
 
+    private boolean shouldRegisterLightSensorWhenScreenOnDocked() {
+        return !mDozeParameters.brightnessUsesProx()
+                || !mDozeParameters.getSelectivelyRegisterSensorsUsingProx();
+    }
+
     private void onDestroy() {
         setLightSensorEnabled(false);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 23c4413..55e6154 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -115,6 +115,7 @@
         mSecureSettings = secureSettings;
         mCallback = callback;
         mProximitySensor = proximitySensor;
+        mProximitySensor.setTag(TAG);
         mSelectivelyRegisterProxSensors = dozeParameters.getSelectivelyRegisterSensorsUsingProx();
         mListeningProxSensors = !mSelectivelyRegisterProxSensors;
         mScreenOffUdfpsEnabled =
@@ -455,13 +456,24 @@
 
         public void updateListening() {
             if (!mConfigured || mSensor == null) return;
-            if (mRequested && !mDisabled && (enabledBySetting() || mIgnoresSetting)
-                    && !mRegistered) {
-                mRegistered = mSensorManager.requestTriggerSensor(this, mSensor);
-                if (DEBUG) Log.d(TAG, "requestTriggerSensor " + mRegistered);
+            if (mRequested && !mDisabled && (enabledBySetting() || mIgnoresSetting)) {
+                if (!mRegistered) {
+                    mRegistered = mSensorManager.requestTriggerSensor(this, mSensor);
+                    if (DEBUG) {
+                        Log.d(TAG, "requestTriggerSensor[" + mSensor
+                                + "] " + mRegistered);
+                    }
+                } else {
+                    if (DEBUG) {
+                        Log.d(TAG, "requestTriggerSensor[" + mSensor
+                                + "] already registered");
+                    }
+                }
             } else if (mRegistered) {
                 final boolean rt = mSensorManager.cancelTriggerSensor(this, mSensor);
-                if (DEBUG) Log.d(TAG, "cancelTriggerSensor " + rt);
+                if (DEBUG) {
+                    Log.d(TAG, "cancelTriggerSensor[" + mSensor + "] " + rt);
+                }
                 mRegistered = false;
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/util/BurnInHelper.kt b/packages/SystemUI/src/com/android/systemui/doze/util/BurnInHelper.kt
index 15e3f3a..5c6478e 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/util/BurnInHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/doze/util/BurnInHelper.kt
@@ -21,8 +21,8 @@
 private const val MILLIS_PER_MINUTES = 1000 * 60f
 private const val BURN_IN_PREVENTION_PERIOD_Y = 521f
 private const val BURN_IN_PREVENTION_PERIOD_X = 83f
-private const val BURN_IN_PREVENTION_PERIOD_SCALE = 180f
-private const val BURN_IN_PREVENTION_PERIOD_PROGRESS = 120f
+private const val BURN_IN_PREVENTION_PERIOD_SCALE = 181f
+private const val BURN_IN_PREVENTION_PERIOD_PROGRESS = 89f
 
 /**
  * Returns the translation offset that should be used to avoid burn in at
diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
index bfa4780..5b327bd 100644
--- a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
@@ -34,7 +34,7 @@
  * See [DumpHandler] for more information on how and when this information is dumped.
  */
 @SysUISingleton
-class DumpManager @Inject constructor() {
+open class DumpManager @Inject constructor() {
     private val dumpables: MutableMap<String, RegisteredDumpable<Dumpable>> = ArrayMap()
     private val buffers: MutableMap<String, RegisteredDumpable<LogBuffer>> = ArrayMap()
 
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java
index 6fbf81c..d4d01c8 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagReader.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.flags;
 
+import android.content.Context;
 import android.content.res.Resources;
 import android.util.SparseArray;
 
@@ -25,7 +26,9 @@
 import com.android.systemui.R;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.plugins.FlagReaderPlugin;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.util.wrapper.BuildInfo;
 
 import javax.inject.Inject;
@@ -55,18 +58,68 @@
 public class FeatureFlagReader {
     private final Resources mResources;
     private final boolean mAreFlagsOverrideable;
+    private final PluginManager mPluginManager;
     private final SystemPropertiesHelper mSystemPropertiesHelper;
     private final SparseArray<CachedFlag> mCachedFlags = new SparseArray<>();
 
+    private FlagReaderPlugin mPlugin = new FlagReaderPlugin(){};
+
     @Inject
     public FeatureFlagReader(
             @Main Resources resources,
             BuildInfo build,
+            PluginManager pluginManager,
             SystemPropertiesHelper systemPropertiesHelper) {
         mResources = resources;
+        mPluginManager = pluginManager;
         mSystemPropertiesHelper = systemPropertiesHelper;
         mAreFlagsOverrideable =
                 build.isDebuggable() && mResources.getBoolean(R.bool.are_flags_overrideable);
+
+        mPluginManager.addPluginListener(mPluginListener, FlagReaderPlugin.class);
+    }
+
+    private final PluginListener<FlagReaderPlugin> mPluginListener =
+            new PluginListener<FlagReaderPlugin>() {
+                public void onPluginConnected(FlagReaderPlugin plugin, Context context) {
+                    mPlugin = plugin;
+                }
+
+                public void onPluginDisconnected(FlagReaderPlugin plugin) {
+                    mPlugin = new FlagReaderPlugin() {};
+                }
+            };
+
+    boolean isEnabled(BooleanFlag flag) {
+        return mPlugin.isEnabled(flag.getId(), flag.getDefault());
+    }
+
+    String getValue(StringFlag flag) {
+        return mPlugin.getValue(flag.getId(), flag.getDefault());
+    }
+
+    int getValue(IntFlag flag) {
+        return mPlugin.getValue(flag.getId(), flag.getDefault());
+    }
+
+    long getValue(LongFlag flag) {
+        return mPlugin.getValue(flag.getId(), flag.getDefault());
+    }
+
+    float getValue(FloatFlag flag) {
+        return mPlugin.getValue(flag.getId(), flag.getDefault());
+    }
+
+    double getValue(DoubleFlag flag) {
+        return mPlugin.getValue(flag.getId(), flag.getDefault());
+    }
+
+    void addListener(FlagReaderPlugin.Listener listener) {
+        mPlugin.addListener(listener);
+    }
+
+    void removeListener(FlagReaderPlugin.Listener listener) {
+        mPlugin.removeListener(listener);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
new file mode 100644
index 0000000..e51f90f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.java
@@ -0,0 +1,200 @@
+/*
+ * 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.flags;
+
+import android.content.Context;
+import android.util.FeatureFlagUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.R;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.plugins.FlagReaderPlugin;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Inject;
+
+/**
+ * Class to manage simple DeviceConfig-based feature flags.
+ *
+ * See {@link FeatureFlagReader} for instructions on defining and flipping flags.
+ */
+@SysUISingleton
+public class FeatureFlags {
+    private final FeatureFlagReader mFlagReader;
+    private final Context mContext;
+    private final Map<Integer, Flag<?>> mFlagMap = new HashMap<>();
+    private final Map<Integer, List<Listener>> mListeners = new HashMap<>();
+
+    @Inject
+    public FeatureFlags(FeatureFlagReader flagReader, Context context) {
+        mFlagReader = flagReader;
+        mContext = context;
+
+        flagReader.addListener(mListener);
+    }
+
+    private final FlagReaderPlugin.Listener mListener = id -> {
+        if (mListeners.containsKey(id) && mFlagMap.containsKey(id)) {
+            mListeners.get(id).forEach(listener -> listener.onFlagChanged(mFlagMap.get(id)));
+        }
+    };
+
+    @VisibleForTesting
+    void addFlag(Flag flag) {
+        mFlagMap.put(flag.getId(), flag);
+    }
+
+    /**
+     * @param flag The {@link BooleanFlag} of interest.
+     * @return The value of the flag.
+     */
+    public boolean isEnabled(BooleanFlag flag) {
+        return mFlagReader.isEnabled(flag);
+    }
+
+    /**
+     * @param flag The {@link StringFlag} of interest.
+     * @return The value of the flag.
+     */
+    public String getValue(StringFlag flag) {
+        return mFlagReader.getValue(flag);
+    }
+
+    /**
+     * @param flag The {@link IntFlag} of interest.
+     * @return The value of the flag.
+     */
+    public int getValue(IntFlag flag) {
+        return mFlagReader.getValue(flag);
+    }
+
+    /**
+     * @param flag The {@link LongFlag} of interest.
+     * @return The value of the flag.
+     */
+    public long getValue(LongFlag flag) {
+        return mFlagReader.getValue(flag);
+    }
+
+    /**
+     * @param flag The {@link FloatFlag} of interest.
+     * @return The value of the flag.
+     */
+    public float getValue(FloatFlag flag) {
+        return mFlagReader.getValue(flag);
+    }
+
+    /**
+     * @param flag The {@link DoubleFlag} of interest.
+     * @return The value of the flag.
+     */
+    public double getValue(DoubleFlag flag) {
+        return mFlagReader.getValue(flag);
+    }
+
+    /** Add a listener for a specific flag. */
+    public void addFlagListener(Flag<?> flag, Listener listener) {
+        mListeners.putIfAbsent(flag.getId(), new ArrayList<>());
+        mListeners.get(flag.getId()).add(listener);
+        mFlagMap.putIfAbsent(flag.getId(), flag);
+    }
+
+    /** Remove a listener for a specific flag. */
+    public void removeFlagListener(Flag<?> flag, Listener listener) {
+        if (mListeners.containsKey(flag.getId())) {
+            mListeners.get(flag.getId()).remove(listener);
+        }
+    }
+
+    public boolean isNewNotifPipelineEnabled() {
+        return mFlagReader.isEnabled(R.bool.flag_notification_pipeline2);
+    }
+
+    public boolean isNewNotifPipelineRenderingEnabled() {
+        return mFlagReader.isEnabled(R.bool.flag_notification_pipeline2_rendering);
+    }
+
+    public boolean isKeyguardLayoutEnabled() {
+        return mFlagReader.isEnabled(R.bool.flag_keyguard_layout);
+    }
+
+    /** */
+    public boolean useNewLockscreenAnimations() {
+        return mFlagReader.isEnabled(R.bool.flag_lockscreen_animations);
+    }
+
+    public boolean isPeopleTileEnabled() {
+        return mFlagReader.isEnabled(R.bool.flag_conversations);
+    }
+
+    public boolean isMonetEnabled() {
+        return mFlagReader.isEnabled(R.bool.flag_monet);
+    }
+
+    public boolean isPMLiteEnabled() {
+        return mFlagReader.isEnabled(R.bool.flag_pm_lite);
+    }
+
+    public boolean isChargingRippleEnabled() {
+        return mFlagReader.isEnabled(R.bool.flag_charging_ripple);
+    }
+
+    public boolean isOngoingCallStatusBarChipEnabled() {
+        return mFlagReader.isEnabled(R.bool.flag_ongoing_call_status_bar_chip);
+    }
+
+    public boolean isSmartspaceEnabled() {
+        return mFlagReader.isEnabled(R.bool.flag_smartspace);
+    }
+
+    public boolean isSmartspaceDedupingEnabled() {
+        return isSmartspaceEnabled() && mFlagReader.isEnabled(R.bool.flag_smartspace_deduping);
+    }
+
+    public boolean isNewKeyguardSwipeAnimationEnabled() {
+        return mFlagReader.isEnabled(R.bool.flag_new_unlock_swipe_animation);
+    }
+
+    public boolean isSmartSpaceSharedElementTransitionEnabled() {
+        return mFlagReader.isEnabled(R.bool.flag_smartspace_shared_element_transition);
+    }
+
+    /** Whether or not to use the provider model behavior for the status bar icons */
+    public boolean isCombinedStatusBarSignalIconsEnabled() {
+        return mFlagReader.isEnabled(R.bool.flag_combined_status_bar_signal_icons);
+    }
+
+    /** System setting for provider model behavior */
+    public boolean isProviderModelSettingEnabled() {
+        return FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
+    }
+
+    /** static method for the system setting */
+    public static boolean isProviderModelSettingEnabled(Context context) {
+        return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
+    }
+
+    /** Simple interface for beinga alerted when a specific flag changes value. */
+    public interface Listener {
+        /** */
+        void onFlagChanged(Flag<?> flag);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt b/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt
index 28f63b0..6561bd5 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt
@@ -25,8 +25,12 @@
  * Proxy to make {@link SystemProperties} easily testable.
  */
 @SysUISingleton
-class SystemPropertiesHelper @Inject constructor() {
+open class SystemPropertiesHelper @Inject constructor() {
     fun getBoolean(name: String, default: Boolean): Boolean {
         return SystemProperties.getBoolean(name, default)
     }
+
+    fun set(name: String, value: Int) {
+        SystemProperties.set(name, value.toString())
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 04a0226..df6aa34 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -62,6 +62,7 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.view.RotationPolicy;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.animation.Interpolators;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
@@ -173,7 +174,8 @@
             SysUiState sysUiState,
             @Main Handler handler,
             PackageManager packageManager,
-            Optional<StatusBar> statusBarOptional) {
+            Optional<StatusBar> statusBarOptional,
+            KeyguardUpdateMonitor keyguardUpdateMonitor) {
 
         super(context,
                 windowManagerFuncs,
@@ -204,7 +206,8 @@
                 sysUiState,
                 handler,
                 packageManager,
-                statusBarOptional);
+                statusBarOptional,
+                keyguardUpdateMonitor);
 
         mLockPatternUtils = lockPatternUtils;
         mKeyguardStateController = keyguardStateController;
@@ -266,7 +269,7 @@
                 this::getWalletViewController, mSysuiColorExtractor,
                 mStatusBarService, mNotificationShadeWindowController,
                 mSysUiState, this::onRotate, isKeyguardShowing(), mPowerAdapter, getEventLogger(),
-                getStatusBar());
+                getStatusBar(), getKeyguardUpdateMonitor(), mLockPatternUtils);
 
         if (shouldShowLockMessage(dialog)) {
             dialog.showLockMessage();
@@ -334,11 +337,13 @@
                 NotificationShadeWindowController notificationShadeWindowController,
                 SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
                 MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
-                Optional<StatusBar> statusBarOptional) {
+                Optional<StatusBar> statusBarOptional, KeyguardUpdateMonitor keyguardUpdateMonitor,
+                LockPatternUtils lockPatternUtils) {
             super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions,
                     adapter, overflowAdapter, sysuiColorExtractor, statusBarService,
                     notificationShadeWindowController, sysuiState, onRotateCallback,
-                    keyguardShowing, powerAdapter, uiEventLogger, statusBarOptional);
+                    keyguardShowing, powerAdapter, uiEventLogger, statusBarOptional,
+                    keyguardUpdateMonitor, lockPatternUtils);
             mWalletFactory = walletFactory;
 
             // Update window attributes
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 6cd5f91..b06b024 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -84,6 +84,7 @@
 import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
 import android.widget.BaseAdapter;
 import android.widget.ImageView;
 import android.widget.ImageView.ScaleType;
@@ -108,6 +109,7 @@
 import com.android.internal.util.EmergencyAffordanceManager;
 import com.android.internal.util.ScreenshotHelper;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.MultiListLayout;
 import com.android.systemui.MultiListLayout.MultiListAdapter;
 import com.android.systemui.animation.Interpolators;
@@ -171,6 +173,11 @@
     static final String GLOBAL_ACTION_KEY_EMERGENCY = "emergency";
     static final String GLOBAL_ACTION_KEY_SCREENSHOT = "screenshot";
 
+    // See NotificationManagerService#scheduleDurationReachedLocked
+    private static final long TOAST_FADE_TIME = 333;
+    // See NotificationManagerService.LONG_DELAY
+    private static final int TOAST_VISIBLE_TIME = 3500;
+
     private final Context mContext;
     private final GlobalActionsManager mWindowManagerFuncs;
     private final AudioManager mAudioManager;
@@ -231,6 +238,7 @@
     protected Handler mMainHandler;
     private int mSmallestScreenWidthDp;
     private final Optional<StatusBar> mStatusBarOptional;
+    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
 
     @VisibleForTesting
     public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum {
@@ -337,7 +345,8 @@
             SysUiState sysUiState,
              @Main Handler handler,
             PackageManager packageManager,
-            Optional<StatusBar> statusBarOptional) {
+            Optional<StatusBar> statusBarOptional,
+            KeyguardUpdateMonitor keyguardUpdateMonitor) {
         mContext = context;
         mWindowManagerFuncs = windowManagerFuncs;
         mAudioManager = audioManager;
@@ -367,6 +376,7 @@
         mMainHandler = handler;
         mSmallestScreenWidthDp = resources.getConfiguration().smallestScreenWidthDp;
         mStatusBarOptional = statusBarOptional;
+        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
 
         // receive broadcasts
         IntentFilter filter = new IntentFilter();
@@ -420,6 +430,10 @@
         return mStatusBarOptional;
     }
 
+    protected KeyguardUpdateMonitor getKeyguardUpdateMonitor() {
+        return mKeyguardUpdateMonitor;
+    }
+
     /**
      * Show the global actions dialog (creating if necessary)
      *
@@ -651,7 +665,7 @@
                 mAdapter, mOverflowAdapter, mSysuiColorExtractor,
                 mStatusBarService, mNotificationShadeWindowController,
                 mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter, mUiEventLogger,
-                mStatusBarOptional);
+                mStatusBarOptional, mKeyguardUpdateMonitor, mLockPatternUtils);
 
         dialog.setOnDismissListener(this);
         dialog.setOnShowListener(this);
@@ -2119,6 +2133,8 @@
         private UiEventLogger mUiEventLogger;
         private GestureDetector mGestureDetector;
         private Optional<StatusBar> mStatusBarOptional;
+        private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+        private LockPatternUtils mLockPatternUtils;
 
         protected ViewGroup mContainer;
 
@@ -2172,7 +2188,8 @@
                 NotificationShadeWindowController notificationShadeWindowController,
                 SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
                 MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
-                Optional<StatusBar> statusBarOptional) {
+                Optional<StatusBar> statusBarOptional,
+                KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils) {
             super(context, themeRes);
             mContext = context;
             mAdapter = adapter;
@@ -2186,6 +2203,8 @@
             mKeyguardShowing = keyguardShowing;
             mUiEventLogger = uiEventLogger;
             mStatusBarOptional = statusBarOptional;
+            mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+            mLockPatternUtils = lockPatternUtils;
 
             mGestureDetector = new GestureDetector(mContext, mGestureListener);
 
@@ -2304,6 +2323,14 @@
                 mBackgroundDrawable = new ScrimDrawable();
                 mScrimAlpha = 1.0f;
             }
+
+            // If user entered from the lock screen and smart lock was enabled, disable it
+            int user = KeyguardUpdateMonitor.getCurrentUser();
+            boolean userHasTrust = mKeyguardUpdateMonitor.getUserHasTrust(user);
+            if (mKeyguardShowing && userHasTrust) {
+                mLockPatternUtils.requireCredentialEntry(KeyguardUpdateMonitor.getCurrentUser());
+                showSmartLockDisabledMessage();
+            }
         }
 
         protected void fixNavBarClipping() {
@@ -2315,6 +2342,37 @@
             contentParent.setClipToPadding(false);
         }
 
+        private void showSmartLockDisabledMessage() {
+            // Since power menu is the top window, make a Toast-like view that will show up
+            View message = LayoutInflater.from(mContext)
+                    .inflate(com.android.systemui.R.layout.global_actions_toast, mContainer, false);
+
+            // Set up animation
+            AccessibilityManager mAccessibilityManager =
+                    (AccessibilityManager) getContext().getSystemService(
+                            Context.ACCESSIBILITY_SERVICE);
+            final int visibleTime = mAccessibilityManager.getRecommendedTimeoutMillis(
+                    TOAST_VISIBLE_TIME, AccessibilityManager.FLAG_CONTENT_TEXT);
+            message.setVisibility(View.VISIBLE);
+            message.setAlpha(0f);
+            mContainer.addView(message);
+
+            // Fade in
+            message.animate()
+                    .alpha(1f)
+                    .setDuration(TOAST_FADE_TIME)
+                    .setListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            // Then fade out
+                            message.animate()
+                                    .alpha(0f)
+                                    .setDuration(TOAST_FADE_TIME)
+                                    .setStartDelay(visibleTime);
+                        }
+                    });
+        }
+
         @Override
         protected void onStart() {
             super.onStart();
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index a641ad4..c4508e0 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -97,7 +97,7 @@
                 int backgroundAlpha = (int) (ScrimController.BUSY_SCRIM_ALPHA * 255);
                 background.setAlpha(backgroundAlpha);
                 mBlurUtils.applyBlur(d.getWindow().getDecorView().getViewRootImpl(),
-                        mBlurUtils.blurRadiusOfRatio(1), backgroundAlpha == 255);
+                        (int) mBlurUtils.blurRadiusOfRatio(1), backgroundAlpha == 255);
             } else {
                 float backgroundAlpha = mContext.getResources().getFloat(
                         com.android.systemui.R.dimen.shutdown_scrim_behind_alpha);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 391e6de..3577395 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -20,6 +20,9 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.RemoteAnimationTarget.MODE_CLOSING;
 import static android.view.RemoteAnimationTarget.MODE_OPENING;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
@@ -28,8 +31,13 @@
 import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE;
 import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
 import static android.view.WindowManager.TRANSIT_OLD_NONE;
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.view.WindowManager.TransitionFlags;
 import static android.view.WindowManager.TransitionOldType;
 import static android.view.WindowManager.TransitionType;
+import static android.window.TransitionInfo.FLAG_OCCLUDES_KEYGUARD;
 
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
@@ -158,14 +166,15 @@
                     info.getChanges().size() - i,
                     new Point(), localBounds, new Rect(change.getEndAbsBounds()),
                     windowConfiguration, isNotInRecents, null /* startLeash */,
-                    change.getStartAbsBounds(), taskInfo));
+                    change.getStartAbsBounds(), taskInfo, false /* allowEnterPip */));
         }
         return out.toArray(new RemoteAnimationTarget[out.size()]);
     }
 
     private static @TransitionOldType int getTransitionOldType(@TransitionType int type,
-            RemoteAnimationTarget[] apps) {
-        if (type == TRANSIT_KEYGUARD_GOING_AWAY) {
+            @TransitionFlags int flags, RemoteAnimationTarget[] apps) {
+        if (type == TRANSIT_KEYGUARD_GOING_AWAY
+                || (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) {
             return apps.length == 0 ? TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER
                     : TRANSIT_OLD_KEYGUARD_GOING_AWAY;
         } else if (type == TRANSIT_KEYGUARD_OCCLUDE) {
@@ -194,7 +203,7 @@
                     t.setAlpha(change.getLeash(), 1.0f);
                 }
                 t.apply();
-                runner.onAnimationStart(getTransitionOldType(info.getType(), apps),
+                runner.onAnimationStart(getTransitionOldType(info.getType(), info.getFlags(), apps),
                         apps, wallpapers, nonApps,
                         new IRemoteAnimationFinishedCallback.Stub() {
                             @Override
@@ -226,14 +235,47 @@
             if (sEnableRemoteKeyguardGoingAwayAnimation) {
                 Slog.d(TAG, "KeyguardService registerRemote: TRANSIT_KEYGUARD_GOING_AWAY");
                 TransitionFilter f = new TransitionFilter();
-                f.mTypeSet = new int[]{TRANSIT_KEYGUARD_GOING_AWAY};
+                f.mFlags = TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
                 shellTransitions.registerRemote(f, wrap(mExitAnimationRunner));
             }
             if (sEnableRemoteKeyguardOccludeAnimation) {
                 Slog.d(TAG, "KeyguardService registerRemote: TRANSIT_KEYGUARD_(UN)OCCLUDE");
+                // Register for occluding
                 TransitionFilter f = new TransitionFilter();
-                f.mTypeSet = new int[]{TRANSIT_KEYGUARD_OCCLUDE, TRANSIT_KEYGUARD_UNOCCLUDE};
-                shellTransitions.registerRemote(f, wrap(mOccludeAnimationRunner));
+                f.mFlags = TRANSIT_FLAG_KEYGUARD_LOCKED;
+                f.mRequirements = new TransitionFilter.Requirement[]{
+                        new TransitionFilter.Requirement(), new TransitionFilter.Requirement()};
+                // First require at-least one app showing that occludes.
+                f.mRequirements[0].mMustBeIndependent = false;
+                f.mRequirements[0].mFlags = FLAG_OCCLUDES_KEYGUARD;
+                f.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
+                // Then require that we aren't closing any occludes (because this would mean a
+                // regular task->task or activity->activity animation not involving keyguard).
+                f.mRequirements[1].mNot = true;
+                f.mRequirements[1].mMustBeIndependent = false;
+                f.mRequirements[1].mFlags = FLAG_OCCLUDES_KEYGUARD;
+                f.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK};
+                shellTransitions.registerRemote(f, mOccludeAnimation);
+
+                // Now register for un-occlude.
+                f = new TransitionFilter();
+                f.mFlags = TRANSIT_FLAG_KEYGUARD_LOCKED;
+                f.mRequirements = new TransitionFilter.Requirement[]{
+                        new TransitionFilter.Requirement(), new TransitionFilter.Requirement()};
+                // First require at-least one app going-away (doesn't need occlude flag
+                // as that is implicit by it having been visible and we don't want to exclude
+                // cases where we are un-occluding because the app removed its showWhenLocked
+                // capability at runtime).
+                f.mRequirements[1].mMustBeIndependent = false;
+                f.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK};
+                f.mRequirements[1].mMustBeTask = true;
+                // Then require that we aren't opening any occludes (otherwise we'd remain
+                // occluded).
+                f.mRequirements[0].mNot = true;
+                f.mRequirements[0].mMustBeIndependent = false;
+                f.mRequirements[0].mFlags = FLAG_OCCLUDES_KEYGUARD;
+                f.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
+                shellTransitions.registerRemote(f, mUnoccludeAnimation);
             }
         } else {
             RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
@@ -328,6 +370,40 @@
         }
     };
 
+    final IRemoteTransition mOccludeAnimation = new IRemoteTransition.Stub() {
+        @Override
+        public void startAnimation(IBinder transition, TransitionInfo info,
+                SurfaceControl.Transaction t, IRemoteTransitionFinishedCallback finishCallback)
+                    throws RemoteException {
+            t.apply();
+            mBinder.setOccluded(true /* isOccluded */, true /* animate */);
+            finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
+        }
+
+        @Override
+        public void mergeAnimation(IBinder transition, TransitionInfo info,
+                SurfaceControl.Transaction t, IBinder mergeTarget,
+                IRemoteTransitionFinishedCallback finishCallback) {
+        }
+    };
+
+    final IRemoteTransition mUnoccludeAnimation = new IRemoteTransition.Stub() {
+        @Override
+        public void startAnimation(IBinder transition, TransitionInfo info,
+                SurfaceControl.Transaction t, IRemoteTransitionFinishedCallback finishCallback)
+                throws RemoteException {
+            t.apply();
+            mBinder.setOccluded(false /* isOccluded */, true /* animate */);
+            finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
+        }
+
+        @Override
+        public void mergeAnimation(IBinder transition, TransitionInfo info,
+                SurfaceControl.Transaction t, IBinder mergeTarget,
+                IRemoteTransitionFinishedCallback finishCallback) {
+        }
+    };
+
     private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() {
 
         @Override // Binder interface
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index a5fe9015..e51b602 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -31,7 +31,7 @@
 import com.android.systemui.animation.Interpolators
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController
-import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import dagger.Lazy
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index d12c763..2817718 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -399,6 +399,12 @@
     private boolean mPendingLock;
 
     /**
+     * When starting to go away, flag a need to show the PIN lock so the keyguard can be brought
+     * back.
+     */
+    private boolean mPendingPinLock = false;
+
+    /**
      * Whether a power button gesture (such as double tap for camera) has been detected. This is
      * delivered directly from {@link KeyguardService}, immediately upon the gesture being detected.
      * This is used in {@link #onStartedWakingUp} to decide whether to execute the pending lock, or
@@ -472,6 +478,19 @@
     KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() {
 
         @Override
+        public void onKeyguardVisibilityChanged(boolean showing) {
+            synchronized (KeyguardViewMediator.this) {
+                if (!showing && mPendingPinLock) {
+                    Log.i(TAG, "PIN lock requested, starting keyguard");
+
+                    // Bring the keyguard back in order to show the PIN lock
+                    mPendingPinLock = false;
+                    doKeyguardLocked(null);
+                }
+            }
+        }
+
+        @Override
         public void onUserSwitching(int userId) {
             if (DEBUG) Log.d(TAG, String.format("onUserSwitching %d", userId));
             // Note that the mLockPatternUtils user has already been updated from setCurrentUser.
@@ -591,6 +610,7 @@
                                     + "showing; need to show keyguard so user can enter sim pin");
                             doKeyguardLocked(null);
                         } else {
+                            mPendingPinLock = true;
                             resetStateLocked();
                         }
                     }
@@ -739,6 +759,9 @@
         @Override
         public void onBouncerVisiblityChanged(boolean shown) {
             synchronized (KeyguardViewMediator.this) {
+                if (shown) {
+                    mPendingPinLock = false;
+                }
                 adjustStatusBarLocked(shown, false);
             }
         }
@@ -2324,8 +2347,8 @@
     }
 
     private Configuration.Builder createInteractionJankMonitorConf(String tag) {
-        return new Configuration.Builder(CUJ_LOCKSCREEN_UNLOCK_ANIMATION)
-                .setView(mKeyguardViewControllerLazy.get().getViewRootImpl().getView())
+        return Configuration.Builder.withView(CUJ_LOCKSCREEN_UNLOCK_ANIMATION,
+                mKeyguardViewControllerLazy.get().getViewRootImpl().getView())
                 .setTag(tag);
     }
 
@@ -2580,7 +2603,7 @@
             if (mContext.getResources().getBoolean(
                     com.android.internal.R.bool.config_guestUserAutoCreated)) {
                 // TODO(b/191067027): Move post-boot guest creation to system_server
-                mUserSwitcherController.guaranteeGuestPresent();
+                mUserSwitcherController.schedulePostBootGuestCreation();
             }
             mBootCompleted = true;
             adjustStatusBarLocked(false, true);
@@ -2736,7 +2759,10 @@
 
         // Don't hide the keyguard due to a doze change if there's a lock pending, because we're
         // just going to show it again.
-        if (mShowing || !mPendingLock) {
+        // If the device is not capable of controlling the screen off animation, SysUI needs to
+        // update lock screen state in ATMS here, otherwise ATMS tries to resume activities when
+        // enabling doze state.
+        if (mShowing || !mPendingLock || !mDozeParameters.canControlUnlockedScreenOff()) {
             setShowingLocked(mShowing);
         }
     }
@@ -2783,7 +2809,7 @@
         }
     }
 
-    private void setShowingLocked(boolean showing) {
+    void setShowingLocked(boolean showing) {
         setShowingLocked(showing, false /* forceCallbacks */);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
index d8905a0..f25ec55 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
@@ -16,8 +16,8 @@
 package com.android.systemui.keyguard
 
 import com.android.systemui.dagger.SysUISingleton
-import com.android.unfold.updates.screen.ScreenStatusProvider
-import com.android.unfold.updates.screen.ScreenStatusProvider.ScreenListener
+import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
+import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener
 import javax.inject.Inject
 
 @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
index 89786ee..a617850 100644
--- a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
+++ b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java
@@ -139,7 +139,7 @@
                                 + " with ducking", e);
                     }
                     player.start();
-                    if (DEBUG) { Log.d(mTag, "player.start"); }
+                    if (DEBUG) { Log.d(mTag, "player.start piid:" + player.getPlayerIId()); }
                 } catch (Exception e) {
                     if (player != null) {
                         player.release();
@@ -155,7 +155,13 @@
                     mPlayer = player;
                 }
                 if (mp != null) {
-                    if (DEBUG) { Log.d(mTag, "mPlayer.release"); }
+                    if (DEBUG) {
+                        Log.d(mTag, "mPlayer.pause+release piid:" + player.getPlayerIId());
+                    }
+                    mp.pause();
+                    try {
+                        Thread.sleep(100);
+                    } catch (InterruptedException ie) { }
                     mp.release();
                 }
                 this.notify();
@@ -244,6 +250,10 @@
                         try {
                             mp.stop();
                         } catch (Exception e) { }
+                        if (DEBUG) {
+                            Log.i(mTag, "About to release MediaPlayer piid:"
+                                    + mp.getPlayerIId() + " due to notif cancelled");
+                        }
                         mp.release();
                         synchronized(mQueueAudioFocusLock) {
                             if (mAudioManagerWithAudioFocus != null) {
@@ -284,7 +294,7 @@
     public void onCompletion(MediaPlayer mp) {
         synchronized(mQueueAudioFocusLock) {
             if (mAudioManagerWithAudioFocus != null) {
-                if (DEBUG) Log.d(mTag, "onCompletion() abandonning AudioFocus");
+                if (DEBUG) Log.d(mTag, "onCompletion() abandoning AudioFocus");
                 mAudioManagerWithAudioFocus.abandonAudioFocus(null);
                 mAudioManagerWithAudioFocus = null;
             } else {
@@ -310,6 +320,10 @@
             }
         }
         if (mp != null) {
+            if (DEBUG) {
+                Log.i("NotificationPlayer", "About to release MediaPlayer piid:"
+                        + mp.getPlayerIId() + " due to onCompletion");
+            }
             mp.release();
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 5293c88..b2def7a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -466,7 +466,7 @@
     }
 
     boolean isVolumeControlEnabled(@NonNull MediaDevice device) {
-        return !device.getFeatures().contains(MediaRoute2Info.FEATURE_REMOTE_GROUP_PLAYBACK);
+        return !isActiveRemoteDevice(device);
     }
 
     private final MediaController.Callback mCb = new MediaController.Callback() {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 543004e..2e7ab6e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -116,7 +116,7 @@
     private final TaskbarDelegate mTaskbarDelegate;
     private final NotificationShadeDepthController mNotificationShadeDepthController;
     private int mNavMode;
-    private boolean mIsTablet;
+    @VisibleForTesting boolean mIsTablet;
     private final UserTracker mUserTracker;
 
     /** A displayId - nav bar maps. */
@@ -191,8 +191,8 @@
         mNavMode = mNavigationModeController.addListener(this);
         mNavigationModeController.addListener(this);
         mTaskbarDelegate = taskbarDelegate;
-        mTaskbarDelegate.setOverviewProxyService(overviewProxyService,
-                navigationBarA11yHelper, mSysUiFlagsContainer);
+        mTaskbarDelegate.setOverviewProxyService(commandQueue, overviewProxyService,
+                navigationBarA11yHelper, navigationModeController, sysUiFlagsContainer);
         mIsTablet = isTablet(mContext);
         mUserTracker = userTracker;
     }
@@ -251,17 +251,14 @@
 
     /** @return {@code true} if taskbar is enabled, false otherwise */
     private boolean initializeTaskbarIfNecessary() {
-        boolean isShowingTaskbar = mIsTablet && mNavMode == NAV_BAR_MODE_3BUTTON;
-        if (isShowingTaskbar) {
-            // Remove navigation bar when taskbar is showing, currently only for 3 button mode
+        if (mIsTablet) {
+            // Remove navigation bar when taskbar is showing
             removeNavigationBar(mContext.getDisplayId());
-            mCommandQueue.addCallback(mTaskbarDelegate);
             mTaskbarDelegate.init(mContext.getDisplayId());
         } else {
-            mCommandQueue.removeCallback(mTaskbarDelegate);
             mTaskbarDelegate.destroy();
         }
-        return isShowingTaskbar;
+        return mIsTablet;
     }
 
     @Override
@@ -332,7 +329,7 @@
             return;
         }
 
-        if (mIsTablet && mNavMode == NAV_BAR_MODE_3BUTTON) {
+        if (mIsTablet) {
             return;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 2d36de3..93388f9 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -79,7 +79,7 @@
 import com.android.systemui.navigationbar.buttons.RotationContextButton;
 import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
 import com.android.systemui.navigationbar.gestural.FloatingRotationButton;
-import com.android.systemui.navigationbar.gestural.RegionSamplingHelper;
+import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index fe24ecd..e0caf12 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -21,15 +21,22 @@
 
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BACK_DISABLED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
 
+import android.content.Context;
 import android.inputmethodservice.InputMethodService;
 import android.os.IBinder;
 import android.view.InsetsVisibilities;
+import android.view.View;
 
 import com.android.internal.view.AppearanceRegion;
+import com.android.systemui.Dependency;
 import com.android.systemui.model.SysUiState;
+import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.shared.recents.utilities.Utilities;
 import com.android.systemui.statusbar.CommandQueue;
@@ -38,33 +45,53 @@
 import javax.inject.Singleton;
 
 @Singleton
-public class TaskbarDelegate implements CommandQueue.Callbacks {
+public class TaskbarDelegate implements CommandQueue.Callbacks,
+        OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener {
 
+    private final EdgeBackGestureHandler mEdgeBackGestureHandler;
+
+    private CommandQueue mCommandQueue;
     private OverviewProxyService mOverviewProxyService;
     private NavigationBarA11yHelper mNavigationBarA11yHelper;
+    private NavigationModeController mNavigationModeController;
     private SysUiState mSysUiState;
     private int mDisplayId;
     private int mNavigationIconHints;
     private final NavigationBarA11yHelper.NavA11yEventListener mNavA11yEventListener =
             this::updateSysuiFlags;
-    @Inject
-    public TaskbarDelegate() { /* no-op */ }
+    private int mDisabledFlags;
 
-    public void setOverviewProxyService(OverviewProxyService overviewProxyService,
+    @Inject
+    public TaskbarDelegate(Context context) {
+        mEdgeBackGestureHandler = Dependency.get(EdgeBackGestureHandler.Factory.class)
+                .create(context);
+    }
+
+    public void setOverviewProxyService(CommandQueue commandQueue,
+            OverviewProxyService overviewProxyService,
             NavigationBarA11yHelper navigationBarA11yHelper,
+            NavigationModeController navigationModeController,
             SysUiState sysUiState) {
         // TODO: adding this in the ctor results in a dagger dependency cycle :(
+        mCommandQueue = commandQueue;
         mOverviewProxyService = overviewProxyService;
         mNavigationBarA11yHelper = navigationBarA11yHelper;
+        mNavigationModeController = navigationModeController;
         mSysUiState = sysUiState;
     }
 
     public void destroy() {
+        mCommandQueue.removeCallback(this);
+        mOverviewProxyService.removeCallback(this);
+        mNavigationModeController.removeListener(this);
         mNavigationBarA11yHelper.removeA11yEventListener(mNavA11yEventListener);
     }
 
     public void init(int displayId) {
         mDisplayId = displayId;
+        mCommandQueue.addCallback(this);
+        mOverviewProxyService.addCallback(this);
+        mNavigationModeController.addListener(this);
         mNavigationBarA11yHelper.registerA11yEventListener(mNavA11yEventListener);
         // Set initial state for any listeners
         updateSysuiFlags();
@@ -81,6 +108,12 @@
                         (mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0)
                 .setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING,
                         (mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0)
+                .setFlag(SYSUI_STATE_OVERVIEW_DISABLED,
+                        (mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0)
+                .setFlag(SYSUI_STATE_HOME_DISABLED,
+                        (mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0)
+                .setFlag(SYSUI_STATE_BACK_DISABLED,
+                        (mDisabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0)
                 .commitUpdate(mDisplayId);
     }
 
@@ -103,6 +136,8 @@
 
     @Override
     public void disable(int displayId, int state1, int state2, boolean animate) {
+        mDisabledFlags = state1;
+        updateSysuiFlags();
         mOverviewProxyService.disable(displayId, state1, state2, animate);
     }
 
@@ -112,4 +147,18 @@
             InsetsVisibilities requestedVisibilities, String packageName) {
         mOverviewProxyService.onSystemBarAttributesChanged(displayId, behavior);
     }
+
+    @Override
+    public void onTaskbarStatusUpdated(boolean visible, boolean stashed) {
+        if (visible) {
+            mEdgeBackGestureHandler.onNavBarAttached();
+        } else {
+            mEdgeBackGestureHandler.onNavBarDetached();
+        }
+    }
+
+    @Override
+    public void onNavigationModeChanged(int mode) {
+        mEdgeBackGestureHandler.onNavigationModeChanged(mode);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
index ea04cef..8d1dfc8 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
@@ -55,6 +55,7 @@
 import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
 import com.android.systemui.plugins.NavigationEdgeBackPlugin;
+import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
 import com.android.systemui.statusbar.VibratorHelper;
 
 import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java
index ad1e21d..0b565ea 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java
@@ -17,25 +17,27 @@
 import android.util.ArrayMap;
 
 import com.android.systemui.Dependency;
-import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.plugins.PluginDependency.DependencyProvider;
 import com.android.systemui.shared.plugins.PluginManager;
 
 import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import dagger.Lazy;
 
 /**
  */
-@SysUISingleton
+@Singleton
 public class PluginDependencyProvider extends DependencyProvider {
 
     private final ArrayMap<Class<?>, Object> mDependencies = new ArrayMap<>();
-    private final PluginManager mManager;
+    private final Lazy<PluginManager> mManagerLazy;
 
     /**
      */
     @Inject
-    public PluginDependencyProvider(PluginManager manager) {
-        mManager = manager;
+    public PluginDependencyProvider(Lazy<PluginManager> managerLazy) {
+        mManagerLazy = managerLazy;
         PluginDependency.sProvider = this;
     }
 
@@ -51,7 +53,7 @@
 
     @Override
     <T> T get(Plugin p, Class<T> cls) {
-        if (!mManager.dependsOn(p, cls)) {
+        if (!mManagerLazy.get().dependsOn(p, cls)) {
             throw new IllegalArgumentException(p.getClass() + " does not depend on " + cls);
         }
         synchronized (mDependencies) {
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java
index 6337415..40f59744 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginEnablerImpl.java
@@ -22,16 +22,22 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.shared.plugins.PluginEnabler;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** */
+@Singleton
 public class PluginEnablerImpl implements PluginEnabler {
     private static final String CRASH_DISABLED_PLUGINS_PREF_FILE = "auto_disabled_plugins_prefs";
 
-    private PackageManager mPm;
+    private final PackageManager mPm;
     private final SharedPreferences mAutoDisabledPrefs;
 
     public PluginEnablerImpl(Context context) {
         this(context, context.getPackageManager());
     }
 
+    @Inject
     @VisibleForTesting public PluginEnablerImpl(Context context, PackageManager pm) {
         mAutoDisabledPrefs = context.getSharedPreferences(
                 CRASH_DISABLED_PLUGINS_PREF_FILE, Context.MODE_PRIVATE);
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java
index 7f01d6f..654d000 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java
@@ -15,16 +15,17 @@
 package com.android.systemui.plugins;
 
 import android.content.Context;
-import android.os.Build;
-import android.os.Looper;
 import android.util.Log;
 
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.shared.plugins.PluginEnabler;
 import com.android.systemui.shared.plugins.PluginInitializer;
 import com.android.systemui.shared.plugins.PluginManagerImpl;
 
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** */
+@Singleton
 public class PluginInitializerImpl implements PluginInitializer {
 
     /**
@@ -33,44 +34,24 @@
     private static final boolean WTFS_SHOULD_CRASH = false;
     private boolean mWtfsSet;
 
-    @Override
-    public Looper getBgLooper() {
-        return Dependency.get(Dependency.BG_LOOPER);
+    @Inject
+    public PluginInitializerImpl(PluginDependencyProvider  dependencyProvider) {
+        dependencyProvider.allowPluginDependency(ActivityStarter.class);
     }
 
     @Override
-    public void onPluginManagerInit() {
-        // Plugin dependencies that don't have another good home can go here, but
-        // dependencies that have better places to init can happen elsewhere.
-        Dependency.get(PluginDependencyProvider.class)
-                .allowPluginDependency(ActivityStarter.class);
-    }
-
-    @Override
-    public String[] getWhitelistedPlugins(Context context) {
+    public String[] getPrivilegedPlugins(Context context) {
         return context.getResources().getStringArray(R.array.config_pluginWhitelist);
     }
 
-    public PluginEnabler getPluginEnabler(Context context) {
-        return new PluginEnablerImpl(context);
-    }
 
     @Override
     public void handleWtfs() {
         if (WTFS_SHOULD_CRASH && !mWtfsSet) {
             mWtfsSet = true;
-            Log.setWtfHandler(new Log.TerribleFailureHandler() {
-                @Override
-                public void onTerribleFailure(String tag, Log.TerribleFailure what,
-                        boolean system) {
-                    throw new PluginManagerImpl.CrashWhilePluginActiveException(what);
-                }
+            Log.setWtfHandler((tag, what, system) -> {
+                throw new PluginManagerImpl.CrashWhilePluginActiveException(what);
             });
         }
     }
-
-    @Override
-    public boolean isDebuggable() {
-        return Build.IS_DEBUGGABLE;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java
new file mode 100644
index 0000000..1ea9b3c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java
@@ -0,0 +1,116 @@
+/*
+ * 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.plugins;
+
+import static com.android.systemui.util.concurrency.GlobalConcurrencyModule.PRE_HANDLER;
+
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Build;
+
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.shared.plugins.PluginEnabler;
+import com.android.systemui.shared.plugins.PluginInitializer;
+import com.android.systemui.shared.plugins.PluginInstanceManager;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManagerImpl;
+import com.android.systemui.shared.plugins.PluginPrefs;
+import com.android.systemui.util.concurrency.GlobalConcurrencyModule;
+import com.android.systemui.util.concurrency.ThreadFactory;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.Executor;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Dagger Module for code related to plugins.
+ *
+ * Covers code both in com.android.systemui.plugins and code in
+ * com.android.systemui.shared.plugins.
+ */
+@Module(includes = {GlobalConcurrencyModule.class})
+public abstract class PluginsModule {
+    public static final String PLUGIN_THREAD = "plugin_thread";
+    public static final String PLUGIN_DEBUG = "plugin_debug";
+    public static final String PLUGIN_PRIVILEGED = "plugin_privileged";
+
+    @Provides
+    @Named(PLUGIN_DEBUG)
+    static boolean providesPluginDebug() {
+        return Build.IS_DEBUGGABLE;
+    }
+
+    @Binds
+    abstract PluginEnabler bindsPluginEnablerImpl(PluginEnablerImpl impl);
+
+    @Binds
+    abstract PluginInitializer bindsPluginInitializerImpl(PluginInitializerImpl impl);
+
+    @Provides
+    @Singleton
+    static PluginInstanceManager.Factory providePluginInstanceManagerFactory(Context context,
+            PackageManager packageManager, @Main Executor mainExecutor,
+            @Named(PLUGIN_THREAD) Executor pluginExecutor, PluginInitializer initializer,
+            NotificationManager notificationManager, PluginEnabler pluginEnabler,
+            @Named(PLUGIN_PRIVILEGED) List<String> privilegedPlugins) {
+        return new PluginInstanceManager.Factory(
+                context, packageManager, mainExecutor, pluginExecutor, initializer,
+                notificationManager, pluginEnabler, privilegedPlugins);
+    }
+
+    @Provides
+    @Singleton
+    @Named(PLUGIN_THREAD)
+    static Executor providesPluginExecutor(ThreadFactory threadFactory) {
+        return threadFactory.buildExecutorOnNewThread("plugin");
+    }
+
+    @Provides
+    static PluginManager providesPluginManager(
+            Context context,
+            PluginInstanceManager.Factory instanceManagerFactory,
+            @Named(PLUGIN_DEBUG) boolean debug,
+            @Named(PRE_HANDLER)
+                    Optional<Thread.UncaughtExceptionHandler> uncaughtExceptionHandlerOptional,
+            PluginEnabler pluginEnabler,
+            PluginPrefs pluginPrefs,
+            @Named(PLUGIN_PRIVILEGED) List<String> privilegedPlugins) {
+        return new PluginManagerImpl(context, instanceManagerFactory, debug,
+                uncaughtExceptionHandlerOptional, pluginEnabler, pluginPrefs,
+                privilegedPlugins);
+    }
+
+    @Provides
+    static PluginPrefs providesPluginPrefs(Context context) {
+        return new PluginPrefs(context);
+    }
+
+    @Provides
+    @Named(PLUGIN_PRIVILEGED)
+    static List<String> providesPrivilegedPlugins(PluginInitializer initializer, Context context) {
+        return Arrays.asList(initializer.getPrivilegedPlugins(context));
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
deleted file mode 100644
index 38b20ee..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.qs;
-
-import static com.android.systemui.statusbar.phone.AutoTileManager.HOTSPOT;
-import static com.android.systemui.statusbar.phone.AutoTileManager.INVERSION;
-import static com.android.systemui.statusbar.phone.AutoTileManager.NIGHT;
-import static com.android.systemui.statusbar.phone.AutoTileManager.SAVER;
-import static com.android.systemui.statusbar.phone.AutoTileManager.WORK;
-
-import android.content.Context;
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.provider.Settings.Secure;
-import android.text.TextUtils;
-import android.util.ArraySet;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Prefs;
-import com.android.systemui.Prefs.Key;
-import com.android.systemui.util.UserAwareController;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-
-import javax.inject.Inject;
-
-public class AutoAddTracker implements UserAwareController {
-
-    private static final String[][] CONVERT_PREFS = {
-            {Key.QS_HOTSPOT_ADDED, HOTSPOT},
-            {Key.QS_DATA_SAVER_ADDED, SAVER},
-            {Key.QS_INVERT_COLORS_ADDED, INVERSION},
-            {Key.QS_WORK_ADDED, WORK},
-            {Key.QS_NIGHTDISPLAY_ADDED, NIGHT},
-    };
-
-    private final ArraySet<String> mAutoAdded;
-    private final Context mContext;
-    private int mUserId;
-
-    public AutoAddTracker(Context context, int userId) {
-        mContext = context;
-        mUserId = userId;
-        mAutoAdded = new ArraySet<>(getAdded());
-    }
-
-    /**
-     * Init method must be called after construction to start listening
-     */
-    public void initialize() {
-        // TODO: remove migration code and shared preferences keys after P release
-        if (mUserId == UserHandle.USER_SYSTEM) {
-            for (String[] convertPref : CONVERT_PREFS) {
-                if (Prefs.getBoolean(mContext, convertPref[0], false)) {
-                    setTileAdded(convertPref[1]);
-                    Prefs.remove(mContext, convertPref[0]);
-                }
-            }
-        }
-        mContext.getContentResolver().registerContentObserver(
-                Secure.getUriFor(Secure.QS_AUTO_ADDED_TILES), false, mObserver,
-                UserHandle.USER_ALL);
-    }
-
-    @Override
-    public void changeUser(UserHandle newUser) {
-        if (newUser.getIdentifier() == mUserId) {
-            return;
-        }
-        mUserId = newUser.getIdentifier();
-        mAutoAdded.clear();
-        mAutoAdded.addAll(getAdded());
-    }
-
-    @Override
-    public int getCurrentUserId() {
-        return mUserId;
-    }
-
-    public boolean isAdded(String tile) {
-        return mAutoAdded.contains(tile);
-    }
-
-    public void setTileAdded(String tile) {
-        if (mAutoAdded.add(tile)) {
-            saveTiles();
-        }
-    }
-
-    public void setTileRemoved(String tile) {
-        if (mAutoAdded.remove(tile)) {
-            saveTiles();
-        }
-    }
-
-    public void destroy() {
-        mContext.getContentResolver().unregisterContentObserver(mObserver);
-    }
-
-    private void saveTiles() {
-        Secure.putStringForUser(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES,
-                TextUtils.join(",", mAutoAdded), mUserId);
-    }
-
-    private Collection<String> getAdded() {
-        String current = Secure.getStringForUser(mContext.getContentResolver(),
-                Secure.QS_AUTO_ADDED_TILES, mUserId);
-        if (current == null) {
-            return Collections.emptyList();
-        }
-        return Arrays.asList(current.split(","));
-    }
-
-    @VisibleForTesting
-    protected final ContentObserver mObserver = new ContentObserver(new Handler()) {
-        @Override
-        public void onChange(boolean selfChange) {
-            mAutoAdded.clear();
-            mAutoAdded.addAll(getAdded());
-        }
-    };
-
-    public static class Builder {
-        private final Context mContext;
-        private int mUserId;
-
-        @Inject
-        public Builder(Context context) {
-            mContext = context;
-        }
-
-        public Builder setUserId(int userId) {
-            mUserId = userId;
-            return this;
-        }
-
-        public AutoAddTracker build() {
-            return new AutoAddTracker(mContext, mUserId);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
new file mode 100644
index 0000000..7ffa9d9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.Handler
+import android.os.UserHandle
+import android.provider.Settings
+import android.text.TextUtils
+import android.util.ArraySet
+import android.util.Log
+import androidx.annotation.GuardedBy
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.Dumpable
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.UserAwareController
+import com.android.systemui.util.settings.SecureSettings
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import java.util.concurrent.Executor
+import javax.inject.Inject
+
+private const val TAG = "AutoAddTracker"
+
+/**
+ * Class to track tiles that have been auto-added
+ *
+ * The list is backed by [Settings.Secure.QS_AUTO_ADDED_TILES].
+ *
+ * It also handles restore gracefully.
+ */
+class AutoAddTracker @VisibleForTesting constructor(
+    private val secureSettings: SecureSettings,
+    private val broadcastDispatcher: BroadcastDispatcher,
+    private val qsHost: QSHost,
+    private val dumpManager: DumpManager,
+    private val mainHandler: Handler?,
+    private val backgroundExecutor: Executor,
+    private var userId: Int
+) : UserAwareController, Dumpable {
+
+    companion object {
+        private val FILTER = IntentFilter(Intent.ACTION_SETTING_RESTORED)
+    }
+
+    @GuardedBy("autoAdded")
+    private val autoAdded = ArraySet<String>()
+    private var restoredTiles: Set<String>? = null
+
+    override val currentUserId: Int
+        get() = userId
+
+    private val contentObserver = object : ContentObserver(mainHandler) {
+        override fun onChange(
+            selfChange: Boolean,
+            uris: Collection<Uri>,
+            flags: Int,
+            _userId: Int
+        ) {
+            if (_userId != userId) {
+                // Ignore changes outside of our user. We'll load the correct value on user change
+                return
+            }
+            loadTiles()
+        }
+    }
+
+    private val restoreReceiver = object : BroadcastReceiver() {
+        override fun onReceive(context: Context, intent: Intent) {
+            if (intent.action != Intent.ACTION_SETTING_RESTORED) return
+            processRestoreIntent(intent)
+        }
+    }
+
+    private fun processRestoreIntent(intent: Intent) {
+        when (intent.getStringExtra(Intent.EXTRA_SETTING_NAME)) {
+            Settings.Secure.QS_TILES -> {
+                restoredTiles = intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)
+                        ?.split(",")
+                        ?.toSet()
+                        ?: run {
+                            Log.w(TAG, "Null restored tiles for user $userId")
+                            emptySet()
+                        }
+            }
+            Settings.Secure.QS_AUTO_ADDED_TILES -> {
+                restoredTiles?.let { tiles ->
+                    val restoredAutoAdded = intent
+                            .getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)
+                            ?.split(",")
+                            ?: emptyList()
+                    val autoAddedBeforeRestore = intent
+                            .getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE)
+                            ?.split(",")
+                            ?: emptyList()
+
+                    val tilesToRemove = restoredAutoAdded.filter { it !in tiles }
+                    if (tilesToRemove.isNotEmpty()) {
+                        qsHost.removeTiles(tilesToRemove)
+                    }
+                    val tiles = synchronized(autoAdded) {
+                        autoAdded.clear()
+                        autoAdded.addAll(restoredAutoAdded + autoAddedBeforeRestore)
+                        getTilesFromListLocked()
+                    }
+                    saveTiles(tiles)
+                } ?: run {
+                    Log.w(TAG, "${Settings.Secure.QS_AUTO_ADDED_TILES} restored before " +
+                            "${Settings.Secure.QS_TILES} for user $userId")
+                }
+            }
+            else -> {} // Do nothing for other Settings
+        }
+    }
+
+    /**
+     * Init method must be called after construction to start listening
+     */
+    fun initialize() {
+        dumpManager.registerDumpable(TAG, this)
+        loadTiles()
+        secureSettings.registerContentObserverForUser(
+                secureSettings.getUriFor(Settings.Secure.QS_AUTO_ADDED_TILES),
+                contentObserver,
+                UserHandle.USER_ALL
+        )
+        registerBroadcastReceiver()
+    }
+
+    /**
+     * Unregister listeners, receivers and observers
+     */
+    fun destroy() {
+        dumpManager.unregisterDumpable(TAG)
+        secureSettings.unregisterContentObserver(contentObserver)
+        unregisterBroadcastReceiver()
+    }
+
+    private fun registerBroadcastReceiver() {
+        broadcastDispatcher.registerReceiver(
+                restoreReceiver,
+                FILTER,
+                backgroundExecutor,
+                UserHandle.of(userId)
+        )
+    }
+
+    private fun unregisterBroadcastReceiver() {
+        broadcastDispatcher.unregisterReceiver(restoreReceiver)
+    }
+
+    override fun changeUser(newUser: UserHandle) {
+        if (newUser.identifier == userId) return
+        unregisterBroadcastReceiver()
+        userId = newUser.identifier
+        restoredTiles = null
+        loadTiles()
+        registerBroadcastReceiver()
+    }
+
+    /**
+     * Returns `true` if the tile has been auto-added before
+     */
+    fun isAdded(tile: String): Boolean {
+        return synchronized(autoAdded) {
+            tile in autoAdded
+        }
+    }
+
+    /**
+     * Sets a tile as auto-added.
+     *
+     * From here on, [isAdded] will return true for that tile.
+     */
+    fun setTileAdded(tile: String) {
+        val tiles = synchronized(autoAdded) {
+                if (autoAdded.add(tile)) {
+                    getTilesFromListLocked()
+                } else {
+                    null
+                }
+            }
+        tiles?.let { saveTiles(it) }
+    }
+
+    /**
+     * Removes a tile from the list of auto-added.
+     *
+     * This allows for this tile to be auto-added again in the future.
+     */
+    fun setTileRemoved(tile: String) {
+        val tiles = synchronized(autoAdded) {
+            if (autoAdded.remove(tile)) {
+                getTilesFromListLocked()
+            } else {
+                null
+            }
+        }
+        tiles?.let { saveTiles(it) }
+    }
+
+    private fun getTilesFromListLocked(): String {
+        return TextUtils.join(",", autoAdded)
+    }
+
+    private fun saveTiles(tiles: String) {
+        secureSettings.putStringForUser(
+                Settings.Secure.QS_AUTO_ADDED_TILES,
+                tiles,
+                /* tag */ null,
+                /* makeDefault */ false,
+                userId,
+                /* overrideableByRestore */ true
+        )
+    }
+
+    private fun loadTiles() {
+        synchronized(autoAdded) {
+            autoAdded.clear()
+            autoAdded.addAll(getAdded())
+        }
+    }
+
+    private fun getAdded(): Collection<String> {
+        val current = secureSettings.getStringForUser(Settings.Secure.QS_AUTO_ADDED_TILES, userId)
+        return current?.split(",") ?: emptySet()
+    }
+
+    override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+        pw.println("Current user: $userId")
+        pw.println("Added tiles: $autoAdded")
+    }
+
+    @SysUISingleton
+    class Builder @Inject constructor(
+        private val secureSettings: SecureSettings,
+        private val broadcastDispatcher: BroadcastDispatcher,
+        private val qsHost: QSHost,
+        private val dumpManager: DumpManager,
+        @Main private val handler: Handler,
+        @Background private val executor: Executor
+    ) {
+        private var userId: Int = 0
+
+        fun setUserId(_userId: Int): Builder {
+            userId = _userId
+            return this
+        }
+
+        fun build(): AutoAddTracker {
+            return AutoAddTracker(
+                    secureSettings,
+                    broadcastDispatcher,
+                    qsHost,
+                    dumpManager,
+                    handler,
+                    executor,
+                    userId
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
new file mode 100644
index 0000000..4f87cad
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs
+
+import android.content.Intent
+import android.os.UserManager
+import android.provider.Settings
+import android.view.View
+import android.widget.Toast
+import androidx.annotation.VisibleForTesting
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.UiEventLogger
+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
+import com.android.systemui.qs.FooterActionsController.ExpansionState.COLLAPSED
+import com.android.systemui.qs.FooterActionsController.ExpansionState.EXPANDED
+import com.android.systemui.qs.dagger.QSFlagsModule.PM_LITE_ENABLED
+import com.android.systemui.statusbar.phone.MultiUserSwitchController
+import com.android.systemui.statusbar.phone.SettingsButton
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.UserInfoController
+import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener
+import com.android.systemui.tuner.TunerService
+import com.android.systemui.util.ViewController
+import javax.inject.Inject
+import javax.inject.Named
+
+/**
+ * Manages [FooterActionsView] behaviour, both when it's placed in QS or QQS (split shade).
+ * Main difference between QS and QQS behaviour is condition when buttons should be visible,
+ * determined by [buttonsVisibleState]
+ */
+class FooterActionsController @Inject constructor(
+    view: FooterActionsView,
+    private val qsPanelController: QSPanelController,
+    private val activityStarter: ActivityStarter,
+    private val userManager: UserManager,
+    private val userInfoController: UserInfoController,
+    private val multiUserSwitchController: MultiUserSwitchController,
+    private val deviceProvisionedController: DeviceProvisionedController,
+    private val falsingManager: FalsingManager,
+    private val metricsLogger: MetricsLogger,
+    private val tunerService: TunerService,
+    private val globalActionsDialog: GlobalActionsDialogLite,
+    private val uiEventLogger: UiEventLogger,
+    @Named(PM_LITE_ENABLED) private val showPMLiteButton: Boolean,
+    private val buttonsVisibleState: ExpansionState
+) : ViewController<FooterActionsView>(view) {
+
+    enum class ExpansionState { COLLAPSED, EXPANDED }
+
+    private var listening: Boolean = false
+
+    var expanded = false
+        set(value) {
+            if (field != value) {
+                field = value
+                updateView()
+            }
+        }
+
+    private val settingsButton: SettingsButton = view.findViewById(R.id.settings_button)
+    private val settingsButtonContainer: View? = view.findViewById(R.id.settings_button_container)
+    private val editButton: View = view.findViewById(android.R.id.edit)
+    private val powerMenuLite: View = view.findViewById(R.id.pm_lite)
+
+    private val onUserInfoChangedListener = OnUserInfoChangedListener { _, picture, _ ->
+        val isGuestUser: Boolean = userManager.isGuestUser(KeyguardUpdateMonitor.getCurrentUser())
+        mView.onUserInfoChanged(picture, isGuestUser)
+    }
+
+    private val onClickListener = View.OnClickListener { v ->
+        // Don't do anything until views are unhidden. Don't do anything if the tap looks
+        // suspicious.
+        if (!buttonsVisible() || falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+            return@OnClickListener
+        }
+        if (v === settingsButton) {
+            if (!deviceProvisionedController.isCurrentUserSetup) {
+                // If user isn't setup just unlock the device and dump them back at SUW.
+                activityStarter.postQSRunnableDismissingKeyguard {}
+                return@OnClickListener
+            }
+            metricsLogger.action(
+                    if (expanded) MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH
+                    else MetricsProto.MetricsEvent.ACTION_QS_COLLAPSED_SETTINGS_LAUNCH)
+            if (settingsButton.isTunerClick) {
+                activityStarter.postQSRunnableDismissingKeyguard {
+                    if (isTunerEnabled()) {
+                        tunerService.showResetRequest {
+                            // Relaunch settings so that the tuner disappears.
+                            startSettingsActivity()
+                        }
+                    } else {
+                        Toast.makeText(context, R.string.tuner_toast, Toast.LENGTH_LONG).show()
+                        tunerService.isTunerEnabled = true
+                    }
+                    startSettingsActivity()
+                }
+            } else {
+                startSettingsActivity()
+            }
+        } else if (v === powerMenuLite) {
+            uiEventLogger.log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS)
+            globalActionsDialog.showOrHideDialog(false, true)
+        }
+    }
+
+    private fun buttonsVisible(): Boolean {
+        return when (buttonsVisibleState) {
+            EXPANDED -> expanded
+            COLLAPSED -> !expanded
+        }
+    }
+
+    override fun onInit() {
+        multiUserSwitchController.init()
+    }
+
+    fun hideFooter() {
+        mView.visibility = View.GONE
+    }
+
+    fun showFooter() {
+        mView.visibility = View.VISIBLE
+        updateView()
+    }
+
+    private fun startSettingsActivity() {
+        val animationController = settingsButtonContainer?.let {
+            ActivityLaunchAnimator.Controller.fromView(
+                    it,
+                    InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON)
+            }
+        activityStarter.startActivity(Intent(Settings.ACTION_SETTINGS),
+                true /* dismissShade */, animationController)
+    }
+
+    @VisibleForTesting
+    public override fun onViewAttached() {
+        if (showPMLiteButton) {
+            powerMenuLite.visibility = View.VISIBLE
+            powerMenuLite.setOnClickListener(onClickListener)
+        } else {
+            powerMenuLite.visibility = View.GONE
+        }
+        settingsButton.setOnClickListener(onClickListener)
+        editButton.setOnClickListener(View.OnClickListener { view: View? ->
+            if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+                return@OnClickListener
+            }
+            activityStarter.postQSRunnableDismissingKeyguard { qsPanelController.showEdit(view) }
+        })
+
+        updateView()
+    }
+
+    private fun updateView() {
+        mView.updateEverything(buttonsVisible(), isTunerEnabled(),
+                multiUserSwitchController.isMultiUserEnabled)
+    }
+
+    override fun onViewDetached() {
+        setListening(false)
+    }
+
+    fun setListening(listening: Boolean) {
+        if (this.listening == listening) {
+            return
+        }
+        this.listening = listening
+        if (this.listening) {
+            userInfoController.addCallback(onUserInfoChangedListener)
+        } else {
+            userInfoController.removeCallback(onUserInfoChangedListener)
+        }
+    }
+
+    fun disable(state2: Int) {
+        mView.disable(buttonsVisible(), state2, isTunerEnabled(),
+                multiUserSwitchController.isMultiUserEnabled)
+    }
+
+    fun setExpansion(headerExpansionFraction: Float) {
+        mView.setExpansion(headerExpansionFraction)
+    }
+
+    fun updateAnimator(width: Int, numTiles: Int) {
+        mView.updateAnimator(width, numTiles)
+    }
+
+    fun setKeyguardShowing() {
+        mView.setKeyguardShowing()
+    }
+
+    fun refreshVisibility(shouldBeVisible: Boolean) {
+        if (shouldBeVisible) {
+            showFooter()
+        } else {
+            hideFooter()
+        }
+    }
+
+    private fun isTunerEnabled() = tunerService.isTunerEnabled
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt
new file mode 100644
index 0000000..fcfa72a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.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.qs
+
+import android.os.UserManager
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.globalactions.GlobalActionsDialogLite
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.qs.FooterActionsController.ExpansionState
+import com.android.systemui.qs.dagger.QSFlagsModule
+import com.android.systemui.statusbar.phone.MultiUserSwitchController
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.UserInfoController
+import com.android.systemui.tuner.TunerService
+import javax.inject.Inject
+import javax.inject.Named
+
+class FooterActionsControllerBuilder @Inject constructor(
+    private val qsPanelController: QSPanelController,
+    private val activityStarter: ActivityStarter,
+    private val userManager: UserManager,
+    private val userInfoController: UserInfoController,
+    private val multiUserSwitchController: MultiUserSwitchController,
+    private val deviceProvisionedController: DeviceProvisionedController,
+    private val falsingManager: FalsingManager,
+    private val metricsLogger: MetricsLogger,
+    private val tunerService: TunerService,
+    private val globalActionsDialog: GlobalActionsDialogLite,
+    private val uiEventLogger: UiEventLogger,
+    @Named(QSFlagsModule.PM_LITE_ENABLED) private val showPMLiteButton: Boolean
+) {
+    private lateinit var view: FooterActionsView
+    private lateinit var buttonsVisibleState: ExpansionState
+
+    fun withView(view: FooterActionsView): FooterActionsControllerBuilder {
+        this.view = view
+        return this
+    }
+
+    fun withButtonsVisibleWhen(state: ExpansionState): FooterActionsControllerBuilder {
+        buttonsVisibleState = state
+        return this
+    }
+
+    fun build(): FooterActionsController {
+        return FooterActionsController(view, qsPanelController, activityStarter, userManager,
+                userInfoController, multiUserSwitchController, deviceProvisionedController,
+                falsingManager, metricsLogger, tunerService, globalActionsDialog, uiEventLogger,
+                showPMLiteButton, buttonsVisibleState)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
new file mode 100644
index 0000000..941e54a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.qs
+
+import android.app.StatusBarManager
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.PorterDuff
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.RippleDrawable
+import android.os.UserManager
+import android.util.AttributeSet
+import android.view.View
+import android.widget.ImageView
+import android.widget.LinearLayout
+import com.android.settingslib.Utils
+import com.android.settingslib.drawable.UserIconDrawable
+import com.android.systemui.R
+import com.android.systemui.statusbar.phone.MultiUserSwitch
+import com.android.systemui.statusbar.phone.SettingsButton
+
+/**
+ * Quick Settings bottom buttons placed in footer (aka utility bar) - always visible in expanded QS,
+ * in split shade mode visible also in collapsed state. May contain up to 5 buttons: settings,
+ * edit tiles, power off and conditionally: user switch and tuner
+ */
+class FooterActionsView(context: Context?, attrs: AttributeSet?) : LinearLayout(context, attrs) {
+    private lateinit var settingsContainer: View
+    private lateinit var settingsButton: SettingsButton
+    private lateinit var multiUserSwitch: MultiUserSwitch
+    private lateinit var multiUserAvatar: ImageView
+    private lateinit var tunerIcon: View
+    private lateinit var editTilesButton: View
+
+    private var settingsCogAnimator: TouchAnimator? = null
+
+    private var qsDisabled = false
+    private var expansionAmount = 0f
+
+    override fun onFinishInflate() {
+        super.onFinishInflate()
+        editTilesButton = requireViewById(android.R.id.edit)
+        settingsButton = findViewById(R.id.settings_button)
+        settingsContainer = findViewById(R.id.settings_button_container)
+        multiUserSwitch = findViewById(R.id.multi_user_switch)
+        multiUserAvatar = multiUserSwitch.findViewById(R.id.multi_user_avatar)
+        tunerIcon = requireViewById(R.id.tuner_icon)
+
+        // RenderThread is doing more harm than good when touching the header (to expand quick
+        // settings), so disable it for this view
+        if (settingsButton.background is RippleDrawable) {
+            (settingsButton.background as RippleDrawable).setForceSoftware(true)
+        }
+        updateResources()
+        importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
+    }
+
+    fun updateAnimator(width: Int, numTiles: Int) {
+        val size = (mContext.resources.getDimensionPixelSize(R.dimen.qs_quick_tile_size) -
+                mContext.resources.getDimensionPixelSize(R.dimen.qs_tile_padding))
+        val remaining = (width - numTiles * size) / (numTiles - 1)
+        val defSpace = mContext.resources.getDimensionPixelOffset(R.dimen.default_gear_space)
+        val translation = if (isLayoutRtl) (remaining - defSpace) else -(remaining - defSpace)
+        settingsCogAnimator = TouchAnimator.Builder()
+                .addFloat(settingsButton, "translationX", translation.toFloat(), 0f)
+                .addFloat(settingsButton, "rotation", -120f, 0f)
+                .build()
+        setExpansion(expansionAmount)
+    }
+
+    override fun onConfigurationChanged(newConfig: Configuration) {
+        super.onConfigurationChanged(newConfig)
+        updateResources()
+    }
+
+    override fun onRtlPropertiesChanged(layoutDirection: Int) {
+        super.onRtlPropertiesChanged(layoutDirection)
+        updateResources()
+    }
+
+    private fun updateResources() {
+        val tunerIconTranslation = mContext.resources
+                .getDimensionPixelOffset(R.dimen.qs_footer_tuner_icon_translation).toFloat()
+        tunerIcon.translationX = if (isLayoutRtl) (-tunerIconTranslation) else tunerIconTranslation
+    }
+
+    fun setKeyguardShowing() {
+        setExpansion(expansionAmount)
+    }
+
+    fun setExpansion(headerExpansionFraction: Float) {
+        expansionAmount = headerExpansionFraction
+        if (settingsCogAnimator != null) settingsCogAnimator!!.setPosition(headerExpansionFraction)
+    }
+
+    fun disable(
+        buttonsVisible: Boolean,
+        state2: Int,
+        isTunerEnabled: Boolean,
+        multiUserEnabled: Boolean
+    ) {
+        val disabled = state2 and StatusBarManager.DISABLE2_QUICK_SETTINGS != 0
+        if (disabled == qsDisabled) return
+        qsDisabled = disabled
+        updateEverything(buttonsVisible, isTunerEnabled, multiUserEnabled)
+    }
+
+    fun updateEverything(
+        buttonsVisible: Boolean,
+        isTunerEnabled: Boolean,
+        multiUserEnabled: Boolean
+    ) {
+        post {
+            updateVisibilities(buttonsVisible, isTunerEnabled, multiUserEnabled)
+            updateClickabilities()
+            isClickable = false
+        }
+    }
+
+    private fun updateClickabilities() {
+        multiUserSwitch.isClickable = multiUserSwitch.visibility == VISIBLE
+        editTilesButton.isClickable = editTilesButton.visibility == VISIBLE
+        settingsButton.isClickable = settingsButton.visibility == VISIBLE
+    }
+
+    private fun updateVisibilities(
+        buttonsVisible: Boolean,
+        isTunerEnabled: Boolean,
+        multiUserEnabled: Boolean
+    ) {
+        settingsContainer.visibility = if (qsDisabled) GONE else VISIBLE
+        tunerIcon.visibility = if (isTunerEnabled) VISIBLE else INVISIBLE
+        multiUserSwitch.visibility = if (buttonsVisible && multiUserEnabled) VISIBLE else GONE
+        val isDemo = UserManager.isDeviceInDemoMode(context)
+        settingsButton.visibility = if (isDemo || !buttonsVisible) INVISIBLE else VISIBLE
+    }
+
+    fun onUserInfoChanged(picture: Drawable?, isGuestUser: Boolean) {
+        var pictureToSet = picture
+        if (picture != null && isGuestUser && picture !is UserIconDrawable) {
+            pictureToSet = picture.constantState.newDrawable(resources).mutate()
+            pictureToSet.setColorFilter(
+                    Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorForeground),
+                    PorterDuff.Mode.SRC_IN)
+        }
+        multiUserAvatar.setImageDrawable(pictureToSet)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index ce1066e..1bd3664 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -268,7 +268,7 @@
     }
 
     @Override
-    public void setExpansion(float expansion) {
+    public void setExpansion(float expansion, float proposedTranslation) {
         mLastExpansion = expansion;
         updateSelected();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 4fcd46c..8659b8b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -14,6 +14,9 @@
 
 package com.android.systemui.qs;
 
+import static com.android.systemui.qs.dagger.QSFragmentModule.QQS_FOOTER;
+import static com.android.systemui.qs.dagger.QSFragmentModule.QS_FOOTER;
+
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.util.Log;
@@ -43,6 +46,7 @@
 import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
+import javax.inject.Named;
 
 /** */
 @QSScope
@@ -67,13 +71,15 @@
      * position to the normal QS panel. These views will only show once the animation is complete,
      * to prevent overlapping of semi transparent views
      */
-    private final ArrayList<View> mQuickQsViews = new ArrayList<>();
+    private final ArrayList<View> mAnimatedQsViews = new ArrayList<>();
     private final QuickQSPanel mQuickQsPanel;
     private final QSPanelController mQsPanelController;
     private final QuickQSPanelController mQuickQSPanelController;
     private final QuickStatusBarHeader mQuickStatusBarHeader;
     private final QSSecurityFooter mSecurityFooter;
     private final QS mQs;
+    private final View mQSFooterActions;
+    private final View mQQSFooterActions;
 
     private PagedTileLayout mPagedLayout;
 
@@ -88,6 +94,7 @@
     // This animates fading of SecurityFooter and media divider
     private TouchAnimator mAllPagesDelayedAnimator;
     private TouchAnimator mBrightnessAnimator;
+    private TouchAnimator mQQSFooterActionsAnimator;
     private HeightExpansionAnimator mQQSTileHeightAnimator;
     private HeightExpansionAnimator mOtherTilesExpandAnimator;
 
@@ -110,12 +117,16 @@
             QSPanelController qsPanelController,
             QuickQSPanelController quickQSPanelController, QSTileHost qsTileHost,
             QSSecurityFooter securityFooter, @Main Executor executor, TunerService tunerService,
-            QSExpansionPathInterpolator qsExpansionPathInterpolator) {
+            QSExpansionPathInterpolator qsExpansionPathInterpolator,
+            @Named(QS_FOOTER) FooterActionsView qsFooterActionsView,
+            @Named(QQS_FOOTER) FooterActionsView qqsFooterActionsView) {
         mQs = qs;
         mQuickQsPanel = quickPanel;
         mQsPanelController = qsPanelController;
         mQuickQSPanelController = quickQSPanelController;
         mQuickStatusBarHeader = quickStatusBarHeader;
+        mQQSFooterActions = qqsFooterActionsView;
+        mQSFooterActions = qsFooterActionsView;
         mSecurityFooter = securityFooter;
         mHost = qsTileHost;
         mExecutor = executor;
@@ -262,7 +273,7 @@
 
         clearAnimationState();
         mAllViews.clear();
-        mQuickQsViews.clear();
+        mAnimatedQsViews.clear();
         mQQSTileHeightAnimator = null;
         mOtherTilesExpandAnimator = null;
 
@@ -360,7 +371,7 @@
 
                     firstPageBuilder.addFloat(quickTileView.getSecondaryLabel(), "alpha", 0, 1);
 
-                    mQuickQsViews.add(tileView);
+                    mAnimatedQsViews.add(tileView);
                     mAllViews.add(quickTileView);
                     mAllViews.add(quickTileView.getSecondaryLabel());
                 } else if (mFullRows && isIconInAnimatedRow(count)) {
@@ -417,6 +428,13 @@
                     .addFloat(tileLayout, "alpha", 0, 1);
             mFirstPageDelayedAnimator = builder.build();
 
+            if (mQQSFooterActions.getVisibility() != View.GONE) {
+                // only when qqs footer is present (which means split shade mode) it needs to
+                // be animated
+                updateQQSFooterAnimation();
+            }
+
+
             // Fade in the security footer and the divider as we reach the final position
             builder = new Builder().setStartDelay(EXPANDED_TILE_DELAY);
             builder.addFloat(mSecurityFooter.getView(), "alpha", 0, 1);
@@ -452,6 +470,20 @@
                 .addFloat(tileLayout, "alpha", 0, 1).build();
     }
 
+    private void updateQQSFooterAnimation() {
+        int[] qsPosition = new int[2];
+        int[] qqsPosition = new int[2];
+        View commonView = mQs.getView();
+        getRelativePositionInt(qsPosition, mQSFooterActions, commonView);
+        getRelativePositionInt(qqsPosition, mQQSFooterActions, commonView);
+        int translationY = (qsPosition[1] - qqsPosition[1])
+                - mQuickStatusBarHeader.getOffsetTranslation();
+        mQQSFooterActionsAnimator = new TouchAnimator.Builder()
+                .addFloat(mQQSFooterActions, "translationY", 0, translationY)
+                .build();
+        mAnimatedQsViews.add(mQSFooterActions);
+    }
+
     private boolean isIconInAnimatedRow(int count) {
         if (mPagedLayout == null) {
             return false;
@@ -521,6 +553,9 @@
             if (mBrightnessAnimator != null) {
                 mBrightnessAnimator.setPosition(position);
             }
+            if (mQQSFooterActionsAnimator != null) {
+                mQQSFooterActionsAnimator.setPosition(position);
+            }
         }
     }
 
@@ -532,9 +567,9 @@
     @Override
     public void onAnimationAtEnd() {
         mQuickQsPanel.setVisibility(View.INVISIBLE);
-        final int N = mQuickQsViews.size();
+        final int N = mAnimatedQsViews.size();
         for (int i = 0; i < N; i++) {
-            mQuickQsViews.get(i).setVisibility(View.VISIBLE);
+            mAnimatedQsViews.get(i).setVisibility(View.VISIBLE);
         }
     }
 
@@ -542,9 +577,9 @@
     public void onAnimationStarted() {
         updateQQSVisibility();
         if (mOnFirstPage) {
-            final int N = mQuickQsViews.size();
+            final int N = mAnimatedQsViews.size();
             for (int i = 0; i < N; i++) {
-                mQuickQsViews.get(i).setVisibility(View.INVISIBLE);
+                mAnimatedQsViews.get(i).setVisibility(View.INVISIBLE);
             }
         }
     }
@@ -569,9 +604,9 @@
         if (mOtherTilesExpandAnimator != null) {
             mOtherTilesExpandAnimator.resetViewsHeights();
         }
-        final int N2 = mQuickQsViews.size();
+        final int N2 = mAnimatedQsViews.size();
         for (int i = 0; i < N2; i++) {
-            mQuickQsViews.get(i).setVisibility(View.VISIBLE);
+            mAnimatedQsViews.get(i).setVisibility(View.VISIBLE);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index adcaf5d..a128694 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -29,13 +29,17 @@
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 
+import com.android.systemui.Dumpable;
 import com.android.systemui.R;
 import com.android.systemui.qs.customize.QSCustomizer;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
 /**
  * Wrapper view with background which contains {@link QSPanel} and {@link QuickStatusBarHeader}
  */
-public class QSContainerImpl extends FrameLayout {
+public class QSContainerImpl extends FrameLayout implements Dumpable {
 
     private final Point mSizePoint = new Point();
     private int mFancyClippingTop;
@@ -302,4 +306,11 @@
                 mFancyClippingBottom, mFancyClippingRadii, Path.Direction.CW);
         invalidate();
     }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println(getClass().getSimpleName() + " updateClippingPath: top("
+                + mFancyClippingTop + ") bottom(" + mFancyClippingBottom  + ") mClippingEnabled("
+                + mClippingEnabled + ")");
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index e38bd4b..0e0681b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -35,11 +35,6 @@
     void setExpanded(boolean expanded);
 
     /**
-     * Returns the full height of the footer.
-     */
-    int getHeight();
-
-    /**
      * Sets the percentage amount that the quick settings has been expanded.
      *
      * @param expansion A value from 1 to 0 that indicates how much the quick settings have been
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
index 57438d1..4d23958 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
@@ -21,60 +21,40 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.RippleDrawable;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.UserHandle;
-import android.os.UserManager;
 import android.provider.Settings;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.FrameLayout;
-import android.widget.ImageView;
 import android.widget.TextView;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
-import com.android.settingslib.Utils;
 import com.android.settingslib.development.DevelopmentSettingsEnabler;
-import com.android.settingslib.drawable.UserIconDrawable;
 import com.android.systemui.R;
-import com.android.systemui.qs.TouchAnimator.Builder;
-import com.android.systemui.statusbar.phone.MultiUserSwitch;
-import com.android.systemui.statusbar.phone.SettingsButton;
 
-/** */
+/**
+ * Footer of expanded Quick Settings, tiles page indicator, (optionally) build number and
+ * {@link FooterActionsView}
+ */
 public class QSFooterView extends FrameLayout {
-    private SettingsButton mSettingsButton;
-    protected View mSettingsContainer;
     private PageIndicator mPageIndicator;
     private TextView mBuildText;
-    private boolean mShouldShowBuildText;
-
-    private boolean mQsDisabled;
-
-    private boolean mExpanded;
-
-    private boolean mListening;
-
-    protected MultiUserSwitch mMultiUserSwitch;
-    private ImageView mMultiUserAvatar;
+    private View mActionsContainer;
 
     protected TouchAnimator mFooterAnimator;
+
+    private boolean mQsDisabled;
+    private boolean mExpanded;
     private float mExpansionAmount;
 
-    protected View mEdit;
-    private TouchAnimator mSettingsCogAnimator;
-
-    private View mActionsContainer;
-    private View mTunerIcon;
-    private int mTunerIconTranslation;
+    private boolean mShouldShowBuildText;
 
     private OnClickListener mExpandClickListener;
 
@@ -94,27 +74,11 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mEdit = requireViewById(android.R.id.edit);
-
         mPageIndicator = findViewById(R.id.footer_page_indicator);
-
-        mSettingsButton = findViewById(R.id.settings_button);
-        mSettingsContainer = findViewById(R.id.settings_button_container);
-
-        mMultiUserSwitch = findViewById(R.id.multi_user_switch);
-        mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
-
-        mActionsContainer = requireViewById(R.id.qs_footer_actions_container);
+        mActionsContainer = requireViewById(R.id.qs_footer_actions);
         mBuildText = findViewById(R.id.build);
-        mTunerIcon = requireViewById(R.id.tuner_icon);
 
-        // RenderThread is doing more harm than good when touching the header (to expand quick
-        // settings), so disable it for this view
-        if (mSettingsButton.getBackground() instanceof RippleDrawable) {
-            ((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
-        }
         updateResources();
-
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
         setBuildText();
     }
@@ -137,18 +101,7 @@
         }
     }
 
-    void updateAnimator(int width, int numTiles) {
-        int size = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size)
-                - mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_padding);
-        int remaining = (width - numTiles * size) / (numTiles - 1);
-        int defSpace = mContext.getResources().getDimensionPixelOffset(R.dimen.default_gear_space);
-
-        mSettingsCogAnimator = new Builder()
-                .addFloat(mSettingsButton, "translationX",
-                        isLayoutRtl() ? (remaining - defSpace) : -(remaining - defSpace), 0)
-                .addFloat(mSettingsButton, "rotation", -120, 0)
-                .build();
-
+    void updateExpansion() {
         setExpansion(mExpansionAmount);
     }
 
@@ -158,20 +111,11 @@
         updateResources();
     }
 
-    @Override
-    public void onRtlPropertiesChanged(int layoutDirection) {
-        super.onRtlPropertiesChanged(layoutDirection);
-        updateResources();
-    }
-
     private void updateResources() {
         updateFooterAnimator();
         MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
         lp.bottomMargin = getResources().getDimensionPixelSize(R.dimen.qs_footers_margin_bottom);
         setLayoutParams(lp);
-        mTunerIconTranslation = mContext.getResources()
-                .getDimensionPixelOffset(R.dimen.qs_footer_tuner_icon_translation);
-        mTunerIcon.setTranslationX(isLayoutRtl() ? -mTunerIconTranslation : mTunerIconTranslation);
     }
 
     private void updateFooterAnimator() {
@@ -197,17 +141,15 @@
         mExpandClickListener = onClickListener;
     }
 
-    void setExpanded(boolean expanded, boolean isTunerEnabled, boolean multiUserEnabled) {
+    void setExpanded(boolean expanded) {
         if (mExpanded == expanded) return;
         mExpanded = expanded;
-        updateEverything(isTunerEnabled, multiUserEnabled);
+        updateEverything();
     }
 
     /** */
     public void setExpansion(float headerExpansionFraction) {
         mExpansionAmount = headerExpansionFraction;
-        if (mSettingsCogAnimator != null) mSettingsCogAnimator.setPosition(headerExpansionFraction);
-
         if (mFooterAnimator != null) {
             mFooterAnimator.setPosition(headerExpansionFraction);
         }
@@ -228,14 +170,6 @@
         super.onDetachedFromWindow();
     }
 
-    /** */
-    public void setListening(boolean listening) {
-        if (listening == mListening) {
-            return;
-        }
-        mListening = listening;
-    }
-
     @Override
     public boolean performAccessibilityAction(int action, Bundle arguments) {
         if (action == AccessibilityNodeInfo.ACTION_EXPAND) {
@@ -253,50 +187,26 @@
         info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
     }
 
-    void disable(int state2, boolean isTunerEnabled, boolean multiUserEnabled) {
+    void disable(int state2) {
         final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
         if (disabled == mQsDisabled) return;
         mQsDisabled = disabled;
-        updateEverything(isTunerEnabled, multiUserEnabled);
+        updateEverything();
     }
 
-    void updateEverything(boolean isTunerEnabled, boolean multiUserEnabled) {
+    void updateEverything() {
         post(() -> {
-            updateVisibilities(isTunerEnabled, multiUserEnabled);
+            updateVisibilities();
             updateClickabilities();
             setClickable(false);
         });
     }
 
     private void updateClickabilities() {
-        mMultiUserSwitch.setClickable(mMultiUserSwitch.getVisibility() == View.VISIBLE);
-        mEdit.setClickable(mEdit.getVisibility() == View.VISIBLE);
-        mSettingsButton.setClickable(mSettingsButton.getVisibility() == View.VISIBLE);
         mBuildText.setLongClickable(mBuildText.getVisibility() == View.VISIBLE);
     }
 
-    private void updateVisibilities(boolean isTunerEnabled, boolean multiUserEnabled) {
-        mSettingsContainer.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
-        mTunerIcon.setVisibility(isTunerEnabled ? View.VISIBLE : View.INVISIBLE);
-        final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
-        mMultiUserSwitch.setVisibility(
-                showUserSwitcher(multiUserEnabled) ? View.VISIBLE : View.GONE);
-        mSettingsButton.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
-
+    private void updateVisibilities() {
         mBuildText.setVisibility(mExpanded && mShouldShowBuildText ? View.VISIBLE : View.INVISIBLE);
     }
-
-    private boolean showUserSwitcher(boolean multiUserEnabled) {
-        return mExpanded && multiUserEnabled;
-    }
-
-    void onUserInfoChanged(Drawable picture, boolean isGuestUser) {
-        if (picture != null && isGuestUser && !(picture instanceof UserIconDrawable)) {
-            picture = picture.getConstantState().newDrawable(getResources()).mutate();
-            picture.setColorFilter(
-                    Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorForeground),
-                    Mode.SRC_IN);
-        }
-        mMultiUserAvatar.setImageDrawable(picture);
-    }
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
index 929aeda..e7c06e3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -16,35 +16,18 @@
 
 package com.android.systemui.qs;
 
-import static com.android.systemui.qs.dagger.QSFlagsModule.PM_LITE_ENABLED;
+import static com.android.systemui.qs.dagger.QSFragmentModule.QS_FOOTER;
 
 import android.content.ClipData;
 import android.content.ClipboardManager;
-import android.content.Intent;
-import android.graphics.drawable.Drawable;
-import android.os.UserManager;
 import android.text.TextUtils;
 import android.view.View;
 import android.widget.TextView;
 import android.widget.Toast;
 
-import com.android.internal.jank.InteractionJankMonitor;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.UiEventLogger;
-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;
 import com.android.systemui.qs.dagger.QSScope;
 import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.phone.MultiUserSwitchController;
-import com.android.systemui.statusbar.phone.SettingsButton;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.UserInfoController;
-import com.android.systemui.tuner.TunerService;
 import com.android.systemui.util.ViewController;
 
 import javax.inject.Inject;
@@ -56,137 +39,45 @@
 @QSScope
 public class QSFooterViewController extends ViewController<QSFooterView> implements QSFooter {
 
-    private final UserManager mUserManager;
-    private final UserInfoController mUserInfoController;
-    private final ActivityStarter mActivityStarter;
-    private final DeviceProvisionedController mDeviceProvisionedController;
     private final UserTracker mUserTracker;
     private final QSPanelController mQsPanelController;
     private final QuickQSPanelController mQuickQSPanelController;
-    private final TunerService mTunerService;
-    private final MetricsLogger mMetricsLogger;
-    private final FalsingManager mFalsingManager;
-    private final MultiUserSwitchController mMultiUserSwitchController;
-    private final SettingsButton mSettingsButton;
-    private final View mSettingsButtonContainer;
+    private final FooterActionsController mFooterActionsController;
     private final TextView mBuildText;
-    private final View mEdit;
     private final PageIndicator mPageIndicator;
-    private final View mPowerMenuLite;
-    private final boolean mShowPMLiteButton;
-    private final GlobalActionsDialogLite mGlobalActionsDialog;
-    private final UiEventLogger mUiEventLogger;
-
-    private final UserInfoController.OnUserInfoChangedListener mOnUserInfoChangedListener =
-            new UserInfoController.OnUserInfoChangedListener() {
-        @Override
-        public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
-            boolean isGuestUser = mUserManager.isGuestUser(KeyguardUpdateMonitor.getCurrentUser());
-            mView.onUserInfoChanged(picture, isGuestUser);
-        }
-    };
-
-    private final View.OnClickListener mSettingsOnClickListener = new View.OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            // Don't do anything until views are unhidden. Don't do anything if the tap looks
-            // suspicious.
-            if (!mExpanded || mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
-                return;
-            }
-
-            if (v == mSettingsButton) {
-                if (!mDeviceProvisionedController.isCurrentUserSetup()) {
-                    // If user isn't setup just unlock the device and dump them back at SUW.
-                    mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
-                    });
-                    return;
-                }
-                mMetricsLogger.action(
-                        mExpanded ? MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH
-                                : MetricsProto.MetricsEvent.ACTION_QS_COLLAPSED_SETTINGS_LAUNCH);
-                if (mSettingsButton.isTunerClick()) {
-                    mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
-                        if (isTunerEnabled()) {
-                            mTunerService.showResetRequest(
-                                    () -> {
-                                        // Relaunch settings so that the tuner disappears.
-                                        startSettingsActivity();
-                                    });
-                        } else {
-                            Toast.makeText(getContext(), R.string.tuner_toast,
-                                    Toast.LENGTH_LONG).show();
-                            mTunerService.setTunerEnabled(true);
-                        }
-                        startSettingsActivity();
-
-                    });
-                } else {
-                    startSettingsActivity();
-                }
-            } else if (v == mPowerMenuLite) {
-                mUiEventLogger.log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS);
-                mGlobalActionsDialog.showOrHideDialog(false, true);
-            }
-        }
-    };
-
-    private boolean mListening;
-    private boolean mExpanded;
 
     @Inject
-    QSFooterViewController(QSFooterView view, UserManager userManager,
-            UserInfoController userInfoController, ActivityStarter activityStarter,
-            DeviceProvisionedController deviceProvisionedController, UserTracker userTracker,
+    QSFooterViewController(QSFooterView view,
+            UserTracker userTracker,
             QSPanelController qsPanelController,
-            MultiUserSwitchController multiUserSwitchController,
             QuickQSPanelController quickQSPanelController,
-            TunerService tunerService, MetricsLogger metricsLogger, FalsingManager falsingManager,
-            @Named(PM_LITE_ENABLED) boolean showPMLiteButton,
-            GlobalActionsDialogLite globalActionsDialog, UiEventLogger uiEventLogger) {
+            @Named(QS_FOOTER) FooterActionsController footerActionsController) {
         super(view);
-        mUserManager = userManager;
-        mUserInfoController = userInfoController;
-        mActivityStarter = activityStarter;
-        mDeviceProvisionedController = deviceProvisionedController;
         mUserTracker = userTracker;
         mQsPanelController = qsPanelController;
         mQuickQSPanelController = quickQSPanelController;
-        mTunerService = tunerService;
-        mMetricsLogger = metricsLogger;
-        mFalsingManager = falsingManager;
-        mMultiUserSwitchController = multiUserSwitchController;
+        mFooterActionsController = footerActionsController;
 
-        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);
         mPageIndicator = mView.findViewById(R.id.footer_page_indicator);
-        mPowerMenuLite = mView.findViewById(R.id.pm_lite);
-        mShowPMLiteButton = showPMLiteButton;
-        mGlobalActionsDialog = globalActionsDialog;
-        mUiEventLogger = uiEventLogger;
     }
 
     @Override
     protected void onInit() {
         super.onInit();
-        mMultiUserSwitchController.init();
+        mFooterActionsController.init();
     }
 
     @Override
     protected void onViewAttached() {
-        if (mShowPMLiteButton) {
-            mPowerMenuLite.setVisibility(View.VISIBLE);
-            mPowerMenuLite.setOnClickListener(mSettingsOnClickListener);
-        } else {
-            mPowerMenuLite.setVisibility(View.GONE);
-        }
         mView.addOnLayoutChangeListener(
-                (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
-                        mView.updateAnimator(
-                                right - left, mQuickQSPanelController.getNumQuickTiles()));
-        mSettingsButton.setOnClickListener(mSettingsOnClickListener);
+                (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+                    mView.updateExpansion();
+                    mFooterActionsController.updateAnimator(right - left,
+                            mQuickQSPanelController.getNumQuickTiles());
+                }
+        );
+
         mBuildText.setOnLongClickListener(view -> {
             CharSequence buildText = mBuildText.getText();
             if (!TextUtils.isEmpty(buildText)) {
@@ -200,17 +91,8 @@
             }
             return false;
         });
-
-        mEdit.setOnClickListener(view -> {
-            if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
-                return;
-            }
-            mActivityStarter.postQSRunnableDismissingKeyguard(() ->
-                    mQsPanelController.showEdit(view));
-        });
-
         mQsPanelController.setFooterPageIndicator(mPageIndicator);
-        mView.updateEverything(isTunerEnabled(), mMultiUserSwitchController.isMultiUserEnabled());
+        mView.updateEverything();
     }
 
     @Override
@@ -225,38 +107,25 @@
 
     @Override
     public void setExpanded(boolean expanded) {
-        mExpanded = expanded;
-        mView.setExpanded(
-                expanded, isTunerEnabled(), mMultiUserSwitchController.isMultiUserEnabled());
-    }
-
-    @Override
-    public int getHeight() {
-        return mView.getHeight();
+        mFooterActionsController.setExpanded(expanded);
+        mView.setExpanded(expanded);
     }
 
     @Override
     public void setExpansion(float expansion) {
         mView.setExpansion(expansion);
+        mFooterActionsController.setExpansion(expansion);
     }
 
     @Override
     public void setListening(boolean listening) {
-        if (mListening == listening) {
-            return;
-        }
-
-        mListening = listening;
-        if (mListening) {
-            mUserInfoController.addCallback(mOnUserInfoChangedListener);
-        } else {
-            mUserInfoController.removeCallback(mOnUserInfoChangedListener);
-        }
+        mFooterActionsController.setListening(listening);
     }
 
     @Override
     public void setKeyguardShowing(boolean keyguardShowing) {
         mView.setKeyguardShowing();
+        mFooterActionsController.setKeyguardShowing();
     }
 
     /** */
@@ -267,19 +136,7 @@
 
     @Override
     public void disable(int state1, int state2, boolean animate) {
-        mView.disable(state2, isTunerEnabled(), mMultiUserSwitchController.isMultiUserEnabled());
-    }
-
-    private void startSettingsActivity() {
-        ActivityLaunchAnimator.Controller animationController =
-                mSettingsButtonContainer != null ? ActivityLaunchAnimator.Controller.fromView(
-                        mSettingsButtonContainer,
-                        InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON) : null;
-        mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS),
-                true /* dismissShade */, animationController);
-    }
-
-    private boolean isTunerEnabled() {
-        return mTunerService.isTunerEnabled();
+        mView.disable(state2);
+        mFooterActionsController.disable(state2);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index dd81b65..4242e1b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -37,8 +37,10 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
+import com.android.systemui.Dumpable;
 import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
+import com.android.systemui.dump.DumpManager;
 import com.android.systemui.media.MediaHost;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QS;
@@ -50,6 +52,7 @@
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
+import com.android.systemui.statusbar.policy.BrightnessMirrorController;
 import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
 import com.android.systemui.util.InjectionInflationController;
 import com.android.systemui.util.LifecycleFragment;
@@ -131,6 +134,8 @@
      */
     private boolean mAnimateNextQsUpdate;
 
+    private DumpManager mDumpManager;
+
     @Inject
     public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
             InjectionInflationController injectionInflater, QSTileHost qsTileHost,
@@ -139,7 +144,7 @@
             @Named(QUICK_QS_PANEL) MediaHost qqsMediaHost,
             KeyguardBypassController keyguardBypassController,
             QSFragmentComponent.Factory qsComponentFactory,
-            FalsingManager falsingManager) {
+            FalsingManager falsingManager, DumpManager dumpManager) {
         mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
         mInjectionInflater = injectionInflater;
         mCommandQueue = commandQueue;
@@ -152,6 +157,7 @@
         mFalsingManager = falsingManager;
         mBypassController = keyguardBypassController;
         mStatusBarStateController = statusBarStateController;
+        mDumpManager = dumpManager;
     }
 
     @Override
@@ -197,6 +203,7 @@
         mQSContainerImplController = qsFragmentComponent.getQSContainerImplController();
         mQSContainerImplController.init();
         mContainer = mQSContainerImplController.getView();
+        mDumpManager.registerDumpable(mContainer.getClass().getName(), mContainer);
 
         mQSDetail.setQsPanel(mQSPanelController, mHeader, mFooter, mFalsingManager);
         mQSAnimator = qsFragmentComponent.getQSAnimator();
@@ -248,6 +255,7 @@
         mQSCustomizerController.setQs(null);
         mQsDetailDisplayer.setQsPanelController(null);
         mScrollListener = null;
+        mDumpManager.unregisterDumpable(mContainer.getClass().getName());
     }
 
     @Override
@@ -407,6 +415,12 @@
         return mQSPanelController;
     }
 
+    public void setBrightnessMirrorController(
+            BrightnessMirrorController brightnessMirrorController) {
+        mQSPanelController.setBrightnessMirror(brightnessMirrorController);
+        mQuickQSPanelController.setBrightnessMirror(brightnessMirrorController);
+    }
+
     @Override
     public boolean isShowingDetail() {
         return mQSCustomizerController.isCustomizing() || mQSDetail.isShowingDetail();
@@ -496,11 +510,13 @@
                             : headerTranslation);
         }
         int currentHeight = getView().getHeight();
-        mLastHeaderTranslation = headerTranslation;
-        if (expansion == mLastQSExpansion && mLastKeyguardAndExpanded == onKeyguardAndExpanded
-                && mLastViewHeight == currentHeight) {
+        if (expansion == mLastQSExpansion
+                && mLastKeyguardAndExpanded == onKeyguardAndExpanded
+                && mLastViewHeight == currentHeight
+                && mLastHeaderTranslation == headerTranslation) {
             return;
         }
+        mLastHeaderTranslation = headerTranslation;
         mLastQSExpansion = expansion;
         mLastKeyguardAndExpanded = onKeyguardAndExpanded;
         mLastViewHeight = currentHeight;
@@ -520,8 +536,8 @@
         }
         mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
         mQSPanelController.setRevealExpansion(expansion);
-        mQSPanelController.getTileLayout().setExpansion(expansion);
-        mQuickQSPanelController.getTileLayout().setExpansion(expansion);
+        mQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);
+        mQuickQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);
         mQSPanelScrollView.setTranslationY(translationScaleY * heightDiff);
         if (fullyCollapsed) {
             mQSPanelScrollView.setScrollY(0);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
index 000fd1c..9f585bd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
@@ -37,6 +37,7 @@
     void removeCallback(Callback callback);
     TileServices getTileServices();
     void removeTile(String tileSpec);
+    void removeTiles(Collection<String> specs);
     void unmarkTileAsAutoAdded(String tileSpec);
 
     int indexOf(String tileSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 0c65510..28aa884 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -27,12 +27,15 @@
 import android.os.Handler;
 import android.os.Message;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
 
+import androidx.annotation.VisibleForTesting;
+
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.widget.RemeasuringLinearLayout;
 import com.android.systemui.R;
@@ -386,13 +389,7 @@
     }
 
     private void switchToParent(View child, ViewGroup parent, int index) {
-        ViewGroup currentParent = (ViewGroup) child.getParent();
-        if (currentParent != parent || currentParent.indexOfChild(child) != index) {
-            if (currentParent != null) {
-                currentParent.removeView(child);
-            }
-            parent.addView(child, index);
-        }
+        switchToParent(child, parent, index, getDumpableTag());
     }
 
     /** Call when orientation has changed and MediaHost needs to be adjusted. */
@@ -739,7 +736,7 @@
         void setListening(boolean listening, UiEventLogger uiEventLogger);
 
         /**
-         * Set the minimum number of rows to show
+         * Sets the minimum number of rows to show
          *
          * @param minRows the minimum.
          */
@@ -748,7 +745,7 @@
         }
 
         /**
-         * Set the max number of columns to show
+         * Sets the max number of columns to show
          *
          * @param maxColumns the maximum
          *
@@ -758,7 +755,10 @@
             return false;
         }
 
-        default void setExpansion(float expansion) {}
+        /**
+         * Sets the expansion value and proposedTranslation to panel.
+         */
+        default void setExpansion(float expansion, float proposedTranslation) {}
 
         int getNumVisibleTiles();
     }
@@ -766,4 +766,29 @@
     interface OnConfigurationChangedListener {
         void onConfigurationChange(Configuration newConfig);
     }
+
+    @VisibleForTesting
+    static void switchToParent(View child, ViewGroup parent, int index, String tag) {
+        if (parent == null) {
+            Log.w(tag, "Trying to move view to null parent",
+                    new IllegalStateException());
+            return;
+        }
+        ViewGroup currentParent = (ViewGroup) child.getParent();
+        if (currentParent != parent) {
+            if (currentParent != null) {
+                currentParent.removeView(child);
+            }
+            parent.addView(child, index);
+            return;
+        }
+        // Same parent, we are just changing indices
+        int currentIndex = parent.indexOfChild(child);
+        if (currentIndex == index) {
+            // We want to be in the same place. Nothing to do here
+            return;
+        }
+        parent.removeView(child);
+        parent.addView(child, index);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index f3d071e..6e09f22 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -40,6 +40,7 @@
 import com.android.systemui.qs.dagger.QSScope;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.settings.brightness.BrightnessController;
+import com.android.systemui.settings.brightness.BrightnessMirrorHandler;
 import com.android.systemui.settings.brightness.BrightnessSlider;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
 import com.android.systemui.tuner.TunerService;
@@ -60,10 +61,9 @@
     private final QSTileRevealController.Factory mQsTileRevealControllerFactory;
     private final FalsingManager mFalsingManager;
     private final BrightnessController mBrightnessController;
-    private final BrightnessSlider.Factory mBrightnessSliderFactory;
     private final BrightnessSlider mBrightnessSlider;
+    private final BrightnessMirrorHandler mBrightnessMirrorHandler;
 
-    private BrightnessMirrorController mBrightnessMirrorController;
     private boolean mGridContentVisible = true;
 
     private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
@@ -75,14 +75,10 @@
             if (mView.isListening()) {
                 refreshAllTiles();
             }
-            updateBrightnessMirror();
             mView.switchSecurityFooter(mShouldUseSplitNotificationShade);
         }
     };
 
-    private final BrightnessMirrorController.BrightnessMirrorListener mBrightnessMirrorListener =
-            mirror -> updateBrightnessMirror();
-
     private View.OnTouchListener mTileLayoutTouchListener = new View.OnTouchListener() {
         @Override
         public boolean onTouch(View v, MotionEvent event) {
@@ -110,12 +106,12 @@
         mQsTileRevealControllerFactory = qsTileRevealControllerFactory;
         mFalsingManager = falsingManager;
         mQsSecurityFooter.setHostEnvironment(qstileHost);
-        mBrightnessSliderFactory = brightnessSliderFactory;
 
-        mBrightnessSlider = mBrightnessSliderFactory.create(getContext(), mView);
+        mBrightnessSlider = brightnessSliderFactory.create(getContext(), mView);
         mView.setBrightnessView(mBrightnessSlider.getRootView());
 
         mBrightnessController = brightnessControllerFactory.create(mBrightnessSlider);
+        mBrightnessMirrorHandler = new BrightnessMirrorHandler(mBrightnessController);
     }
 
     @Override
@@ -142,9 +138,7 @@
         mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener);
         mView.setSecurityFooter(mQsSecurityFooter.getView(), mShouldUseSplitNotificationShade);
         switchTileLayout(true);
-        if (mBrightnessMirrorController != null) {
-            mBrightnessMirrorController.addCallback(mBrightnessMirrorListener);
-        }
+        mBrightnessMirrorHandler.onQsPanelAttached();
 
         ((PagedTileLayout) mView.getOrCreateTileLayout())
                 .setOnTouchListener(mTileLayoutTouchListener);
@@ -160,9 +154,7 @@
     protected void onViewDetached() {
         mTunerService.removeTunable(mView);
         mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener);
-        if (mBrightnessMirrorController != null) {
-            mBrightnessMirrorController.removeCallback(mBrightnessMirrorListener);
-        }
+        mBrightnessMirrorHandler.onQsPanelDettached();
         super.onViewDetached();
     }
 
@@ -196,23 +188,8 @@
         }
     }
 
-    /** */
     public void setBrightnessMirror(BrightnessMirrorController brightnessMirrorController) {
-        mBrightnessMirrorController = brightnessMirrorController;
-        if (mBrightnessMirrorController != null) {
-            mBrightnessMirrorController.removeCallback(mBrightnessMirrorListener);
-        }
-        mBrightnessMirrorController = brightnessMirrorController;
-        if (mBrightnessMirrorController != null) {
-            mBrightnessMirrorController.addCallback(mBrightnessMirrorListener);
-        }
-        updateBrightnessMirror();
-    }
-
-    private void updateBrightnessMirror() {
-        if (mBrightnessMirrorController != null) {
-            mBrightnessSlider.setMirrorControllerAndMirror(mBrightnessMirrorController);
-        }
+        mBrightnessMirrorHandler.setController(brightnessMirrorController);
     }
 
     /** Get the QSTileHost this panel uses. */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 0da4814..42323e3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -84,15 +84,15 @@
                 public void onConfigurationChange(Configuration newConfig) {
                     mShouldUseSplitNotificationShade =
                             Utils.shouldUseSplitNotificationShade(getResources());
+                    onConfigurationChanged();
                     if (newConfig.orientation != mLastOrientation) {
                         mLastOrientation = newConfig.orientation;
-                        onScreenRotated();
                         switchTileLayout(false);
                     }
                 }
             };
 
-    protected void onScreenRotated() { }
+    protected void onConfigurationChanged() { }
 
     private final Function1<Boolean, Unit> mMediaHostVisibilityListener = (visible) -> {
         if (mMediaVisibilityChangedListener != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 756ad99..9ceeb75 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -39,6 +39,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.qs.QSFactory;
 import com.android.systemui.plugins.qs.QSTile;
@@ -51,7 +52,6 @@
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.phone.AutoTileManager;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -348,6 +348,17 @@
         changeTileSpecs(tileSpecs-> tileSpecs.remove(spec));
     }
 
+    /**
+     * Remove many tiles at once.
+     *
+     * It will only save to settings once (as opposed to {@link QSTileHost#removeTile} called
+     * multiple times).
+     */
+    @Override
+    public void removeTiles(Collection<String> specs) {
+        changeTileSpecs(tileSpecs -> tileSpecs.removeAll(specs));
+    }
+
     @Override
     public void unmarkTileAsAutoAdded(String spec) {
         if (mAutoTiles != null) mAutoTiles.unmarkTileAsAutoAdded(spec);
@@ -369,6 +380,7 @@
      * @param requestPosition -1 for end, 0 for beginning, or X for insertion at position X
      */
     public void addTile(String spec, int requestPosition) {
+        if (spec.equals("work")) Log.wtfStack(TAG, "Adding work tile");
         changeTileSpecs(tileSpecs -> {
             if (tileSpecs.contains(spec)) return false;
 
@@ -383,6 +395,7 @@
     }
 
     void saveTilesToSettings(List<String> tileSpecs) {
+        if (tileSpecs.contains("work")) Log.wtfStack(TAG, "Saving work tile");
         mSecureSettings.putStringForUser(TILES_SETTING, TextUtils.join(",", tileSpecs),
                 null /* tag */, false /* default */, mCurrentUser,
                 true /* overrideable by restore */);
@@ -512,33 +525,6 @@
                 }
             }
         }
-        // TODO(b/174753536): Move it into the config file.
-        // Only do the below hacking when at least one of the below tiles exist
-        //   --InternetTile
-        //   --WiFiTile
-        //   --CellularTIle
-        if (tiles.contains("internet") || tiles.contains("wifi") || tiles.contains("cell")) {
-            if (FeatureFlags.isProviderModelSettingEnabled(context)) {
-                if (!tiles.contains("internet")) {
-                    if (tiles.contains("wifi")) {
-                        // Replace the WiFi with Internet, and remove the Cell
-                        tiles.set(tiles.indexOf("wifi"), "internet");
-                        tiles.remove("cell");
-                    } else if (tiles.contains("cell")) {
-                        // Replace the Cell with Internet
-                        tiles.set(tiles.indexOf("cell"), "internet");
-                    }
-                } else {
-                    tiles.remove("wifi");
-                    tiles.remove("cell");
-                }
-            } else {
-                if (tiles.contains("internet")) {
-                    tiles.set(tiles.indexOf("internet"), "wifi");
-                    tiles.add("cell");
-                }
-            }
-        }
         return tiles;
     }
 
@@ -558,14 +544,6 @@
                 && GarbageMonitor.ADD_MEMORY_TILE_TO_DEFAULT_ON_DEBUGGABLE_BUILDS) {
             tiles.add(GarbageMonitor.MemoryTile.TILE_SPEC);
         }
-        // TODO(b/174753536): Change the config file directly.
-        // Filter out unused tiles from the default QS config.
-        if (FeatureFlags.isProviderModelSettingEnabled(context)) {
-            tiles.remove("cell");
-            tiles.remove("wifi");
-        } else {
-            tiles.remove("internet");
-        }
         return tiles;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSBrightnessController.kt b/packages/SystemUI/src/com/android/systemui/qs/QuickQSBrightnessController.kt
index 7c81abc..14374ff 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSBrightnessController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSBrightnessController.kt
@@ -19,6 +19,8 @@
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.settings.brightness.BrightnessController
 import com.android.systemui.settings.brightness.BrightnessSlider
+import com.android.systemui.settings.brightness.MirroredBrightnessController
+import com.android.systemui.statusbar.policy.BrightnessMirrorController
 import javax.inject.Inject
 
 /**
@@ -27,7 +29,7 @@
  */
 class QuickQSBrightnessController @VisibleForTesting constructor(
     private val brightnessControllerFactory: () -> BrightnessController
-) {
+) : MirroredBrightnessController {
 
     @Inject constructor(
         brightnessControllerFactory: BrightnessController.Factory,
@@ -42,6 +44,7 @@
 
     private var isListening = false
     private var brightnessController: BrightnessController? = null
+    private var mirrorController: BrightnessMirrorController? = null
 
     fun init(shouldUseSplitNotificationShade: Boolean) {
         refreshVisibility(shouldUseSplitNotificationShade)
@@ -77,6 +80,11 @@
         }
     }
 
+    override fun setMirror(controller: BrightnessMirrorController) {
+        mirrorController = controller
+        mirrorController?.let { brightnessController?.setMirror(it) }
+    }
+
     private fun hideBrightnessSlider() {
         brightnessController?.hideSlider()
     }
@@ -84,11 +92,10 @@
     private fun showBrightnessSlider() {
         if (brightnessController == null) {
             brightnessController = brightnessControllerFactory()
-        }
-        brightnessController?.showSlider()
-        if (!isListening) {
+            mirrorController?.also { brightnessController?.setMirror(it) }
             brightnessController?.registerCallbacks()
             isListening = true
         }
+        brightnessController?.showSlider()
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index f1c1e12..613e7f8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -171,6 +171,8 @@
 
     static class QQSSideLabelTileLayout extends SideLabelTileLayout {
 
+        private boolean mLastSelected;
+
         QQSSideLabelTileLayout(Context context) {
             super(context, null);
             setClipChildren(false);
@@ -216,5 +218,29 @@
                 }
             }
         }
+
+        @Override
+        public void setExpansion(float expansion, float proposedTranslation) {
+            if (expansion > 0f && expansion < 1f) {
+                return;
+            }
+            // The cases we must set select for marquee when QQS/QS collapsed, and QS full expanded.
+            // Expansion == 0f is when QQS is fully showing (as opposed to 1f, which is QS). At this
+            // point we want them to be selected so the tiles will marquee (but not at other points
+            // of expansion.
+            boolean selected = (expansion == 1f || proposedTranslation < 0f);
+            if (mLastSelected == selected) {
+                return;
+            }
+            // We set it as not important while we change this, so setting each tile as selected
+            // will not cause them to announce themselves until the user has actually selected the
+            // item.
+            setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+            for (int i = 0; i < getChildCount(); i++) {
+                getChildAt(i).setSelected(selected);
+            }
+            setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+            mLastSelected = selected;
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index 74cd50c..92690c7d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -17,6 +17,7 @@
 package com.android.systemui.qs;
 
 import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL;
+import static com.android.systemui.qs.dagger.QSFragmentModule.QQS_FOOTER;
 import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
 
 import com.android.internal.logging.MetricsLogger;
@@ -29,6 +30,8 @@
 import com.android.systemui.qs.customize.QSCustomizerController;
 import com.android.systemui.qs.dagger.QSScope;
 import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.settings.brightness.BrightnessMirrorHandler;
+import com.android.systemui.statusbar.policy.BrightnessMirrorController;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -50,6 +53,8 @@
 
     // brightness is visible only in split shade
     private final QuickQSBrightnessController mBrightnessController;
+    private final BrightnessMirrorHandler mBrightnessMirrorHandler;
+    private final FooterActionsController mFooterActionsController;
 
     @Inject
     QuickQSPanelController(QuickQSPanel view, QSTileHost qsTileHost,
@@ -58,11 +63,14 @@
             @Named(QUICK_QS_PANEL) MediaHost mediaHost,
             MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
             DumpManager dumpManager,
-            QuickQSBrightnessController quickQSBrightnessController
+            QuickQSBrightnessController quickQSBrightnessController,
+            @Named(QQS_FOOTER) FooterActionsController footerActionsController
     ) {
         super(view, qsTileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger,
                 uiEventLogger, qsLogger, dumpManager);
         mBrightnessController = quickQSBrightnessController;
+        mBrightnessMirrorHandler = new BrightnessMirrorHandler(mBrightnessController);
+        mFooterActionsController = footerActionsController;
     }
 
     @Override
@@ -72,24 +80,29 @@
         mMediaHost.setShowsOnlyActiveMedia(true);
         mMediaHost.init(MediaHierarchyManager.LOCATION_QQS);
         mBrightnessController.init(mShouldUseSplitNotificationShade);
+        mFooterActionsController.init();
+        mFooterActionsController.refreshVisibility(mShouldUseSplitNotificationShade);
     }
 
     @Override
     protected void onViewAttached() {
         super.onViewAttached();
         mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener);
+        mBrightnessMirrorHandler.onQsPanelAttached();
     }
 
     @Override
     protected void onViewDetached() {
         super.onViewDetached();
         mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener);
+        mBrightnessMirrorHandler.onQsPanelDettached();
     }
 
     @Override
     void setListening(boolean listening) {
         super.setListening(listening);
         mBrightnessController.setListening(listening);
+        mFooterActionsController.setListening(listening);
     }
 
     public boolean isListening() {
@@ -108,8 +121,9 @@
     }
 
     @Override
-    protected void onScreenRotated() {
+    protected void onConfigurationChanged() {
         mBrightnessController.refreshVisibility(mShouldUseSplitNotificationShade);
+        mFooterActionsController.refreshVisibility(mShouldUseSplitNotificationShade);
     }
 
     @Override
@@ -132,4 +146,8 @@
     public int getNumQuickTiles() {
         return mView.getNumQuickTiles();
     }
+
+    public void setBrightnessMirror(BrightnessMirrorController brightnessMirrorController) {
+        mBrightnessMirrorHandler.setController(brightnessMirrorController);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index ce056ab..a85800b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -35,13 +35,14 @@
 import androidx.annotation.NonNull;
 
 import com.android.settingslib.Utils;
-import com.android.systemui.BatteryMeterView;
 import com.android.systemui.R;
+import com.android.systemui.battery.BatteryMeterView;
 import com.android.systemui.qs.QSDetail.Callback;
 import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
 import com.android.systemui.statusbar.phone.StatusBarWindowView;
 import com.android.systemui.statusbar.phone.StatusIconContainer;
 import com.android.systemui.statusbar.policy.Clock;
+import com.android.systemui.statusbar.policy.VariableDateView;
 
 import java.util.List;
 
@@ -61,11 +62,15 @@
 
     protected QuickQSPanel mHeaderQsPanel;
     private View mDatePrivacyView;
+    private View mDateView;
+    // DateView next to clock. Visible on QQS
+    private VariableDateView mClockDateView;
     private View mSecurityHeaderView;
     private View mStatusIconsView;
     private View mContainer;
 
     private View mQSCarriers;
+    private ViewGroup mClockContainer;
     private Clock mClockView;
     private Space mDatePrivacySeparator;
     private View mClockIconsSeparator;
@@ -93,6 +98,9 @@
     private List<String> mRssiIgnoredSlots;
     private boolean mIsSingleCarrier;
 
+    private boolean mHasCenterCutout;
+    private boolean mConfigShowBatteryEstimate;
+
     public QuickStatusBarHeader(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -117,12 +125,15 @@
         mContainer = findViewById(R.id.qs_container);
         mIconContainer = findViewById(R.id.statusIcons);
         mPrivacyChip = findViewById(R.id.privacy_chip);
+        mDateView = findViewById(R.id.date);
+        mClockDateView = findViewById(R.id.date_clock);
         mSecurityHeaderView = findViewById(R.id.header_text_container);
         mClockIconsSeparator = findViewById(R.id.separator);
         mRightLayout = findViewById(R.id.rightLayout);
         mDateContainer = findViewById(R.id.date_container);
         mPrivacyContainer = findViewById(R.id.privacy_container);
 
+        mClockContainer = findViewById(R.id.clock_container);
         mClockView = findViewById(R.id.clock);
         mDatePrivacySeparator = findViewById(R.id.space);
         // Tint for the battery icons are handled in setupHost()
@@ -130,8 +141,6 @@
 
         updateResources();
 
-        // Don't need to worry about tuner settings for this icon
-        mBatteryRemainingIcon.setIgnoreTunerUpdates(true);
         // QS will always show the estimate, and BatteryMeterView handles the case where
         // it's unavailable or charging
         mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
@@ -171,7 +180,7 @@
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         if (mDatePrivacyView.getMeasuredHeight() != mTopViewMeasureHeight) {
             mTopViewMeasureHeight = mDatePrivacyView.getMeasuredHeight();
-            updateAnimators();
+            post(this::updateAnimators);
         }
     }
 
@@ -200,6 +209,14 @@
         mPrivacyContainer.setLayoutParams(lp);
     }
 
+    private void updateBatteryMode() {
+        if (mConfigShowBatteryEstimate && !mHasCenterCutout) {
+            mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
+        } else {
+            mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ON);
+        }
+    }
+
     void updateResources() {
         Resources resources = mContext.getResources();
         // status bar is already displayed out of QS in split shade
@@ -208,6 +225,8 @@
         mStatusIconsView.setVisibility(shouldUseSplitShade ? View.GONE : View.VISIBLE);
         mDatePrivacyView.setVisibility(shouldUseSplitShade ? View.GONE : View.VISIBLE);
 
+        mConfigShowBatteryEstimate = resources.getBoolean(R.bool.config_showBatteryEstimateQSBH);
+
         mRoundedCornerPadding = resources.getDimensionPixelSize(
                 R.dimen.rounded_corner_content_padding);
 
@@ -248,6 +267,7 @@
                 .getDimensionPixelSize(R.dimen.qqs_layout_margin_top);
         mHeaderQsPanel.setLayoutParams(qqsLP);
 
+        updateBatteryMode();
         updateHeadersPadding();
         updateAnimators();
     }
@@ -268,7 +288,8 @@
         TouchAnimator.Builder builder = new TouchAnimator.Builder()
                 .addFloat(mSecurityHeaderView, "alpha", 0, 1)
                 // These views appear on expanding down
-                .addFloat(mClockView, "alpha", 0, 1)
+                .addFloat(mDateView, "alpha", 0, 0, 1)
+                .addFloat(mClockDateView, "alpha", 1, 0, 0)
                 .addFloat(mQSCarriers, "alpha", 0, 1)
                 .setListener(new TouchAnimator.ListenerAdapter() {
                     @Override
@@ -277,10 +298,14 @@
                         if (!mIsSingleCarrier) {
                             mIconContainer.addIgnoredSlots(mRssiIgnoredSlots);
                         }
+                        // Make it gone so there's enough room for carrier names
+                        mClockDateView.setVisibility(View.GONE);
                     }
 
                     @Override
                     public void onAnimationStarted() {
+                        mClockDateView.setVisibility(View.VISIBLE);
+                        mClockDateView.setFreezeSwitching(true);
                         setSeparatorVisibility(false);
                         if (!mIsSingleCarrier) {
                             mIconContainer.addIgnoredSlots(mRssiIgnoredSlots);
@@ -290,6 +315,7 @@
                     @Override
                     public void onAnimationAtStart() {
                         super.onAnimationAtStart();
+                        mClockDateView.setFreezeSwitching(false);
                         setSeparatorVisibility(mShowClockIconsSeparator);
                         // In QQS we never ignore RSSI.
                         mIconContainer.removeIgnoredSlots(mRssiIgnoredSlots);
@@ -386,14 +412,14 @@
                 mClockIconsSeparatorLayoutParams.width = 0;
                 setSeparatorVisibility(false);
                 mShowClockIconsSeparator = false;
-                mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
+                mHasCenterCutout = false;
             } else {
                 datePrivacySeparatorLayoutParams.width = topCutout.width();
                 mDatePrivacySeparator.setVisibility(View.VISIBLE);
                 mClockIconsSeparatorLayoutParams.width = topCutout.width();
                 mShowClockIconsSeparator = true;
                 setSeparatorVisibility(mKeyguardExpansionFraction == 0f);
-                mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ON);
+                mHasCenterCutout = true;
             }
         }
         mDatePrivacySeparator.setLayoutParams(datePrivacySeparatorLayoutParams);
@@ -401,6 +427,8 @@
         mCutOutPaddingLeft = padding.first;
         mCutOutPaddingRight = padding.second;
         mWaterfallTopInset = cutout == null ? 0 : cutout.getWaterfallInsets().top;
+
+        updateBatteryMode();
         updateHeadersPadding();
         return super.onApplyWindowInsets(insets);
     }
@@ -420,10 +448,11 @@
         mClockIconsSeparator.setVisibility(visible ? View.VISIBLE : View.GONE);
         mQSCarriers.setVisibility(visible ? View.GONE : View.VISIBLE);
 
-        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mClockView.getLayoutParams();
+        LinearLayout.LayoutParams lp =
+                (LinearLayout.LayoutParams) mClockContainer.getLayoutParams();
         lp.width = visible ? 0 : WRAP_CONTENT;
         lp.weight = visible ? 1f : 0f;
-        mClockView.setLayoutParams(lp);
+        mClockContainer.setLayoutParams(lp);
 
         lp = (LinearLayout.LayoutParams) mRightLayout.getLayoutParams();
         lp.width = visible ? 0 : WRAP_CONTENT;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index 98eb381..38428c5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -25,9 +25,11 @@
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.R;
+import com.android.systemui.battery.BatteryMeterViewController;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.demomode.DemoMode;
 import com.android.systemui.demomode.DemoModeController;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.privacy.OngoingPrivacyChip;
 import com.android.systemui.privacy.PrivacyChipEvent;
@@ -37,10 +39,10 @@
 import com.android.systemui.privacy.logging.PrivacyLogger;
 import com.android.systemui.qs.carrier.QSCarrierGroupController;
 import com.android.systemui.qs.dagger.QSScope;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusIconContainer;
 import com.android.systemui.statusbar.policy.Clock;
+import com.android.systemui.statusbar.policy.VariableDateViewController;
 import com.android.systemui.util.ViewController;
 
 import java.util.List;
@@ -69,8 +71,12 @@
     private final PrivacyLogger mPrivacyLogger;
     private final PrivacyDialogController mPrivacyDialogController;
     private final QSExpansionPathInterpolator mQSExpansionPathInterpolator;
+    private final BatteryMeterViewController mBatteryMeterViewController;
     private final FeatureFlags mFeatureFlags;
 
+    private final VariableDateViewController mVariableDateViewControllerDateView;
+    private final VariableDateViewController mVariableDateViewControllerClockDateView;
+
     private boolean mListening;
     private boolean mMicCameraIndicatorsEnabled;
     private boolean mLocationIndicatorsEnabled;
@@ -134,7 +140,9 @@
             SysuiColorExtractor colorExtractor,
             PrivacyDialogController privacyDialogController,
             QSExpansionPathInterpolator qsExpansionPathInterpolator,
-            FeatureFlags featureFlags) {
+            BatteryMeterViewController batteryMeterViewController,
+            FeatureFlags featureFlags,
+            VariableDateViewController.Factory variableDateViewControllerFactory) {
         super(view);
         mPrivacyItemController = privacyItemController;
         mActivityStarter = activityStarter;
@@ -145,6 +153,7 @@
         mPrivacyLogger = privacyLogger;
         mPrivacyDialogController = privacyDialogController;
         mQSExpansionPathInterpolator = qsExpansionPathInterpolator;
+        mBatteryMeterViewController = batteryMeterViewController;
         mFeatureFlags = featureFlags;
 
         mQSCarrierGroupController = qsCarrierGroupControllerBuilder
@@ -154,6 +163,12 @@
         mPrivacyChip = mView.findViewById(R.id.privacy_chip);
         mClockView = mView.findViewById(R.id.clock);
         mIconContainer = mView.findViewById(R.id.statusIcons);
+        mVariableDateViewControllerDateView = variableDateViewControllerFactory.create(
+                mView.requireViewById(R.id.date)
+        );
+        mVariableDateViewControllerClockDateView = variableDateViewControllerFactory.create(
+                mView.requireViewById(R.id.date_clock)
+        );
 
         mIconManager = new StatusBarIconController.TintedIconManager(mIconContainer, featureFlags);
         mDemoModeReceiver = new ClockDemoModeReceiver(mClockView);
@@ -167,6 +182,14 @@
         mCameraSlot = getResources().getString(com.android.internal.R.string.status_bar_camera);
         mMicSlot = getResources().getString(com.android.internal.R.string.status_bar_microphone);
         mLocationSlot = getResources().getString(com.android.internal.R.string.status_bar_location);
+
+        // Don't need to worry about tuner settings for this icon
+        mBatteryMeterViewController.ignoreTunerUpdates();
+    }
+
+    @Override
+    protected void onInit() {
+        mBatteryMeterViewController.init();
     }
 
     @Override
@@ -178,6 +201,8 @@
 
         // Ignore privacy icons because they show in the space above QQS
         updatePrivacyIconSlots();
+        mIconContainer.addIgnoredSlot(
+                getResources().getString(com.android.internal.R.string.status_bar_managed_profile));
         mIconContainer.setShouldRestrictIcons(false);
         mStatusBarIconController.addIconGroup(mIconManager);
 
@@ -203,6 +228,9 @@
         mView.onAttach(mIconManager, mQSExpansionPathInterpolator, rssiIgnoredSlots);
 
         mDemoModeController.addCallback(mDemoModeReceiver);
+
+        mVariableDateViewControllerDateView.init();
+        mVariableDateViewControllerClockDateView.init();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
index 67c4d33..953f9fb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
@@ -40,8 +40,8 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
 import com.android.systemui.util.CarrierConfigTracker;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 3cb715c..1c20a86 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -34,6 +34,7 @@
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.qs.QSTile.State;
 import com.android.systemui.qs.QSTileHost;
@@ -41,7 +42,6 @@
 import com.android.systemui.qs.external.CustomTile;
 import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon;
 import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.util.leak.GarbageMonitor;
 
 import java.util.ArrayList;
@@ -63,7 +63,6 @@
     private final Executor mBgExecutor;
     private final Context mContext;
     private final UserTracker mUserTracker;
-    private final FeatureFlags mFeatureFlags;
     private TileStateListener mListener;
 
     private boolean mFinished;
@@ -73,14 +72,12 @@
             Context context,
             UserTracker userTracker,
             @Main Executor mainExecutor,
-            @Background Executor bgExecutor,
-            FeatureFlags featureFlags
+            @Background Executor bgExecutor
     ) {
         mContext = context;
         mMainExecutor = mainExecutor;
         mBgExecutor = bgExecutor;
         mUserTracker = userTracker;
-        mFeatureFlags = featureFlags;
     }
 
     public void setListener(TileStateListener listener) {
@@ -121,19 +118,11 @@
         }
 
         final ArrayList<QSTile> tilesToAdd = new ArrayList<>();
-        // TODO(b/174753536): Move it into the config file.
-        if (mFeatureFlags.isProviderModelSettingEnabled()) {
-            possibleTiles.remove("cell");
-            possibleTiles.remove("wifi");
-        } else {
-            possibleTiles.remove("internet");
-        }
 
         for (String spec : possibleTiles) {
             // Only add current and stock tiles that can be created from QSFactoryImpl.
             // Do not include CustomTile. Those will be created by `addPackageTiles`.
             if (spec.startsWith(CustomTile.PREFIX)) continue;
-            // TODO(b/174753536): Move it into the config file.
             final QSTile tile = host.createTile(spec);
             if (tile == null) {
                 continue;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
index 6fa44eb..103ac65 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
@@ -20,7 +20,7 @@
 import android.hardware.display.ColorDisplayManager;
 
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.util.settings.GlobalSettings;
 
 import javax.inject.Named;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
index 36a1910..386769c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
@@ -23,8 +23,13 @@
 import android.view.View;
 
 import com.android.systemui.R;
+import com.android.systemui.battery.BatteryMeterView;
 import com.android.systemui.dagger.qualifiers.RootView;
 import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.qs.FooterActionsController;
+import com.android.systemui.qs.FooterActionsController.ExpansionState;
+import com.android.systemui.qs.FooterActionsControllerBuilder;
+import com.android.systemui.qs.FooterActionsView;
 import com.android.systemui.qs.QSContainerImpl;
 import com.android.systemui.qs.QSFooter;
 import com.android.systemui.qs.QSFooterView;
@@ -48,6 +53,8 @@
 @Module
 public interface QSFragmentModule {
     String QS_SECURITY_FOOTER_VIEW = "qs_security_footer";
+    String QQS_FOOTER = "qqs_footer";
+    String QS_FOOTER = "qs_footer";
     String QS_USING_MEDIA_PLAYER = "qs_using_media_player";
 
     /**
@@ -109,12 +116,56 @@
 
     /** */
     @Provides
+    static BatteryMeterView providesBatteryMeterView(QuickStatusBarHeader quickStatusBarHeader) {
+        return quickStatusBarHeader.findViewById(R.id.batteryRemainingIcon);
+    }
+
+    /** */
+    @Provides
     static QSFooterView providesQSFooterView(@RootView View view) {
         return view.findViewById(R.id.qs_footer);
     }
 
     /** */
     @Provides
+    @Named(QS_FOOTER)
+    static FooterActionsView providesQSFooterActionsView(@RootView View view) {
+        return view.findViewById(R.id.qs_footer_actions);
+    }
+
+    /** */
+    @Provides
+    @Named(QQS_FOOTER)
+    static FooterActionsView providesQQSFooterActionsView(@RootView View view) {
+        return view.findViewById(R.id.qqs_footer_actions);
+    }
+
+    /** */
+    @Provides
+    @Named(QQS_FOOTER)
+    static FooterActionsController providesQQSFooterActionsController(
+            FooterActionsControllerBuilder footerActionsControllerBuilder,
+            @Named(QQS_FOOTER) FooterActionsView qqsFooterActionsView) {
+        return footerActionsControllerBuilder
+                .withView(qqsFooterActionsView)
+                .withButtonsVisibleWhen(ExpansionState.COLLAPSED)
+                .build();
+    }
+
+    /** */
+    @Provides
+    @Named(QS_FOOTER)
+    static FooterActionsController providesQSFooterActionsController(
+            FooterActionsControllerBuilder footerActionsControllerBuilder,
+            @Named(QS_FOOTER) FooterActionsView qsFooterActionsView) {
+        return footerActionsControllerBuilder
+                .withView(qsFooterActionsView)
+                .withButtonsVisibleWhen(ExpansionState.EXPANDED)
+                .build();
+    }
+
+    /** */
+    @Provides
     @QSScope
     static QSFooter providesQSFooter(QSFooterViewController qsFooterViewController) {
         qsFooterViewController.init();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index 2e771d6..b1cd03c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -116,6 +116,9 @@
                     : icon.getInvisibleDrawable(mContext) : null;
             int padding = icon != null ? icon.getPadding() : 0;
             if (d != null) {
+                if (d.getConstantState() != null) {
+                    d = d.getConstantState().newDrawable();
+                }
                 d.setAutoMirrored(false);
                 d.setLayoutDirection(getLayoutDirection());
             }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 70685a6..222539d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -195,9 +195,6 @@
             // sibling methods to have special behavior for labelContainer.
             labelContainer.forceUnspecifiedMeasure = true
             secondaryLabel.alpha = 0f
-            // Do not marque in QQS
-            label.ellipsize = TextUtils.TruncateAt.END
-            secondaryLabel.ellipsize = TextUtils.TruncateAt.END
         }
         setLabelColor(getLabelColorForState(QSTile.State.DEFAULT_STATE))
         setSecondaryLabelColor(getSecondaryLabelColorForState(QSTile.State.DEFAULT_STATE))
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 4b13015..04f089d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -150,11 +150,7 @@
         }
 
         List<CastDevice> activeDevices = getActiveDevices();
-        // We want to pop up the media route selection dialog if we either have no active devices
-        // (neither routes nor projection), or if we have an active route. In other cases, we assume
-        // that a projection is active. This is messy, but this tile never correctly handled the
-        // case where multiple devices were active :-/.
-        if (activeDevices.isEmpty() || (activeDevices.get(0).tag instanceof RouteInfo)) {
+        if (willPopDetail()) {
             mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
                 showDetail(true);
             });
@@ -163,6 +159,15 @@
         }
     }
 
+    // We want to pop up the media route selection dialog if we either have no active devices
+    // (neither routes nor projection), or if we have an active route. In other cases, we assume
+    // that a projection is active. This is messy, but this tile never correctly handled the
+    // case where multiple devices were active :-/.
+    private boolean willPopDetail() {
+        List<CastDevice> activeDevices = getActiveDevices();
+        return activeDevices.isEmpty() || (activeDevices.get(0).tag instanceof RouteInfo);
+    }
+
     private List<CastDevice> getActiveDevices() {
         ArrayList<CastDevice> activeDevices = new ArrayList<>();
         for (CastDevice device : mController.getCastDevices()) {
@@ -234,10 +239,12 @@
             state.contentDescription = state.contentDescription + ","
                     + mContext.getString(R.string.accessibility_quick_settings_open_details);
             state.expandedAccessibilityClassName = Button.class.getName();
+            state.forceExpandIcon = willPopDetail();
         } else {
             state.state = Tile.STATE_UNAVAILABLE;
             String noWifi = mContext.getString(R.string.quick_settings_cast_no_wifi);
             state.secondaryLabel = noWifi;
+            state.forceExpandIcon = false;
         }
         state.stateDescription = state.stateDescription + ", " + state.secondaryLabel;
         mDetailAdapter.updateItems(devices);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index 7cb1421..cc9e748 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -51,7 +51,9 @@
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
 import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
 import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
@@ -66,15 +68,16 @@
 /** Quick settings tile: Internet **/
 public class InternetTile extends QSTileImpl<SignalState> {
     private static final Intent WIFI_SETTINGS = new Intent(Settings.ACTION_WIFI_SETTINGS);
-    private static final Intent INTERNET_PANEL =
-            new Intent(Settings.Panel.ACTION_INTERNET_CONNECTIVITY);
 
     protected final NetworkController mController;
+    private final AccessPointController mAccessPointController;
     private final DataUsageController mDataController;
     // The last updated tile state, 0: mobile, 1: wifi, 2: ethernet.
     private int mLastTileState = -1;
 
     protected final InternetSignalCallback mSignalCallback = new InternetSignalCallback();
+    private final InternetDialogFactory mInternetDialogFactory;
+    final Handler mHandler;
 
     @Inject
     public InternetTile(
@@ -86,11 +89,16 @@
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger,
-            NetworkController networkController
+            NetworkController networkController,
+            AccessPointController accessPointController,
+            InternetDialogFactory internetDialogFactory
     ) {
         super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
                 statusBarStateController, activityStarter, qsLogger);
+        mInternetDialogFactory = internetDialogFactory;
+        mHandler = mainHandler;
         mController = networkController;
+        mAccessPointController = accessPointController;
         mDataController = mController.getMobileDataController();
         mController.observe(getLifecycle(), mSignalCallback);
     }
@@ -114,7 +122,9 @@
 
     @Override
     protected void handleClick(@Nullable View view) {
-        mActivityStarter.postStartActivityDismissingKeyguard(INTERNET_PANEL, 0);
+        mHandler.post(() -> mInternetDialogFactory.create(true,
+                mAccessPointController.canConfigMobileData(),
+                mAccessPointController.canConfigWifi()));
     }
 
     @Override
@@ -429,7 +439,7 @@
                 state.icon = ResourceIcon.get(cb.mWifiSignalIconId);
             }
         } else if (cb.mNoDefaultNetwork) {
-            if (cb.mNoNetworksAvailable) {
+            if (cb.mNoNetworksAvailable || !cb.mEnabled) {
                 state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable);
                 state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable);
             } else {
@@ -489,7 +499,7 @@
             state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable);
             state.secondaryLabel = r.getString(R.string.status_bar_airplane);
         } else if (cb.mNoDefaultNetwork) {
-            if (cb.mNoNetworksAvailable) {
+            if (cb.mNoNetworksAvailable || !mSignalCallback.mWifiInfo.mEnabled) {
                 state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable);
                 state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable);
             } else {
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 82b6c0c..d9919bd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -131,22 +131,16 @@
                 Intent intent = new Intent(mContext, WalletActivity.class)
                         .setAction(Intent.ACTION_VIEW)
                         .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
-                if (mKeyguardStateController.isUnlocked()) {
-                    mActivityStarter.startActivity(intent, true /* dismissShade */,
-                            animationController);
-                } else {
-                    mHost.collapsePanels();
-                    // Do not use ActivityStarter here because the WalletActivity is required to be
-                    // started without prompting keyguard when the device is locked.
-                    mContext.startActivity(intent);
-                }
+                mActivityStarter.startActivity(intent, true /* dismissShade */,
+                        animationController, true /* showOverLockscreenWhenLocked */);
             } else {
-                if (mController.getWalletClient().createWalletIntent() == null) {
+                Intent intent = mController.getWalletClient().createWalletIntent();
+                if (intent == null) {
                     Log.w(TAG, "Could not get intent of the wallet app.");
                     return;
                 }
                 mActivityStarter.postStartActivityDismissingKeyguard(
-                        mController.getWalletClient().createWalletIntent(),
+                        intent,
                         /* delay= */ 0,
                         animationController);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
new file mode 100644
index 0000000..4e897d9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.dialog;
+
+import static com.android.wifitrackerlib.WifiEntry.SECURITY_NONE;
+import static com.android.wifitrackerlib.WifiEntry.SECURITY_OWE;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.text.Html;
+import android.text.TextUtils;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.settingslib.Utils;
+import com.android.settingslib.wifi.WifiUtils;
+import com.android.systemui.R;
+import com.android.wifitrackerlib.WifiEntry;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Adapter for showing Wi-Fi networks.
+ */
+public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.InternetViewHolder> {
+
+    private static final String TAG = "InternetAdapter";
+    private static final String ACTION_WIFI_DIALOG = "com.android.settings.WIFI_DIALOG";
+    private static final String EXTRA_CHOSEN_WIFI_ENTRY_KEY = "key_chosen_wifientry_key";
+    private static final String EXTRA_CONNECT_FOR_CALLER = "connect_for_caller";
+
+    private final InternetDialogController mInternetDialogController;
+    private List<WifiEntry> mWifiEntries;
+    private int mWifiEntriesCount;
+
+    protected View mHolderView;
+    protected Context mContext;
+
+    public InternetAdapter(InternetDialogController controller) {
+        mInternetDialogController = controller;
+    }
+
+    @Override
+    public InternetViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
+            int viewType) {
+        mContext = viewGroup.getContext();
+        mHolderView = LayoutInflater.from(mContext).inflate(R.layout.internet_list_item,
+                viewGroup, false);
+        return new InternetViewHolder(mHolderView, mInternetDialogController);
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull InternetViewHolder viewHolder, int position) {
+        if (mWifiEntries == null || position >= mWifiEntriesCount) {
+            return;
+        }
+        viewHolder.onBind(mWifiEntries.get(position));
+    }
+
+    /**
+     * Updates the Wi-Fi networks.
+     *
+     * @param wifiEntries the updated Wi-Fi entries.
+     * @param wifiEntriesCount the total number of Wi-Fi entries.
+     */
+    public void setWifiEntries(@Nullable List<WifiEntry> wifiEntries, int wifiEntriesCount) {
+        mWifiEntries = wifiEntries;
+        mWifiEntriesCount = wifiEntriesCount;
+    }
+
+    /**
+     * Gets the total number of Wi-Fi networks.
+     *
+     * @return The total number of Wi-Fi entries.
+     */
+    @Override
+    public int getItemCount() {
+        return mWifiEntriesCount;
+    }
+
+    /**
+     * ViewHolder for binding Wi-Fi view.
+     */
+    static class InternetViewHolder extends RecyclerView.ViewHolder {
+
+        final LinearLayout mContainerLayout;
+        final LinearLayout mWifiListLayout;
+        final LinearLayout mWifiNetworkLayout;
+        final ImageView mWifiIcon;
+        final TextView mWifiTitleText;
+        final TextView mWifiSummaryText;
+        final ImageView mWifiLockedIcon;
+        final Context mContext;
+        final InternetDialogController mInternetDialogController;
+
+        protected WifiUtils.InternetIconInjector mWifiIconInjector;
+
+        InternetViewHolder(View view, InternetDialogController internetDialogController) {
+            super(view);
+            mContext = view.getContext();
+            mInternetDialogController = internetDialogController;
+            mContainerLayout = view.requireViewById(R.id.internet_container);
+            mWifiListLayout = view.requireViewById(R.id.wifi_list);
+            mWifiNetworkLayout = view.requireViewById(R.id.wifi_network_layout);
+            mWifiIcon = view.requireViewById(R.id.wifi_icon);
+            mWifiTitleText = view.requireViewById(R.id.wifi_title);
+            mWifiSummaryText = view.requireViewById(R.id.wifi_summary);
+            mWifiLockedIcon = view.requireViewById(R.id.wifi_locked_icon);
+            mWifiIconInjector = mInternetDialogController.getWifiIconInjector();
+        }
+
+        void onBind(WifiEntry wifiEntry) {
+            int security = wifiEntry.getSecurity();
+            try {
+                mWifiIcon.setImageDrawable(getWifiDrawable(wifiEntry));
+                if (isOpenNetwork(security)) {
+                    mWifiLockedIcon.setVisibility(View.GONE);
+                } else {
+                    mWifiLockedIcon.setVisibility(View.VISIBLE);
+                    mWifiLockedIcon.setImageDrawable(
+                            mContext.getDrawable(R.drawable.ic_friction_lock_closed));
+                }
+            } catch (Throwable throwable) {
+                throwable.printStackTrace();
+            }
+
+            setWifiNetworkLayout(wifiEntry.getTitle(),
+                    Html.fromHtml(wifiEntry.getSummary(false), Html.FROM_HTML_MODE_LEGACY));
+
+            mWifiListLayout.setOnClickListener(v -> {
+                if (wifiEntry.shouldEditBeforeConnect()) {
+                    final Intent intent = new Intent(ACTION_WIFI_DIALOG);
+                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
+                    intent.putExtra(EXTRA_CHOSEN_WIFI_ENTRY_KEY, wifiEntry.getKey());
+                    intent.putExtra(EXTRA_CONNECT_FOR_CALLER, false);
+                    mContext.startActivity(intent);
+                }
+                mInternetDialogController.connect(wifiEntry);
+            });
+        }
+
+        /** Return true if this is an open network AccessPoint. */
+        boolean isOpenNetwork(int security) {
+            return security == SECURITY_NONE
+                    || security == SECURITY_OWE;
+        }
+
+        void setWifiNetworkLayout(CharSequence title, CharSequence summary) {
+            mWifiNetworkLayout.setVisibility(View.VISIBLE);
+            mWifiTitleText.setText(title);
+            if (TextUtils.isEmpty(summary)) {
+                mWifiSummaryText.setVisibility(View.GONE);
+                return;
+            } else {
+                mWifiSummaryText.setVisibility(View.VISIBLE);
+            }
+            mWifiSummaryText.setText(summary);
+        }
+
+        Drawable getWifiDrawable(@NonNull WifiEntry wifiEntry) throws Throwable {
+            if (wifiEntry.getLevel() == WifiEntry.WIFI_LEVEL_UNREACHABLE) {
+                return null;
+            }
+            final Drawable drawable = mWifiIconInjector.getIcon(wifiEntry.shouldShowXLevelIcon(),
+                    wifiEntry.getLevel());
+            if (drawable == null) {
+                return null;
+            }
+            drawable.setTint(
+                    Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorTertiary));
+            final AtomicReference<Drawable> shared = new AtomicReference<>();
+            shared.set(drawable);
+            return shared.get();
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
new file mode 100644
index 0000000..4d0cbd1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -0,0 +1,599 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.qs.tiles.dialog;
+
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+
+import static com.android.systemui.Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyDisplayInfo;
+import android.telephony.TelephonyManager;
+import android.text.Html;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.Window;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.annotation.WorkerThread;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
+import com.android.settingslib.Utils;
+import com.android.systemui.Prefs;
+import com.android.systemui.R;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.wifitrackerlib.WifiEntry;
+
+import java.util.List;
+
+/**
+ * Dialog for showing mobile network, connected Wi-Fi network and Wi-Fi networks.
+ */
+@SysUISingleton
+public class InternetDialog extends SystemUIDialog implements
+        InternetDialogController.InternetDialogCallback, Window.Callback {
+    private static final String TAG = "InternetDialog";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    static final long PROGRESS_DELAY_MS = 2000L;
+
+    private final Handler mHandler;
+    private final LinearLayoutManager mLayoutManager;
+
+    @VisibleForTesting
+    protected InternetAdapter mAdapter;
+    @VisibleForTesting
+    protected WifiManager mWifiManager;
+    @VisibleForTesting
+    protected View mDialogView;
+    @VisibleForTesting
+    protected boolean mCanConfigWifi;
+
+    private InternetDialogFactory mInternetDialogFactory;
+    private SubscriptionManager mSubscriptionManager;
+    private TelephonyManager mTelephonyManager;
+    private AlertDialog mAlertDialog;
+    private UiEventLogger mUiEventLogger;
+    private Context mContext;
+    private InternetDialogController mInternetDialogController;
+    private TextView mInternetDialogTitle;
+    private TextView mInternetDialogSubTitle;
+    private View mDivider;
+    private ProgressBar mProgressBar;
+    private LinearLayout mInternetDialogLayout;
+    private LinearLayout mInternetListLayout;
+    private LinearLayout mConnectedWifListLayout;
+    private LinearLayout mConnectedWifList;
+    private LinearLayout mMobileNetworkLayout;
+    private LinearLayout mMobileNetworkList;
+    private LinearLayout mTurnWifiOnLayout;
+    private TextView mWifiToggleTitleText;
+    private LinearLayout mSeeAllLayout;
+    private RecyclerView mWifiRecyclerView;
+    private ImageView mConnectedWifiIcon;
+    private ImageView mWifiSettingsIcon;
+    private TextView mConnectedWifiTitleText;
+    private TextView mConnectedWifiSummaryText;
+    private ImageView mSignalIcon;
+    private TextView mMobileTitleText;
+    private TextView mMobileSummaryText;
+    private Switch mMobileDataToggle;
+    private Switch mWiFiToggle;
+    private Button mDoneButton;
+    private Drawable mBackgroundOn;
+    private int mListMaxHeight;
+    private int mLayoutWidth;
+    private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    private boolean mCanConfigMobileData;
+
+    // Wi-Fi entries
+    protected WifiEntry mConnectedWifiEntry;
+    protected int mWifiEntriesCount;
+
+    // Wi-Fi scanning progress bar
+    protected boolean mIsProgressBarVisible;
+    protected boolean mIsSearchingHidden;
+    protected final Runnable mHideProgressBarRunnable = () -> {
+        setProgressBarVisible(false);
+    };
+    protected Runnable mHideSearchingRunnable = () -> {
+        mIsSearchingHidden = true;
+        mInternetDialogSubTitle.setText(getSubtitleText());
+    };
+
+    private final ViewTreeObserver.OnGlobalLayoutListener mInternetListLayoutListener = () -> {
+        // Set max height for list
+        if (mInternetDialogLayout.getHeight() > mListMaxHeight) {
+            ViewGroup.LayoutParams params = mInternetDialogLayout.getLayoutParams();
+            params.height = mListMaxHeight;
+            mInternetDialogLayout.setLayoutParams(params);
+        }
+    };
+
+    public InternetDialog(Context context, InternetDialogFactory internetDialogFactory,
+            InternetDialogController internetDialogController, boolean canConfigMobileData,
+            boolean canConfigWifi, boolean aboveStatusBar, UiEventLogger uiEventLogger,
+            @Main Handler handler) {
+        super(context, R.style.Theme_SystemUI_Dialog_Internet);
+        if (DEBUG) {
+            Log.d(TAG, "Init InternetDialog");
+        }
+        mContext = context;
+        mHandler = handler;
+        mInternetDialogFactory = internetDialogFactory;
+        mInternetDialogController = internetDialogController;
+        mSubscriptionManager = mInternetDialogController.getSubscriptionManager();
+        mDefaultDataSubId = mInternetDialogController.getDefaultDataSubscriptionId();
+        mTelephonyManager = mInternetDialogController.getTelephonyManager();
+        mWifiManager = mInternetDialogController.getWifiManager();
+        mCanConfigMobileData = canConfigMobileData;
+        mCanConfigWifi = canConfigWifi;
+
+        mLayoutManager = new LinearLayoutManager(mContext) {
+            @Override
+            public boolean canScrollVertically() {
+                return false;
+            }
+        };
+        mListMaxHeight = context.getResources().getDimensionPixelSize(
+                R.dimen.internet_dialog_list_max_height);
+        mLayoutWidth = context.getResources().getDimensionPixelSize(
+                R.dimen.internet_dialog_list_max_width);
+        mUiEventLogger = uiEventLogger;
+        mAdapter = new InternetAdapter(mInternetDialogController);
+        if (!aboveStatusBar) {
+            getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+        }
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        if (DEBUG) {
+            Log.d(TAG, "onCreate");
+        }
+        mUiEventLogger.log(InternetDialogEvent.INTERNET_DIALOG_SHOW);
+        mDialogView = LayoutInflater.from(mContext).inflate(R.layout.internet_connectivity_dialog,
+                null);
+        final Window window = getWindow();
+        final WindowManager.LayoutParams layoutParams = window.getAttributes();
+        layoutParams.gravity = Gravity.BOTTOM;
+        // Move down the dialog to overlay the navigation bar.
+        layoutParams.setFitInsetsTypes(
+                layoutParams.getFitInsetsTypes() & ~WindowInsets.Type.navigationBars());
+        layoutParams.setFitInsetsSides(WindowInsets.Side.all());
+        layoutParams.setFitInsetsIgnoringVisibility(true);
+        window.setAttributes(layoutParams);
+        window.setContentView(mDialogView);
+        //Only fix the width for large screen or tablet.
+        window.setLayout(mContext.getResources().getDimensionPixelSize(
+                R.dimen.internet_dialog_list_max_width), ViewGroup.LayoutParams.WRAP_CONTENT);
+        window.setWindowAnimations(R.style.Animation_InternetDialog);
+        window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+        window.addFlags(FLAG_LAYOUT_NO_LIMITS);
+
+        mInternetDialogLayout = mDialogView.requireViewById(R.id.internet_connectivity_dialog);
+        mInternetDialogTitle = mDialogView.requireViewById(R.id.internet_dialog_title);
+        mInternetDialogSubTitle = mDialogView.requireViewById(R.id.internet_dialog_subtitle);
+        mDivider = mDialogView.requireViewById(R.id.divider);
+        mProgressBar = mDialogView.requireViewById(R.id.wifi_searching_progress);
+        mInternetListLayout = mDialogView.requireViewById(R.id.internet_list);
+        mMobileNetworkLayout = mDialogView.requireViewById(R.id.mobile_network_layout);
+        mMobileNetworkList = mDialogView.requireViewById(R.id.mobile_network_list);
+        mTurnWifiOnLayout = mDialogView.requireViewById(R.id.turn_on_wifi_layout);
+        mWifiToggleTitleText = mDialogView.requireViewById(R.id.wifi_toggle_title);
+        mConnectedWifListLayout = mDialogView.requireViewById(R.id.wifi_connected_layout);
+        mConnectedWifList = mDialogView.requireViewById(R.id.wifi_connected_list);
+        mConnectedWifiIcon = mDialogView.requireViewById(R.id.wifi_connected_icon);
+        mConnectedWifiTitleText = mDialogView.requireViewById(R.id.wifi_connected_title);
+        mConnectedWifiSummaryText = mDialogView.requireViewById(R.id.wifi_connected_summary);
+        mWifiSettingsIcon = mDialogView.requireViewById(R.id.wifi_settings_icon);
+        mWifiRecyclerView = mDialogView.requireViewById(R.id.wifi_list_layout);
+        mSeeAllLayout = mDialogView.requireViewById(R.id.see_all_layout);
+        mDoneButton = mDialogView.requireViewById(R.id.done);
+        mSignalIcon = mDialogView.requireViewById(R.id.signal_icon);
+        mMobileTitleText = mDialogView.requireViewById(R.id.mobile_title);
+        mMobileSummaryText = mDialogView.requireViewById(R.id.mobile_summary);
+        mMobileDataToggle = mDialogView.requireViewById(R.id.mobile_toggle);
+        mWiFiToggle = mDialogView.requireViewById(R.id.wifi_toggle);
+        mBackgroundOn = mContext.getDrawable(R.drawable.settingslib_switch_bar_bg_on);
+        mInternetDialogLayout.getViewTreeObserver().addOnGlobalLayoutListener(
+                mInternetListLayoutListener);
+        mInternetDialogTitle.setText(getDialogTitleText());
+        mInternetDialogTitle.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
+
+        setOnClickListener();
+        mTurnWifiOnLayout.setBackground(null);
+        mWifiRecyclerView.setLayoutManager(mLayoutManager);
+        mWifiRecyclerView.setAdapter(mAdapter);
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        if (DEBUG) {
+            Log.d(TAG, "onStart");
+        }
+        mInternetDialogController.onStart(this, mCanConfigWifi);
+        if (!mCanConfigWifi) {
+            hideWifiViews();
+        }
+    }
+
+    @VisibleForTesting
+    void hideWifiViews() {
+        setProgressBarVisible(false);
+        mTurnWifiOnLayout.setVisibility(View.GONE);
+        mConnectedWifListLayout.setVisibility(View.GONE);
+        mWifiRecyclerView.setVisibility(View.GONE);
+        mSeeAllLayout.setVisibility(View.GONE);
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        if (DEBUG) {
+            Log.d(TAG, "onStop");
+        }
+        mHandler.removeCallbacks(mHideProgressBarRunnable);
+        mHandler.removeCallbacks(mHideSearchingRunnable);
+        mMobileNetworkLayout.setOnClickListener(null);
+        mMobileDataToggle.setOnCheckedChangeListener(null);
+        mConnectedWifListLayout.setOnClickListener(null);
+        mSeeAllLayout.setOnClickListener(null);
+        mWiFiToggle.setOnCheckedChangeListener(null);
+        mDoneButton.setOnClickListener(null);
+        mInternetDialogController.onStop();
+        mInternetDialogFactory.destroyDialog();
+    }
+
+    @Override
+    public void dismissDialog() {
+        if (DEBUG) {
+            Log.d(TAG, "dismissDialog");
+        }
+        mInternetDialogFactory.destroyDialog();
+        dismiss();
+    }
+
+    void updateDialog() {
+        if (DEBUG) {
+            Log.d(TAG, "updateDialog");
+        }
+        if (mInternetDialogController.isAirplaneModeEnabled()) {
+            mInternetDialogSubTitle.setVisibility(View.GONE);
+        } else {
+            mInternetDialogSubTitle.setText(getSubtitleText());
+        }
+        setMobileDataLayout(mInternetDialogController.activeNetworkIsCellular());
+
+        if (!mCanConfigWifi) {
+            return;
+        }
+
+        showProgressBar();
+        final boolean isDeviceLocked = mInternetDialogController.isDeviceLocked();
+        final boolean isWifiEnabled = mWifiManager.isWifiEnabled();
+        updateWifiToggle(isWifiEnabled, isDeviceLocked);
+        updateConnectedWifi(isWifiEnabled, isDeviceLocked);
+
+        final int visibility = (isDeviceLocked || !isWifiEnabled || mWifiEntriesCount <= 0)
+                ? View.GONE : View.VISIBLE;
+        mWifiRecyclerView.setVisibility(visibility);
+        mSeeAllLayout.setVisibility(visibility);
+    }
+
+    private void setOnClickListener() {
+        mMobileNetworkLayout.setOnClickListener(v -> {
+            if (mInternetDialogController.isMobileDataEnabled()
+                    && !mInternetDialogController.isDeviceLocked()) {
+                if (!mInternetDialogController.activeNetworkIsCellular()) {
+                    mInternetDialogController.connectCarrierNetwork();
+                }
+            }
+        });
+        mMobileDataToggle.setOnCheckedChangeListener(
+                (buttonView, isChecked) -> {
+                    if (!isChecked && shouldShowMobileDialog()) {
+                        showTurnOffMobileDialog();
+                    } else if (!shouldShowMobileDialog()) {
+                        mInternetDialogController.setMobileDataEnabled(mContext, mDefaultDataSubId,
+                                isChecked, false);
+                    }
+                });
+        mConnectedWifListLayout.setOnClickListener(v -> onClickConnectedWifi());
+        mSeeAllLayout.setOnClickListener(v -> onClickSeeMoreButton());
+        mWiFiToggle.setOnCheckedChangeListener(
+                (buttonView, isChecked) -> {
+                    buttonView.setChecked(isChecked);
+                    mWifiManager.setWifiEnabled(isChecked);
+                });
+        mDoneButton.setOnClickListener(v -> dismiss());
+    }
+
+    private void setMobileDataLayout(boolean isCellularNetwork) {
+        if (mInternetDialogController.isAirplaneModeEnabled()
+                || !mInternetDialogController.hasCarrier()) {
+            mMobileNetworkLayout.setVisibility(View.GONE);
+        } else {
+            mMobileDataToggle.setChecked(mInternetDialogController.isMobileDataEnabled());
+            mMobileNetworkLayout.setVisibility(View.VISIBLE);
+            mMobileTitleText.setText(getMobileNetworkTitle());
+            if (!TextUtils.isEmpty(getMobileNetworkSummary())) {
+                mMobileSummaryText.setText(
+                        Html.fromHtml(getMobileNetworkSummary(), Html.FROM_HTML_MODE_LEGACY));
+                mMobileSummaryText.setVisibility(View.VISIBLE);
+            } else {
+                mMobileSummaryText.setVisibility(View.GONE);
+            }
+            mSignalIcon.setImageDrawable(getSignalStrengthDrawable());
+            if (mInternetDialogController.isNightMode()) {
+                int titleColor = isCellularNetwork ? mContext.getColor(
+                        R.color.connected_network_primary_color) : Utils.getColorAttrDefaultColor(
+                        mContext, android.R.attr.textColorPrimary);
+                int summaryColor = isCellularNetwork ? mContext.getColor(
+                        R.color.connected_network_secondary_color) : Utils.getColorAttrDefaultColor(
+                        mContext, android.R.attr.textColorSecondary);
+
+                mMobileTitleText.setTextColor(titleColor);
+                mMobileSummaryText.setTextColor(summaryColor);
+            }
+            mMobileNetworkLayout.setBackground(isCellularNetwork ? mBackgroundOn : null);
+
+            mMobileDataToggle.setVisibility(mCanConfigMobileData ? View.VISIBLE : View.INVISIBLE);
+        }
+    }
+
+    private void updateWifiToggle(boolean isWifiEnabled, boolean isDeviceLocked) {
+        mWiFiToggle.setChecked(isWifiEnabled);
+        if (isDeviceLocked && mInternetDialogController.isNightMode()) {
+            int titleColor = mConnectedWifiEntry != null ? mContext.getColor(
+                    R.color.connected_network_primary_color) : Utils.getColorAttrDefaultColor(
+                    mContext, android.R.attr.textColorPrimary);
+            mWifiToggleTitleText.setTextColor(titleColor);
+        }
+        mTurnWifiOnLayout.setBackground(
+                (isDeviceLocked && mConnectedWifiEntry != null) ? mBackgroundOn : null);
+    }
+
+    private void updateConnectedWifi(boolean isWifiEnabled, boolean isDeviceLocked) {
+        if (!isWifiEnabled || mConnectedWifiEntry == null || isDeviceLocked) {
+            mConnectedWifListLayout.setBackground(null);
+            mConnectedWifListLayout.setVisibility(View.GONE);
+            return;
+        }
+        mConnectedWifListLayout.setVisibility(View.VISIBLE);
+        mConnectedWifiTitleText.setText(mConnectedWifiEntry.getTitle());
+        mConnectedWifiSummaryText.setText(mConnectedWifiEntry.getSummary(false));
+        mConnectedWifiIcon.setImageDrawable(
+                mInternetDialogController.getInternetWifiDrawable(mConnectedWifiEntry));
+        if (mInternetDialogController.isNightMode()) {
+            mConnectedWifiTitleText.setTextColor(
+                    mContext.getColor(R.color.connected_network_primary_color));
+            mConnectedWifiSummaryText.setTextColor(
+                    mContext.getColor(R.color.connected_network_secondary_color));
+        }
+        mWifiSettingsIcon.setColorFilter(
+                mContext.getColor(R.color.connected_network_primary_color));
+        mConnectedWifListLayout.setBackground(mBackgroundOn);
+    }
+
+    void onClickConnectedWifi() {
+        mInternetDialogController.launchWifiNetworkDetailsSetting();
+    }
+
+    void onClickSeeMoreButton() {
+        mInternetDialogController.launchNetworkSetting();
+    }
+
+    CharSequence getDialogTitleText() {
+        return mInternetDialogController.getDialogTitleText();
+    }
+
+    CharSequence getSubtitleText() {
+        return mInternetDialogController.getSubtitleText(
+                mIsProgressBarVisible && !mIsSearchingHidden);
+    }
+
+    private Drawable getSignalStrengthDrawable() {
+        return mInternetDialogController.getSignalStrengthDrawable();
+    }
+
+    CharSequence getMobileNetworkTitle() {
+        return mInternetDialogController.getMobileNetworkTitle();
+    }
+
+    String getMobileNetworkSummary() {
+        return mInternetDialogController.getMobileNetworkSummary();
+    }
+
+    protected void showProgressBar() {
+        if (mWifiManager == null || !mWifiManager.isWifiEnabled()
+                || mInternetDialogController.isDeviceLocked()) {
+            setProgressBarVisible(false);
+            return;
+        }
+        setProgressBarVisible(true);
+        if (mConnectedWifiEntry != null || mWifiEntriesCount > 0) {
+            mHandler.postDelayed(mHideProgressBarRunnable, PROGRESS_DELAY_MS);
+        } else if (!mIsSearchingHidden) {
+            mHandler.postDelayed(mHideSearchingRunnable, PROGRESS_DELAY_MS);
+        }
+    }
+
+    private void setProgressBarVisible(boolean visible) {
+        if (mWifiManager.isWifiEnabled() && mAdapter.mHolderView != null
+                && mAdapter.mHolderView.isAttachedToWindow()) {
+            mIsProgressBarVisible = true;
+        }
+        mIsProgressBarVisible = visible;
+        mProgressBar.setVisibility(mIsProgressBarVisible ? View.VISIBLE : View.GONE);
+        mDivider.setVisibility(mIsProgressBarVisible ? View.GONE : View.VISIBLE);
+        mInternetDialogSubTitle.setText(getSubtitleText());
+    }
+
+    private boolean shouldShowMobileDialog() {
+        boolean flag = Prefs.getBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA,
+                false);
+        if (mInternetDialogController.isMobileDataEnabled() && !flag) {
+            return true;
+        }
+        return false;
+    }
+
+    private void showTurnOffMobileDialog() {
+        CharSequence carrierName =
+                mSubscriptionManager.getDefaultDataSubscriptionInfo().getCarrierName();
+        boolean isInService = mInternetDialogController.isVoiceStateInService();
+        if (TextUtils.isEmpty(carrierName) || !isInService) {
+            carrierName = mContext.getString(R.string.mobile_data_disable_message_default_carrier);
+        }
+        mAlertDialog = new Builder(mContext)
+                .setTitle(R.string.mobile_data_disable_title)
+                .setMessage(mContext.getString(R.string.mobile_data_disable_message, carrierName))
+                .setNegativeButton(android.R.string.cancel, (d, w) -> {
+                    mMobileDataToggle.setChecked(true);
+                })
+                .setPositiveButton(
+                        com.android.internal.R.string.alert_windows_notification_turn_off_action,
+                        (d, w) -> {
+                            mInternetDialogController.setMobileDataEnabled(mContext,
+                                    mDefaultDataSubId, false, false);
+                            mMobileDataToggle.setChecked(false);
+                            Prefs.putBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA, true);
+                        })
+                .create();
+        mAlertDialog.setOnCancelListener(dialog -> mMobileDataToggle.setChecked(true));
+        mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+        SystemUIDialog.setShowForAllUsers(mAlertDialog, true);
+        SystemUIDialog.registerDismissListener(mAlertDialog);
+        SystemUIDialog.setWindowOnTop(mAlertDialog);
+        mAlertDialog.show();
+    }
+
+    @Override
+    public void onRefreshCarrierInfo() {
+        mHandler.post(() -> updateDialog());
+    }
+
+    @Override
+    public void onSimStateChanged() {
+        mHandler.post(() -> updateDialog());
+    }
+
+    @Override
+    public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
+        mHandler.post(() -> updateDialog());
+    }
+
+    @Override
+    public void onSubscriptionsChanged(int defaultDataSubId) {
+        mDefaultDataSubId = defaultDataSubId;
+        mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
+        mHandler.post(() -> updateDialog());
+    }
+
+    @Override
+    public void onServiceStateChanged(ServiceState serviceState) {
+        mHandler.post(() -> updateDialog());
+    }
+
+    @Override
+    @WorkerThread
+    public void onDataConnectionStateChanged(int state, int networkType) {
+        mHandler.post(() -> updateDialog());
+    }
+
+    @Override
+    public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+        mHandler.post(() -> updateDialog());
+    }
+
+    @Override
+    public void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo) {
+        mHandler.post(() -> updateDialog());
+    }
+
+    @Override
+    @WorkerThread
+    public void onAccessPointsChanged(@Nullable List<WifiEntry> wifiEntries,
+            @Nullable WifiEntry connectedEntry) {
+        mConnectedWifiEntry = connectedEntry;
+        mWifiEntriesCount = wifiEntries == null ? 0 : wifiEntries.size();
+        mAdapter.setWifiEntries(wifiEntries, mWifiEntriesCount);
+        mHandler.post(() -> {
+            mAdapter.notifyDataSetChanged();
+            updateDialog();
+        });
+    }
+
+    @Override
+    public void onWindowFocusChanged(boolean hasFocus) {
+        super.onWindowFocusChanged(hasFocus);
+        if (mAlertDialog != null && !mAlertDialog.isShowing()) {
+            if (!hasFocus && isShowing()) {
+                dismiss();
+            }
+        }
+    }
+
+    public enum InternetDialogEvent implements UiEventLogger.UiEventEnum {
+        @UiEvent(doc = "The Internet dialog became visible on the screen.")
+        INTERNET_DIALOG_SHOW(843);
+
+        private final int mId;
+
+        InternetDialogEvent(int id) {
+            mId = id;
+        }
+
+        @Override
+        public int getId() {
+            return mId;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
new file mode 100644
index 0000000..dfe78de8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -0,0 +1,939 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.dialog;
+
+import static com.android.settingslib.mobile.MobileMappings.getIconKey;
+import static com.android.settingslib.mobile.MobileMappings.mapIconSets;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Configuration;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.provider.Settings;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
+import android.telephony.TelephonyDisplayInfo;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Gravity;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.annotation.WorkerThread;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.settingslib.DeviceInfoUtils;
+import com.android.settingslib.Utils;
+import com.android.settingslib.graph.SignalDrawable;
+import com.android.settingslib.mobile.MobileMappings;
+import com.android.settingslib.net.SignalStrengthUtil;
+import com.android.settingslib.wifi.WifiUtils;
+import com.android.systemui.R;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
+import com.android.systemui.util.settings.GlobalSettings;
+import com.android.wifitrackerlib.MergedCarrierEntry;
+import com.android.wifitrackerlib.WifiEntry;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import javax.inject.Inject;
+
+public class InternetDialogController implements WifiEntry.DisconnectCallback,
+        NetworkController.AccessPointController.AccessPointCallback {
+
+    private static final String TAG = "InternetDialogController";
+    private static final String ACTION_NETWORK_PROVIDER_SETTINGS =
+            "android.settings.NETWORK_PROVIDER_SETTINGS";
+    private static final String EXTRA_CHOSEN_WIFI_ENTRY_KEY = "key_chosen_wifientry_key";
+    public static final Drawable EMPTY_DRAWABLE = new ColorDrawable(Color.TRANSPARENT);
+    public static final int NO_CELL_DATA_TYPE_ICON = 0;
+    private static final int SUBTITLE_TEXT_WIFI_IS_OFF = R.string.wifi_is_off;
+    private static final int SUBTITLE_TEXT_TAP_A_NETWORK_TO_CONNECT =
+            R.string.tap_a_network_to_connect;
+    private static final int SUBTITLE_TEXT_UNLOCK_TO_VIEW_NETWORKS =
+            R.string.unlock_to_view_networks;
+    private static final int SUBTITLE_TEXT_SEARCHING_FOR_NETWORKS =
+            R.string.wifi_empty_list_wifi_on;
+    private static final int SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE =
+            R.string.non_carrier_network_unavailable;
+    private static final int SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE =
+            R.string.all_network_unavailable;
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    static final int MAX_WIFI_ENTRY_COUNT = 4;
+
+    private WifiManager mWifiManager;
+    private Context mContext;
+    private SubscriptionManager mSubscriptionManager;
+    private TelephonyManager mTelephonyManager;
+    private ConnectivityManager mConnectivityManager;
+    private TelephonyDisplayInfo mTelephonyDisplayInfo =
+            new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
+                    TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
+    private Handler mHandler;
+    private MobileMappings.Config mConfig = null;
+    private Executor mExecutor;
+    private AccessPointController mAccessPointController;
+    private IntentFilter mConnectionStateFilter;
+    private InternetDialogCallback mCallback;
+    private WifiEntry mConnectedEntry;
+    private int mWifiEntriesCount;
+    private UiEventLogger mUiEventLogger;
+    private BroadcastDispatcher mBroadcastDispatcher;
+    private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private GlobalSettings mGlobalSettings;
+    private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+    @VisibleForTesting
+    protected ActivityStarter mActivityStarter;
+    @VisibleForTesting
+    protected SubscriptionManager.OnSubscriptionsChangedListener mOnSubscriptionsChangedListener;
+    @VisibleForTesting
+    protected InternetTelephonyCallback mInternetTelephonyCallback;
+    @VisibleForTesting
+    protected WifiUtils.InternetIconInjector mWifiIconInjector;
+    @VisibleForTesting
+    protected boolean mCanConfigWifi;
+    @VisibleForTesting
+    protected KeyguardStateController mKeyguardStateController;
+
+    private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
+            new KeyguardUpdateMonitorCallback() {
+                @Override
+                public void onRefreshCarrierInfo() {
+                    mCallback.onRefreshCarrierInfo();
+                }
+
+                @Override
+                public void onSimStateChanged(int subId, int slotId, int simState) {
+                    mCallback.onSimStateChanged();
+                }
+            };
+
+    protected List<SubscriptionInfo> getSubscriptionInfo() {
+        return mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false);
+    }
+
+    @Inject
+    public InternetDialogController(@NonNull Context context, UiEventLogger uiEventLogger,
+            ActivityStarter starter, AccessPointController accessPointController,
+            SubscriptionManager subscriptionManager, TelephonyManager telephonyManager,
+            @Nullable WifiManager wifiManager, ConnectivityManager connectivityManager,
+            @Main Handler handler, @Main Executor mainExecutor,
+            BroadcastDispatcher broadcastDispatcher, KeyguardUpdateMonitor keyguardUpdateMonitor,
+            GlobalSettings globalSettings, KeyguardStateController keyguardStateController) {
+        if (DEBUG) {
+            Log.d(TAG, "Init InternetDialogController");
+        }
+        mHandler = handler;
+        mExecutor = mainExecutor;
+        mContext = context;
+        mGlobalSettings = globalSettings;
+        mWifiManager = wifiManager;
+        mTelephonyManager = telephonyManager;
+        mConnectivityManager = connectivityManager;
+        mSubscriptionManager = subscriptionManager;
+        mBroadcastDispatcher = broadcastDispatcher;
+        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        mKeyguardStateController = keyguardStateController;
+        mConnectionStateFilter = new IntentFilter();
+        mConnectionStateFilter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
+        mUiEventLogger = uiEventLogger;
+        mActivityStarter = starter;
+        mAccessPointController = accessPointController;
+        mConfig = MobileMappings.Config.readConfig(mContext);
+        mWifiIconInjector = new WifiUtils.InternetIconInjector(mContext);
+    }
+
+    void onStart(@NonNull InternetDialogCallback callback, boolean canConfigWifi) {
+        if (DEBUG) {
+            Log.d(TAG, "onStart");
+        }
+        mCallback = callback;
+        mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
+        mAccessPointController.addAccessPointCallback(this);
+        mBroadcastDispatcher.registerReceiver(mConnectionStateReceiver, mConnectionStateFilter,
+                mExecutor);
+        // Listen the subscription changes
+        mOnSubscriptionsChangedListener = new InternetOnSubscriptionChangedListener();
+        mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor,
+                mOnSubscriptionsChangedListener);
+        mDefaultDataSubId = getDefaultDataSubscriptionId();
+        if (DEBUG) {
+            Log.d(TAG, "Init, SubId: " + mDefaultDataSubId);
+        }
+        mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
+        mInternetTelephonyCallback = new InternetTelephonyCallback();
+        mTelephonyManager.registerTelephonyCallback(mExecutor, mInternetTelephonyCallback);
+        // Listen the connectivity changes
+        mConnectivityManager.registerNetworkCallback(new NetworkRequest.Builder()
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                .build(), new DataConnectivityListener(), mHandler);
+        mCanConfigWifi = canConfigWifi;
+        scanWifiAccessPoints();
+    }
+
+    void onStop() {
+        if (DEBUG) {
+            Log.d(TAG, "onStop");
+        }
+        mBroadcastDispatcher.unregisterReceiver(mConnectionStateReceiver);
+        mTelephonyManager.unregisterTelephonyCallback(mInternetTelephonyCallback);
+        mSubscriptionManager.removeOnSubscriptionsChangedListener(
+                mOnSubscriptionsChangedListener);
+        mAccessPointController.removeAccessPointCallback(this);
+        mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
+    }
+
+    @VisibleForTesting
+    boolean isAirplaneModeEnabled() {
+        return mGlobalSettings.getInt(Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
+    }
+
+    @VisibleForTesting
+    protected int getDefaultDataSubscriptionId() {
+        return mSubscriptionManager.getDefaultDataSubscriptionId();
+    }
+
+    @VisibleForTesting
+    protected Intent getSettingsIntent() {
+        return new Intent(ACTION_NETWORK_PROVIDER_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+    }
+
+    protected Intent getWifiDetailsSettingsIntent() {
+        String key = mConnectedEntry == null ? null : mConnectedEntry.getKey();
+        if (TextUtils.isEmpty(key)) {
+            if (DEBUG) {
+                Log.d(TAG, "connected entry's key is empty");
+            }
+            return null;
+        }
+        return WifiUtils.getWifiDetailsSettingsIntent(key);
+    }
+
+    CharSequence getDialogTitleText() {
+        if (isAirplaneModeEnabled()) {
+            return mContext.getText(R.string.airplane_mode);
+        }
+        return mContext.getText(R.string.quick_settings_internet_label);
+    }
+
+    CharSequence getSubtitleText(boolean isProgressBarVisible) {
+        if (isAirplaneModeEnabled()) {
+            return null;
+        }
+
+        if (mCanConfigWifi && !mWifiManager.isWifiEnabled()) {
+            // When the airplane mode is off and Wi-Fi is disabled.
+            //   Sub-Title: Wi-Fi is off
+            if (DEBUG) {
+                Log.d(TAG, "Airplane mode off + Wi-Fi off.");
+            }
+            return mContext.getText(SUBTITLE_TEXT_WIFI_IS_OFF);
+        }
+
+        if (isDeviceLocked()) {
+            // When the device is locked.
+            //   Sub-Title: Unlock to view networks
+            if (DEBUG) {
+                Log.d(TAG, "The device is locked.");
+            }
+            return mContext.getText(SUBTITLE_TEXT_UNLOCK_TO_VIEW_NETWORKS);
+        }
+
+        if (mConnectedEntry != null || mWifiEntriesCount > 0) {
+            return mCanConfigWifi ? mContext.getText(SUBTITLE_TEXT_TAP_A_NETWORK_TO_CONNECT) : null;
+        }
+
+        if (mCanConfigWifi && isProgressBarVisible) {
+            // When the Wi-Fi scan result callback is received
+            //   Sub-Title: Searching for networks...
+            return mContext.getText(SUBTITLE_TEXT_SEARCHING_FOR_NETWORKS);
+        }
+
+        // Sub-Title:
+        // show non_carrier_network_unavailable
+        //   - while Wi-Fi on + no Wi-Fi item
+        //   - while Wi-Fi on + no Wi-Fi item + mobile data off
+        // show all_network_unavailable:
+        //   - while Wi-Fi on + no Wi-Fi item + no carrier item
+        //   - while Wi-Fi on + no Wi-Fi item + service is out of service
+        //   - while Wi-Fi on + no Wi-Fi item + mobile data on + no carrier data.
+        if (DEBUG) {
+            Log.d(TAG, "No Wi-Fi item.");
+        }
+        if (!hasCarrier() || (!isVoiceStateInService() && !isDataStateInService())) {
+            if (DEBUG) {
+                Log.d(TAG, "No carrier or service is out of service.");
+            }
+            return mContext.getText(SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE);
+        }
+
+        if (mCanConfigWifi && !isMobileDataEnabled()) {
+            if (DEBUG) {
+                Log.d(TAG, "Mobile data off");
+            }
+            return mContext.getText(SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE);
+        }
+
+        if (!activeNetworkIsCellular()) {
+            if (DEBUG) {
+                Log.d(TAG, "No carrier data.");
+            }
+            return mContext.getText(SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE);
+        }
+
+        if (mCanConfigWifi) {
+            return mContext.getText(SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE);
+        }
+        return null;
+    }
+
+    Drawable getInternetWifiDrawable(@NonNull WifiEntry wifiEntry) {
+        final Drawable drawable =
+                mWifiIconInjector.getIcon(wifiEntry.shouldShowXLevelIcon(), wifiEntry.getLevel());
+        if (drawable == null) {
+            return null;
+        }
+        drawable.setTint(mContext.getColor(R.color.connected_network_primary_color));
+        return drawable;
+    }
+
+    boolean isNightMode() {
+        return (mContext.getResources().getConfiguration().uiMode
+                & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
+    }
+
+    Drawable getSignalStrengthDrawable() {
+        Drawable drawable = mContext.getDrawable(
+                R.drawable.ic_signal_strength_zero_bar_no_internet);
+        try {
+            if (mTelephonyManager == null) {
+                if (DEBUG) {
+                    Log.d(TAG, "TelephonyManager is null");
+                }
+                return drawable;
+            }
+
+            if (isDataStateInService() || isVoiceStateInService()) {
+                AtomicReference<Drawable> shared = new AtomicReference<>();
+                shared.set(getSignalStrengthDrawableWithLevel());
+                drawable = shared.get();
+            }
+
+            drawable.setTint(activeNetworkIsCellular() ? mContext.getColor(
+                    R.color.connected_network_primary_color) : Utils.getColorAttrDefaultColor(
+                    mContext, android.R.attr.textColorTertiary));
+        } catch (Throwable e) {
+            e.printStackTrace();
+        }
+        return drawable;
+    }
+
+    /**
+     * To get the signal bar icon with level.
+     *
+     * @return The Drawable which is a signal bar icon with level.
+     */
+    Drawable getSignalStrengthDrawableWithLevel() {
+        final SignalStrength strength = mTelephonyManager.getSignalStrength();
+        int level = (strength == null) ? 0 : strength.getLevel();
+        int numLevels = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
+        if (mSubscriptionManager != null && shouldInflateSignalStrength(mDefaultDataSubId)) {
+            level += 1;
+            numLevels += 1;
+        }
+        return getSignalStrengthIcon(mContext, level, numLevels, NO_CELL_DATA_TYPE_ICON, false);
+    }
+
+    Drawable getSignalStrengthIcon(Context context, int level, int numLevels,
+            int iconType, boolean cutOut) {
+        Log.d(TAG, "getSignalStrengthIcon");
+        final SignalDrawable signalDrawable = new SignalDrawable(context);
+        signalDrawable.setLevel(
+                SignalDrawable.getState(level, numLevels, cutOut));
+
+        // Make the network type drawable
+        final Drawable networkDrawable =
+                iconType == NO_CELL_DATA_TYPE_ICON
+                        ? EMPTY_DRAWABLE
+                        : context.getResources().getDrawable(iconType, context.getTheme());
+
+        // Overlay the two drawables
+        final Drawable[] layers = {networkDrawable, signalDrawable};
+        final int iconSize =
+                context.getResources().getDimensionPixelSize(R.dimen.signal_strength_icon_size);
+
+        final LayerDrawable icons = new LayerDrawable(layers);
+        // Set the network type icon at the top left
+        icons.setLayerGravity(0 /* index of networkDrawable */, Gravity.TOP | Gravity.LEFT);
+        // Set the signal strength icon at the bottom right
+        icons.setLayerGravity(1 /* index of SignalDrawable */, Gravity.BOTTOM | Gravity.RIGHT);
+        icons.setLayerSize(1 /* index of SignalDrawable */, iconSize, iconSize);
+        icons.setTintList(Utils.getColorAttr(context, android.R.attr.textColorTertiary));
+        return icons;
+    }
+
+    private boolean shouldInflateSignalStrength(int subId) {
+        return SignalStrengthUtil.shouldInflateSignalStrength(mContext, subId);
+    }
+
+    private CharSequence getUniqueSubscriptionDisplayName(int subscriptionId, Context context) {
+        final Map<Integer, CharSequence> displayNames = getUniqueSubscriptionDisplayNames(context);
+        return displayNames.getOrDefault(subscriptionId, "");
+    }
+
+    private Map<Integer, CharSequence> getUniqueSubscriptionDisplayNames(Context context) {
+        class DisplayInfo {
+            public SubscriptionInfo subscriptionInfo;
+            public CharSequence originalName;
+            public CharSequence uniqueName;
+        }
+
+        // Map of SubscriptionId to DisplayName
+        final Supplier<Stream<DisplayInfo>> originalInfos =
+                () -> getSubscriptionInfo()
+                        .stream()
+                        .filter(i -> {
+                            // Filter out null values.
+                            return (i != null && i.getDisplayName() != null);
+                        })
+                        .map(i -> {
+                            DisplayInfo info = new DisplayInfo();
+                            info.subscriptionInfo = i;
+                            info.originalName = i.getDisplayName().toString().trim();
+                            return info;
+                        });
+
+        // A Unique set of display names
+        Set<CharSequence> uniqueNames = new HashSet<>();
+        // Return the set of duplicate names
+        final Set<CharSequence> duplicateOriginalNames = originalInfos.get()
+                .filter(info -> !uniqueNames.add(info.originalName))
+                .map(info -> info.originalName)
+                .collect(Collectors.toSet());
+
+        // If a display name is duplicate, append the final 4 digits of the phone number.
+        // Creates a mapping of Subscription id to original display name + phone number display name
+        final Supplier<Stream<DisplayInfo>> uniqueInfos = () -> originalInfos.get().map(info -> {
+            if (duplicateOriginalNames.contains(info.originalName)) {
+                // This may return null, if the user cannot view the phone number itself.
+                final String phoneNumber = DeviceInfoUtils.getBidiFormattedPhoneNumber(context,
+                        info.subscriptionInfo);
+                String lastFourDigits = "";
+                if (phoneNumber != null) {
+                    lastFourDigits = (phoneNumber.length() > 4)
+                            ? phoneNumber.substring(phoneNumber.length() - 4) : phoneNumber;
+                }
+
+                if (TextUtils.isEmpty(lastFourDigits)) {
+                    info.uniqueName = info.originalName;
+                } else {
+                    info.uniqueName = info.originalName + " " + lastFourDigits;
+                }
+
+            } else {
+                info.uniqueName = info.originalName;
+            }
+            return info;
+        });
+
+        // Check uniqueness a second time.
+        // We might not have had permission to view the phone numbers.
+        // There might also be multiple phone numbers whose last 4 digits the same.
+        uniqueNames.clear();
+        final Set<CharSequence> duplicatePhoneNames = uniqueInfos.get()
+                .filter(info -> !uniqueNames.add(info.uniqueName))
+                .map(info -> info.uniqueName)
+                .collect(Collectors.toSet());
+
+        return uniqueInfos.get().map(info -> {
+            if (duplicatePhoneNames.contains(info.uniqueName)) {
+                info.uniqueName = info.originalName + " "
+                        + info.subscriptionInfo.getSubscriptionId();
+            }
+            return info;
+        }).collect(Collectors.toMap(
+                info -> info.subscriptionInfo.getSubscriptionId(),
+                info -> info.uniqueName));
+    }
+
+    CharSequence getMobileNetworkTitle() {
+        return getUniqueSubscriptionDisplayName(mDefaultDataSubId, mContext);
+    }
+
+    String getMobileNetworkSummary() {
+        String description = getNetworkTypeDescription(mContext, mConfig,
+                mTelephonyDisplayInfo, mDefaultDataSubId);
+        return getMobileSummary(mContext, mTelephonyManager, description);
+    }
+
+    /**
+     * Get currently description of mobile network type.
+     */
+    private String getNetworkTypeDescription(Context context, MobileMappings.Config config,
+            TelephonyDisplayInfo telephonyDisplayInfo, int subId) {
+        String iconKey = getIconKey(telephonyDisplayInfo);
+
+        if (mapIconSets(config) == null || mapIconSets(config).get(iconKey) == null) {
+            if (DEBUG) {
+                Log.d(TAG, "The description of network type is empty.");
+            }
+            return "";
+        }
+
+        int resId = mapIconSets(config).get(iconKey).dataContentDescription;
+        return resId != 0
+                ? SubscriptionManager.getResourcesForSubId(context, subId).getString(resId) : "";
+    }
+
+    private String getMobileSummary(Context context, TelephonyManager telephonyManager,
+            String networkTypeDescription) {
+        if (!isMobileDataEnabled()) {
+            return context.getString(R.string.mobile_data_off_summary);
+        }
+        if (!isDataStateInService()) {
+            return context.getString(R.string.mobile_data_no_connection);
+        }
+        String summary = networkTypeDescription;
+        if (activeNetworkIsCellular()) {
+            summary = context.getString(R.string.preference_summary_default_combination,
+                    context.getString(R.string.mobile_data_connection_active),
+                    networkTypeDescription);
+        }
+        return summary;
+    }
+
+    void launchNetworkSetting() {
+        mCallback.dismissDialog();
+        mActivityStarter.postStartActivityDismissingKeyguard(getSettingsIntent(), 0);
+    }
+
+    void launchWifiNetworkDetailsSetting() {
+        Intent intent = getWifiDetailsSettingsIntent();
+        if (intent != null) {
+            mCallback.dismissDialog();
+            mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
+        }
+    }
+
+    void connectCarrierNetwork() {
+        final MergedCarrierEntry mergedCarrierEntry =
+                mAccessPointController.getMergedCarrierEntry();
+        if (mergedCarrierEntry != null && mergedCarrierEntry.canConnect()) {
+            mergedCarrierEntry.connect(null /* ConnectCallback */);
+        }
+    }
+
+    WifiManager getWifiManager() {
+        return mWifiManager;
+    }
+
+    TelephonyManager getTelephonyManager() {
+        return mTelephonyManager;
+    }
+
+    SubscriptionManager getSubscriptionManager() {
+        return mSubscriptionManager;
+    }
+
+    /**
+     * @return whether there is the carrier item in the slice.
+     */
+    boolean hasCarrier() {
+        if (mSubscriptionManager == null) {
+            if (DEBUG) {
+                Log.d(TAG, "SubscriptionManager is null, can not check carrier.");
+            }
+            return false;
+        }
+
+        if (isAirplaneModeEnabled() || mTelephonyManager == null
+                || mSubscriptionManager.getActiveSubscriptionIdList().length <= 0) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Return {@code true} if mobile data is enabled
+     */
+    boolean isMobileDataEnabled() {
+        if (mTelephonyManager == null || !mTelephonyManager.isDataEnabled()) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Set whether to enable data for {@code subId}, also whether to disable data for other
+     * subscription
+     */
+    void setMobileDataEnabled(Context context, int subId, boolean enabled,
+            boolean disableOtherSubscriptions) {
+        if (mTelephonyManager == null) {
+            if (DEBUG) {
+                Log.d(TAG, "TelephonyManager is null, can not set mobile data.");
+            }
+            return;
+        }
+
+        if (mSubscriptionManager == null) {
+            if (DEBUG) {
+                Log.d(TAG, "SubscriptionManager is null, can not set mobile data.");
+            }
+            return;
+        }
+
+        mTelephonyManager.setDataEnabled(enabled);
+        if (disableOtherSubscriptions) {
+            final List<SubscriptionInfo> subInfoList =
+                    mSubscriptionManager.getActiveSubscriptionInfoList();
+            if (subInfoList != null) {
+                for (SubscriptionInfo subInfo : subInfoList) {
+                    // We never disable mobile data for opportunistic subscriptions.
+                    if (subInfo.getSubscriptionId() != subId && !subInfo.isOpportunistic()) {
+                        context.getSystemService(TelephonyManager.class).createForSubscriptionId(
+                                subInfo.getSubscriptionId()).setDataEnabled(false);
+                    }
+                }
+            }
+        }
+    }
+
+    boolean isDataStateInService() {
+        final ServiceState serviceState = mTelephonyManager.getServiceState();
+        NetworkRegistrationInfo regInfo =
+                (serviceState == null) ? null : serviceState.getNetworkRegistrationInfo(
+                        NetworkRegistrationInfo.DOMAIN_PS,
+                        AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+        return (regInfo == null) ? false : regInfo.isRegistered();
+    }
+
+    boolean isVoiceStateInService() {
+        if (mTelephonyManager == null) {
+            if (DEBUG) {
+                Log.d(TAG, "TelephonyManager is null, can not detect voice state.");
+            }
+            return false;
+        }
+
+        final ServiceState serviceState = mTelephonyManager.getServiceState();
+        return serviceState != null
+                && serviceState.getState() == serviceState.STATE_IN_SERVICE;
+    }
+
+    public boolean isDeviceLocked() {
+        return !mKeyguardStateController.isUnlocked();
+    }
+
+    boolean activeNetworkIsCellular() {
+        if (mConnectivityManager == null) {
+            if (DEBUG) {
+                Log.d(TAG, "ConnectivityManager is null, can not check active network.");
+            }
+            return false;
+        }
+
+        final Network activeNetwork = mConnectivityManager.getActiveNetwork();
+        if (activeNetwork == null) {
+            return false;
+        }
+        final NetworkCapabilities networkCapabilities =
+                mConnectivityManager.getNetworkCapabilities(activeNetwork);
+        if (networkCapabilities == null) {
+            return false;
+        }
+        return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
+    }
+
+    boolean connect(WifiEntry ap) {
+        if (ap == null) {
+            if (DEBUG) {
+                Log.d(TAG, "No Wi-Fi ap to connect.");
+            }
+            return false;
+        }
+
+        if (ap.getWifiConfiguration() != null) {
+            if (DEBUG) {
+                Log.d(TAG, "connect networkId=" + ap.getWifiConfiguration().networkId);
+            }
+        } else {
+            if (DEBUG) {
+                Log.d(TAG, "connect to unsaved network " + ap.getTitle());
+            }
+        }
+        ap.connect(new WifiEntryConnectCallback(mActivityStarter, mContext, ap));
+        return false;
+    }
+
+    static class WifiEntryConnectCallback implements WifiEntry.ConnectCallback {
+        final ActivityStarter mActivityStarter;
+        final Context mContext;
+        final WifiEntry mWifiEntry;
+
+        WifiEntryConnectCallback(ActivityStarter activityStarter, Context context,
+                WifiEntry connectWifiEntry) {
+            mActivityStarter = activityStarter;
+            mContext = context;
+            mWifiEntry = connectWifiEntry;
+        }
+
+        @Override
+        public void onConnectResult(@ConnectStatus int status) {
+            if (DEBUG) {
+                Log.d(TAG, "onConnectResult " + status);
+            }
+
+            if (status == WifiEntry.ConnectCallback.CONNECT_STATUS_FAILURE_NO_CONFIG) {
+                final Intent intent = new Intent("com.android.settings.WIFI_DIALOG")
+                        .putExtra(EXTRA_CHOSEN_WIFI_ENTRY_KEY, mWifiEntry.getKey());
+                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                mActivityStarter.startActivity(intent, true);
+            } else if (status == CONNECT_STATUS_FAILURE_UNKNOWN) {
+                Toast.makeText(mContext, R.string.wifi_failed_connect_message,
+                        Toast.LENGTH_SHORT).show();
+            } else {
+                if (DEBUG) {
+                    Log.d(TAG, "connect failure reason=" + status);
+                }
+            }
+        }
+    }
+
+    private void scanWifiAccessPoints() {
+        if (mCanConfigWifi) {
+            mAccessPointController.scanForAccessPoints();
+        }
+    }
+
+    @Override
+    @WorkerThread
+    public void onAccessPointsChanged(List<WifiEntry> accessPoints) {
+        if (!mCanConfigWifi) {
+            return;
+        }
+
+        if (accessPoints == null || accessPoints.size() == 0) {
+            mConnectedEntry = null;
+            mWifiEntriesCount = 0;
+            if (mCallback != null) {
+                mCallback.onAccessPointsChanged(null /* wifiEntries */, null /* connectedEntry */);
+            }
+            return;
+        }
+
+        boolean hasConnectedWifi = false;
+        final int accessPointSize = accessPoints.size();
+        for (int i = 0; i < accessPointSize; i++) {
+            WifiEntry wifiEntry = accessPoints.get(i);
+            if (wifiEntry.isDefaultNetwork() && wifiEntry.hasInternetAccess()) {
+                mConnectedEntry = wifiEntry;
+                hasConnectedWifi = true;
+                break;
+            }
+        }
+        if (!hasConnectedWifi) {
+            mConnectedEntry = null;
+        }
+
+        int count = MAX_WIFI_ENTRY_COUNT;
+        if (hasCarrier()) {
+            count -= 1;
+        }
+        if (hasConnectedWifi) {
+            count -= 1;
+        }
+        final List<WifiEntry> wifiEntries = accessPoints.stream()
+                .filter(wifiEntry -> (!wifiEntry.isDefaultNetwork()
+                        || !wifiEntry.hasInternetAccess()))
+                .limit(count)
+                .collect(Collectors.toList());
+        mWifiEntriesCount = wifiEntries == null ? 0 : wifiEntries.size();
+
+        if (mCallback != null) {
+            mCallback.onAccessPointsChanged(wifiEntries, mConnectedEntry);
+        }
+    }
+
+    @Override
+    public void onSettingsActivityTriggered(Intent settingsIntent) {
+    }
+
+    @Override
+    public void onDisconnectResult(int status) {
+    }
+
+    private class InternetTelephonyCallback extends TelephonyCallback implements
+            TelephonyCallback.DataConnectionStateListener,
+            TelephonyCallback.DisplayInfoListener,
+            TelephonyCallback.ServiceStateListener,
+            TelephonyCallback.SignalStrengthsListener {
+
+        @Override
+        public void onServiceStateChanged(@NonNull ServiceState serviceState) {
+            mCallback.onServiceStateChanged(serviceState);
+        }
+
+        @Override
+        public void onDataConnectionStateChanged(int state, int networkType) {
+            mCallback.onDataConnectionStateChanged(state, networkType);
+        }
+
+        @Override
+        public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength) {
+            mCallback.onSignalStrengthsChanged(signalStrength);
+        }
+
+        @Override
+        public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo) {
+            mTelephonyDisplayInfo = telephonyDisplayInfo;
+            mCallback.onDisplayInfoChanged(telephonyDisplayInfo);
+        }
+    }
+
+    private class InternetOnSubscriptionChangedListener
+            extends SubscriptionManager.OnSubscriptionsChangedListener {
+        InternetOnSubscriptionChangedListener() {
+            super();
+        }
+
+        @Override
+        public void onSubscriptionsChanged() {
+            updateListener();
+        }
+    }
+
+    private class DataConnectivityListener extends ConnectivityManager.NetworkCallback {
+        @Override
+        @WorkerThread
+        public void onCapabilitiesChanged(@NonNull Network network,
+                @NonNull NetworkCapabilities networkCapabilities) {
+            if (mCanConfigWifi) {
+                for (int transport : networkCapabilities.getTransportTypes()) {
+                    if (transport == NetworkCapabilities.TRANSPORT_WIFI) {
+                        scanWifiAccessPoints();
+                        break;
+                    }
+                }
+            }
+            final Network activeNetwork = mConnectivityManager.getActiveNetwork();
+            if (activeNetwork != null && activeNetwork.equals(network)) {
+                // update UI
+                mCallback.onCapabilitiesChanged(network, networkCapabilities);
+            }
+        }
+    }
+
+    private final BroadcastReceiver mConnectionStateReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (action.equals(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
+                if (DEBUG) {
+                    Log.d(TAG, "ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED");
+                }
+                updateListener();
+            }
+        }
+    };
+
+    private void updateListener() {
+        int defaultDataSubId = getDefaultDataSubscriptionId();
+        if (mDefaultDataSubId == getDefaultDataSubscriptionId()) {
+            if (DEBUG) {
+                Log.d(TAG, "DDS: no change");
+            }
+            return;
+        }
+
+        mDefaultDataSubId = defaultDataSubId;
+        if (DEBUG) {
+            Log.d(TAG, "DDS: defaultDataSubId:" + mDefaultDataSubId);
+        }
+        if (SubscriptionManager.isUsableSubscriptionId(mDefaultDataSubId)) {
+            mTelephonyManager.unregisterTelephonyCallback(mInternetTelephonyCallback);
+            mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
+            mTelephonyManager.registerTelephonyCallback(mHandler::post,
+                    mInternetTelephonyCallback);
+            mCallback.onSubscriptionsChanged(mDefaultDataSubId);
+        }
+    }
+
+    public WifiUtils.InternetIconInjector getWifiIconInjector() {
+        return mWifiIconInjector;
+    }
+
+    interface InternetDialogCallback {
+
+        void onRefreshCarrierInfo();
+
+        void onSimStateChanged();
+
+        void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities);
+
+        void onSubscriptionsChanged(int defaultDataSubId);
+
+        void onServiceStateChanged(ServiceState serviceState);
+
+        void onDataConnectionStateChanged(int state, int networkType);
+
+        void onSignalStrengthsChanged(SignalStrength signalStrength);
+
+        void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo);
+
+        void dismissDialog();
+
+        void onAccessPointsChanged(@Nullable List<WifiEntry> wifiEntries,
+                @Nullable WifiEntry connectedEntry);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
new file mode 100644
index 0000000..11c6980
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.qs.tiles.dialog
+
+import android.content.Context
+import android.os.Handler
+import android.util.Log
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import javax.inject.Inject
+
+private const val TAG = "InternetDialogFactory"
+private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
+
+/**
+ * Factory to create [InternetDialog] objects.
+ */
+@SysUISingleton
+class InternetDialogFactory @Inject constructor(
+    @Main private val handler: Handler,
+    private val internetDialogController: InternetDialogController,
+    private val context: Context,
+    private val uiEventLogger: UiEventLogger
+) {
+    companion object {
+        var internetDialog: InternetDialog? = null
+    }
+
+    /** Creates a [InternetDialog]. */
+    fun create(aboveStatusBar: Boolean, canConfigMobileData: Boolean, canConfigWifi: Boolean) {
+        if (internetDialog != null) {
+            if (DEBUG) {
+                Log.d(TAG, "InternetDialog is showing, do not create it twice.")
+            }
+            return
+        } else {
+            internetDialog = InternetDialog(context, this, internetDialogController,
+                    canConfigMobileData, canConfigWifi, aboveStatusBar, uiEventLogger, handler)
+            internetDialog?.show()
+        }
+    }
+
+    fun destroyDialog() {
+        if (DEBUG) {
+            Log.d(TAG, "destroyDialog")
+        }
+        internetDialog = null
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogUtil.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogUtil.java
new file mode 100644
index 0000000..6aaba99
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogUtil.java
@@ -0,0 +1,14 @@
+package com.android.systemui.qs.tiles.dialog;
+
+import android.content.Context;
+import android.util.FeatureFlagUtils;
+
+public class InternetDialogUtil {
+
+    public static boolean isProviderModelEnabled(Context context) {
+        if (context == null) {
+            return false;
+        }
+        return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index eb72296..a5fc5ab 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -78,6 +78,7 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.NavigationBar;
 import com.android.systemui.navigationbar.NavigationBarController;
@@ -530,6 +531,7 @@
             Optional<OneHanded> oneHandedOptional,
             BroadcastDispatcher broadcastDispatcher,
             ShellTransitions shellTransitions,
+            ScreenLifecycle screenLifecycle,
             Optional<StartingSurface> startingSurface,
             SmartspaceTransitionController smartspaceTransitionController) {
         super(broadcastDispatcher);
@@ -588,6 +590,13 @@
         // Listen for user setup
         startTracking();
 
+        screenLifecycle.addObserver(new ScreenLifecycle.Observer() {
+            @Override
+            public void onScreenTurnedOn() {
+                notifyScreenTurnedOn();
+            }
+        });
+
         // Connect to the service
         updateEnabledState();
         startConnectionToCurrentUser();
@@ -880,6 +889,21 @@
         }
     }
 
+    /**
+     * Notifies the Launcher that screen turned on and ready to use
+     */
+    public void notifyScreenTurnedOn() {
+        try {
+            if (mOverviewProxy != null) {
+                mOverviewProxy.onScreenTurnedOn();
+            } else {
+                Log.e(TAG_OPS, "Failed to get overview proxy for screen turned on event.");
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG_OPS, "Failed to call notifyScreenTurnedOn()", e);
+        }
+    }
+
     void notifyToggleRecentApps() {
         for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
             mConnectionCallbacks.get(i).onToggleRecentApps();
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java
index 0aa9d4d..5a6f2a2 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java
@@ -27,6 +27,7 @@
 import android.media.MediaRecorder;
 import android.media.projection.MediaProjection;
 import android.util.Log;
+import android.util.MathUtils;
 
 import java.io.IOException;
 import java.nio.ByteBuffer;
@@ -128,30 +129,64 @@
         mThread = new Thread(() -> {
             short[] bufferInternal = null;
             short[] bufferMic = null;
-            byte[] buffer = null;
+            byte[] buffer = new byte[size];
 
             if (mMic) {
                 bufferInternal = new short[size / 2];
                 bufferMic = new short[size / 2];
-            } else {
-                buffer = new byte[size];
             }
 
+            int readBytes = 0;
+            int readShortsInternal = 0;
+            int offsetShortsInternal = 0;
+            int readShortsMic = 0;
+            int offsetShortsMic = 0;
             while (true) {
-                int readBytes = 0;
-                int readShortsInternal = 0;
-                int readShortsMic = 0;
                 if (mMic) {
-                    readShortsInternal = mAudioRecord.read(bufferInternal, 0,
-                            bufferInternal.length);
-                    readShortsMic = mAudioRecordMic.read(bufferMic, 0, bufferMic.length);
+                    readShortsInternal = mAudioRecord.read(bufferInternal, offsetShortsInternal,
+                            bufferInternal.length - offsetShortsInternal);
+                    readShortsMic = mAudioRecordMic.read(
+                            bufferMic, offsetShortsMic, bufferMic.length - offsetShortsMic);
+
+                    // if both error, end the recording
+                    if (readShortsInternal < 0 && readShortsMic < 0) {
+                        break;
+                    }
+
+                    // if one has an errors, fill its buffer with zeros and assume it is mute
+                    // with the same size as the other buffer
+                    if (readShortsInternal < 0) {
+                        readShortsInternal = readShortsMic;
+                        offsetShortsInternal = offsetShortsMic;
+                        java.util.Arrays.fill(bufferInternal, (short) 0);
+                    }
+
+                    if (readShortsMic < 0) {
+                        readShortsMic = readShortsInternal;
+                        offsetShortsMic = offsetShortsInternal;
+                        java.util.Arrays.fill(bufferMic, (short) 0);
+                    }
+
+                    // Add offset (previous unmixed values) to the buffer
+                    readShortsInternal += offsetShortsInternal;
+                    readShortsMic += offsetShortsMic;
+
+                    int minShorts = Math.min(readShortsInternal, readShortsMic);
+                    readBytes = minShorts * 2;
 
                     // modify the volume
-                    bufferMic = scaleValues(bufferMic,
-                            readShortsMic, MIC_VOLUME_SCALE);
-                    readBytes = Math.min(readShortsInternal, readShortsMic) * 2;
-                    buffer = addAndConvertBuffers(bufferInternal, readShortsInternal, bufferMic,
-                            readShortsMic);
+                    // scale only mixed shorts
+                    scaleValues(bufferMic, minShorts, MIC_VOLUME_SCALE);
+                    // Mix the two buffers
+                    addAndConvertBuffers(bufferInternal, bufferMic, buffer, minShorts);
+
+                    // shift unmixed shorts to the beginning of the buffer
+                    shiftToStart(bufferInternal, minShorts, offsetShortsInternal);
+                    shiftToStart(bufferMic, minShorts, offsetShortsMic);
+
+                    // reset the offset for the next loop
+                    offsetShortsInternal = readShortsInternal - minShorts;
+                    offsetShortsMic = readShortsMic - minShorts;
                 } else {
                     readBytes = mAudioRecord.read(buffer, 0, buffer.length);
                 }
@@ -169,40 +204,31 @@
         });
     }
 
-    private short[] scaleValues(short[] buff, int len, float scale) {
-        for (int i = 0; i < len; i++) {
-            int oldValue = buff[i];
-            int newValue = (int) (buff[i] * scale);
-            if (newValue > Short.MAX_VALUE) {
-                newValue = Short.MAX_VALUE;
-            } else if (newValue < Short.MIN_VALUE) {
-                newValue = Short.MIN_VALUE;
-            }
-            buff[i] = (short) (newValue);
+    /**
+     * moves all bits from start to end to the beginning of the array
+     */
+    private void shiftToStart(short[] target, int start, int end) {
+        for (int i = 0; i  < end - start; i++) {
+            target[i] = target[start + i];
         }
-        return buff;
     }
-    private byte[] addAndConvertBuffers(short[] a1, int a1Limit, short[] a2, int a2Limit) {
-        int size = Math.max(a1Limit, a2Limit);
-        if (size < 0) return new byte[0];
-        byte[] buff = new byte[size * 2];
-        for (int i = 0; i < size; i++) {
-            int sum;
-            if (i > a1Limit) {
-                sum = a2[i];
-            } else if (i > a2Limit) {
-                sum = a1[i];
-            } else {
-                sum = (int) a1[i] + (int) a2[i];
-            }
 
-            if (sum > Short.MAX_VALUE) sum = Short.MAX_VALUE;
-            if (sum < Short.MIN_VALUE) sum = Short.MIN_VALUE;
-            int byteIndex = i * 2;
-            buff[byteIndex] = (byte) (sum & 0xff);
-            buff[byteIndex + 1] = (byte) ((sum >> 8) & 0xff);
+    private void scaleValues(short[] buff, int len, float scale) {
+        for (int i = 0; i < len; i++) {
+            int newValue = (int) (buff[i] * scale);
+            buff[i] = (short) MathUtils.constrain(newValue, Short.MIN_VALUE, Short.MAX_VALUE);
         }
-        return buff;
+    }
+
+    private void addAndConvertBuffers(short[] src1, short[] src2, byte[] dst, int sizeShorts) {
+        for (int i = 0; i < sizeShorts; i++) {
+            int sum;
+            sum = (short) MathUtils.constrain(
+                    (int) src1[i] + (int) src2[i], Short.MIN_VALUE, Short.MAX_VALUE);
+            int byteIndex = i * 2;
+            dst[byteIndex] = (byte) (sum & 0xff);
+            dst[byteIndex + 1] = (byte) ((sum >> 8) & 0xff);
+        }
     }
 
     private void encode(byte[] buffer, int readBytes) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
index 0eaef72..31d51f1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -154,6 +154,7 @@
     @Override
     public void onStart() {
         super.onStart();
+        mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_ACTIVITY_STARTED);
 
         if (mPreview.getDrawable() != null) {
             // We already have an image, so no need to try to load again.
@@ -245,6 +246,8 @@
     }
 
     private void onCachedImageLoaded(ImageLoader.Result imageResult) {
+        mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_ACTIVITY_CACHED_IMAGE_LOADED);
+
         BitmapDrawable drawable = new BitmapDrawable(getResources(), imageResult.bitmap);
         mPreview.setImageDrawable(drawable);
         mPreview.setAlpha(1f);
@@ -282,6 +285,8 @@
             finish();
         }
         if (isFinishing()) {
+            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_ACTIVITY_FINISHED);
+
             if (mScrollCaptureResponse != null) {
                 mScrollCaptureResponse.close();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 16872b0..3fbdc23 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -559,8 +559,8 @@
             mScreenshotView.reset();
         }
 
-        mScreenshotView.updateOrientation(mWindowManager.getCurrentWindowMetrics()
-                .getWindowInsets().getDisplayCutout());
+        mScreenshotView.updateOrientation(
+                mWindowManager.getCurrentWindowMetrics().getWindowInsets());
 
         mScreenBitmap = screenshot;
 
@@ -594,9 +594,8 @@
                             // Delay scroll capture eval a bit to allow the underlying activity
                             // to set up in the new orientation.
                             mScreenshotHandler.postDelayed(this::requestScrollCapture, 150);
-                            mScreenshotView.updateDisplayCutoutMargins(
-                                    mWindowManager.getCurrentWindowMetrics().getWindowInsets()
-                                            .getDisplayCutout());
+                            mScreenshotView.updateInsets(
+                                    mWindowManager.getCurrentWindowMetrics().getWindowInsets());
                             // screenshot animation calculations won't be valid anymore, so just end
                             if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) {
                                 mScreenshotAnimation.end();
@@ -660,7 +659,7 @@
                     + mLastScrollCaptureResponse.getWindowTitle() + "]");
 
             final ScrollCaptureResponse response = mLastScrollCaptureResponse;
-            mScreenshotView.showScrollChip(/* onClick */ () -> {
+            mScreenshotView.showScrollChip(response.getPackageName(), /* onClick */ () -> {
                 DisplayMetrics displayMetrics = new DisplayMetrics();
                 getDefaultDisplay().getRealMetrics(displayMetrics);
                 Bitmap newScreenshot = captureScreenshot(
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
index 5cf0188..169b28c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
@@ -71,7 +71,19 @@
     @UiEvent(doc = "User has shared a long screenshot")
     SCREENSHOT_LONG_SCREENSHOT_SHARE(689),
     @UiEvent(doc = "User has sent a long screenshot to the editor")
-    SCREENSHOT_LONG_SCREENSHOT_EDIT(690);
+    SCREENSHOT_LONG_SCREENSHOT_EDIT(690),
+    @UiEvent(doc = "A long screenshot capture has started")
+    SCREENSHOT_LONG_SCREENSHOT_STARTED(880),
+    @UiEvent(doc = "The long screenshot capture failed")
+    SCREENSHOT_LONG_SCREENSHOT_FAILURE(881),
+    @UiEvent(doc = "The long screenshot capture completed successfully")
+    SCREENSHOT_LONG_SCREENSHOT_COMPLETED(882),
+    @UiEvent(doc = "Long screenshot editor activity started")
+    SCREENSHOT_LONG_SCREENSHOT_ACTIVITY_STARTED(889),
+    @UiEvent(doc = "Long screenshot editor activity loaded a previously saved screenshot")
+    SCREENSHOT_LONG_SCREENSHOT_ACTIVITY_CACHED_IMAGE_LOADED(890),
+    @UiEvent(doc = "Long screenshot editor activity finished")
+    SCREENSHOT_LONG_SCREENSHOT_ACTIVITY_FINISHED(891);
 
     private final int mId;
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index e9e62f2..dfb39e3 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -118,8 +118,8 @@
     private static final long SCREENSHOT_TO_CORNER_SCALE_DURATION_MS = 234;
     private static final long SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS = 400;
     private static final long SCREENSHOT_ACTIONS_ALPHA_DURATION_MS = 100;
-    private static final long SCREENSHOT_DISMISS_Y_DURATION_MS = 350;
-    private static final long SCREENSHOT_DISMISS_ALPHA_DURATION_MS = 183;
+    private static final long SCREENSHOT_DISMISS_X_DURATION_MS = 350;
+    private static final long SCREENSHOT_DISMISS_ALPHA_DURATION_MS = 350;
     private static final long SCREENSHOT_DISMISS_ALPHA_OFFSET_MS = 50; // delay before starting fade
     private static final float SCREENSHOT_ACTIONS_START_SCALE_X = .7f;
     private static final float ROUNDED_CORNER_RADIUS = .25f;
@@ -242,19 +242,21 @@
     /**
      * Called to display the scroll action chip when support is detected.
      *
+     * @param packageName the owning package of the window to be captured
      * @param onClick the action to take when the chip is clicked.
      */
-    public void showScrollChip(Runnable onClick) {
+    public void showScrollChip(String packageName, Runnable onClick) {
         if (DEBUG_SCROLL) {
             Log.d(TAG, "Showing Scroll option");
         }
-        mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_IMPRESSION);
+        mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_IMPRESSION, 0, packageName);
         mScrollChip.setVisibility(VISIBLE);
         mScrollChip.setOnClickListener((v) -> {
             if (DEBUG_INPUT) {
                 Log.d(TAG, "scroll chip tapped");
             }
-            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_REQUESTED);
+            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_REQUESTED, 0,
+                    packageName);
             onClick.run();
         });
     }
@@ -414,21 +416,30 @@
         mScreenshotPreview.setImageDrawable(createScreenDrawable(mResources, bitmap, screenInsets));
     }
 
-    void updateDisplayCutoutMargins(DisplayCutout cutout) {
+    void updateInsets(WindowInsets insets) {
         int orientation = mContext.getResources().getConfiguration().orientation;
         mOrientationPortrait = (orientation == ORIENTATION_PORTRAIT);
         FrameLayout.LayoutParams p =
                 (FrameLayout.LayoutParams) mScreenshotStatic.getLayoutParams();
+        DisplayCutout cutout = insets.getDisplayCutout();
+        Insets navBarInsets = insets.getInsets(WindowInsets.Type.navigationBars());
         if (cutout == null) {
-            p.setMargins(0, 0, 0, 0);
+            p.setMargins(0, 0, 0, navBarInsets.bottom);
         } else {
             Insets waterfall = cutout.getWaterfallInsets();
             if (mOrientationPortrait) {
-                p.setMargins(waterfall.left, Math.max(cutout.getSafeInsetTop(), waterfall.top),
-                        waterfall.right, Math.max(cutout.getSafeInsetBottom(), waterfall.bottom));
+                p.setMargins(
+                        waterfall.left,
+                        Math.max(cutout.getSafeInsetTop(), waterfall.top),
+                        waterfall.right,
+                        Math.max(cutout.getSafeInsetBottom(),
+                                Math.max(navBarInsets.bottom, waterfall.bottom)));
             } else {
-                p.setMargins(Math.max(cutout.getSafeInsetLeft(), waterfall.left), waterfall.top,
-                        Math.max(cutout.getSafeInsetRight(), waterfall.right), waterfall.bottom);
+                p.setMargins(
+                        Math.max(cutout.getSafeInsetLeft(), waterfall.left),
+                        waterfall.top,
+                        Math.max(cutout.getSafeInsetRight(), waterfall.right),
+                        Math.max(navBarInsets.bottom, waterfall.bottom));
             }
         }
         mStaticLeftMargin = p.leftMargin;
@@ -436,10 +447,10 @@
         mScreenshotStatic.requestLayout();
     }
 
-    void updateOrientation(DisplayCutout cutout) {
+    void updateOrientation(WindowInsets insets) {
         int orientation = mContext.getResources().getConfiguration().orientation;
         mOrientationPortrait = (orientation == ORIENTATION_PORTRAIT);
-        updateDisplayCutoutMargins(cutout);
+        updateInsets(insets);
         int screenshotFixedSize =
                 mContext.getResources().getDimensionPixelSize(R.dimen.global_screenshot_x_scale);
         ViewGroup.LayoutParams params = mScreenshotPreview.getLayoutParams();
@@ -978,7 +989,6 @@
         mScrollingScrim.setVisibility(View.GONE);
         mScrollablePreview.setVisibility(View.GONE);
         mScreenshotStatic.setTranslationX(0);
-        mScreenshotPreview.setTranslationY(0);
         mScreenshotPreview.setContentDescription(
                 mContext.getResources().getString(R.string.screenshot_preview_description));
         mScreenshotPreview.setOnClickListener(null);
@@ -994,9 +1004,6 @@
         mSmartChips.clear();
         mQuickShareChip = null;
         setAlpha(1);
-        mDismissButton.setTranslationY(0);
-        mActionsContainer.setTranslationY(0);
-        mActionsContainerBackground.setTranslationY(0);
         mScreenshotSelectorView.stop();
     }
 
@@ -1024,22 +1031,19 @@
             setAlpha(1 - animation.getAnimatedFraction());
         });
 
-        ValueAnimator yAnim = ValueAnimator.ofFloat(0, 1);
-        yAnim.setInterpolator(mAccelerateInterpolator);
-        yAnim.setDuration(SCREENSHOT_DISMISS_Y_DURATION_MS);
-        float screenshotStartY = mScreenshotPreview.getTranslationY();
-        float dismissStartY = mDismissButton.getTranslationY();
-        yAnim.addUpdateListener(animation -> {
-            float yDelta = MathUtils.lerp(0, mDismissDeltaY, animation.getAnimatedFraction());
-            mScreenshotPreview.setTranslationY(screenshotStartY + yDelta);
-            mScreenshotPreviewBorder.setTranslationY(screenshotStartY + yDelta);
-            mDismissButton.setTranslationY(dismissStartY + yDelta);
-            mActionsContainer.setTranslationY(yDelta);
-            mActionsContainerBackground.setTranslationY(yDelta);
+        ValueAnimator xAnim = ValueAnimator.ofFloat(0, 1);
+        xAnim.setInterpolator(mAccelerateInterpolator);
+        xAnim.setDuration(SCREENSHOT_DISMISS_X_DURATION_MS);
+        float deltaX = mDirectionLTR
+                    ? -1 * (mScreenshotPreviewBorder.getX() + mScreenshotPreviewBorder.getWidth())
+                    : (mDisplayMetrics.widthPixels - mScreenshotPreviewBorder.getX());
+        xAnim.addUpdateListener(animation -> {
+            float currXDelta = MathUtils.lerp(0, deltaX, animation.getAnimatedFraction());
+            mScreenshotStatic.setTranslationX(currXDelta);
         });
 
         AnimatorSet animSet = new AnimatorSet();
-        animSet.play(yAnim).with(alphaAnim);
+        animSet.play(xAnim).with(alphaAnim);
 
         return animSet;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
index ce6e469..93e5021 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
@@ -45,9 +45,12 @@
 import androidx.concurrent.futures.CallbackToFutureAdapter.Completer;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.dagger.qualifiers.Background;
 
 import com.google.common.util.concurrent.ListenableFuture;
 
+import java.util.concurrent.Executor;
+
 import javax.inject.Inject;
 
 /**
@@ -63,6 +66,8 @@
 
     private static final String TAG = LogConfig.logTag(ScrollCaptureClient.class);
 
+    private final Executor mBgExecutor;
+
     /**
      * Represents the connection to a target window and provides a mechanism for requesting tiles.
      */
@@ -155,8 +160,10 @@
     private IBinder mHostWindowToken;
 
     @Inject
-    public ScrollCaptureClient(@UiContext Context context, IWindowManager windowManagerService) {
+    public ScrollCaptureClient(IWindowManager windowManagerService,
+            @Background Executor bgExecutor, @UiContext Context context) {
         requireNonNull(context.getDisplay(), "context must be associated with a Display!");
+        mBgExecutor = bgExecutor;
         mWindowManagerService = windowManagerService;
     }
 
@@ -220,21 +227,25 @@
                 return "";
             }
             SessionWrapper session = new SessionWrapper(connection, response.getWindowBounds(),
-                    response.getBoundsInWindow(), maxPages);
+                    response.getBoundsInWindow(), maxPages, mBgExecutor);
             session.start(completer);
             return "IScrollCaptureCallbacks#onCaptureStarted";
         });
     }
 
     private static class SessionWrapper extends IScrollCaptureCallbacks.Stub implements Session,
-            IBinder.DeathRecipient {
+            IBinder.DeathRecipient, ImageReader.OnImageAvailableListener {
 
         private IScrollCaptureConnection mConnection;
+        private final Executor mBgExecutor;
+        private final Object mLock = new Object();
 
         private ImageReader mReader;
         private final int mTileHeight;
         private final int mTileWidth;
         private Rect mRequestRect;
+        private Rect mCapturedArea;
+        private Image mCapturedImage;
         private boolean mStarted;
         private final int mTargetHeight;
 
@@ -247,7 +258,8 @@
         private Completer<Void> mEndCompleter;
 
         private SessionWrapper(IScrollCaptureConnection connection, Rect windowBounds,
-                Rect boundsInWindow, float maxPages) throws RemoteException {
+                Rect boundsInWindow, float maxPages, Executor bgExecutor)
+                throws RemoteException {
             mConnection = requireNonNull(connection);
             mConnection.asBinder().linkToDeath(SessionWrapper.this, 0);
             mWindowBounds = requireNonNull(windowBounds);
@@ -259,7 +271,7 @@
             mTileWidth = mBoundsInWindow.width();
             mTileHeight = pxPerTile / mBoundsInWindow.width();
             mTargetHeight = (int) (mBoundsInWindow.height() * maxPages);
-
+            mBgExecutor = bgExecutor;
             if (DEBUG_SCROLL) {
                 Log.d(TAG, "boundsInWindow: " + mBoundsInWindow);
                 Log.d(TAG, "tile size: " + mTileWidth + "x" + mTileHeight);
@@ -289,6 +301,7 @@
             mReader = ImageReader.newInstance(mTileWidth, mTileHeight, PixelFormat.RGBA_8888,
                     MAX_TILES, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
             mStartCompleter = completer;
+            mReader.setOnImageAvailableListenerWithExecutor(this, mBgExecutor);
             try {
                 mCancellationSignal = mConnection.startCapture(mReader.getSurface(), this);
                 completer.addCancellationListener(() -> {
@@ -339,9 +352,34 @@
 
         @BinderThread
         @Override
-        public void onImageRequestCompleted(int flags, Rect contentArea) {
-            Image image = mReader.acquireLatestImage();
-            mTileRequestCompleter.set(new CaptureResult(image, mRequestRect, contentArea));
+        public void onImageRequestCompleted(int flagsUnused, Rect contentArea) {
+            synchronized (mLock) {
+                mCapturedArea = contentArea;
+                if (mCapturedImage != null || (mCapturedArea == null || mCapturedArea.isEmpty())) {
+                    completeCaptureRequest();
+                }
+            }
+        }
+
+        /** @see ImageReader.OnImageAvailableListener */
+        @Override
+        public void onImageAvailable(ImageReader reader) {
+            synchronized (mLock) {
+                mCapturedImage = mReader.acquireLatestImage();
+                if (mCapturedArea != null) {
+                    completeCaptureRequest();
+                }
+            }
+        }
+
+        /** Produces a result for the caller as soon as both asynchronous results are received. */
+        private void completeCaptureRequest() {
+            CaptureResult result =
+                    new CaptureResult(mCapturedImage, mRequestRect, mCapturedArea);
+            mCapturedImage = null;
+            mRequestRect = null;
+            mCapturedArea = null;
+            mTileRequestCompleter.set(result);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index 6dc6874..ef7355a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -28,6 +28,7 @@
 import androidx.concurrent.futures.CallbackToFutureAdapter.Completer;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult;
 import com.android.systemui.screenshot.ScrollCaptureClient.Session;
@@ -61,6 +62,7 @@
     private final Context mContext;
     private final Executor mBgExecutor;
     private final ImageTileSet mImageTileSet;
+    private final UiEventLogger mEventLogger;
     private final ScrollCaptureClient mClient;
 
     private Completer<LongScreenshot> mCaptureCompleter;
@@ -69,6 +71,7 @@
     private Session mSession;
     private ListenableFuture<CaptureResult> mTileFuture;
     private ListenableFuture<Void> mEndFuture;
+    private String mWindowOwner;
 
     static class LongScreenshot {
         private final ImageTileSet mImageTileSet;
@@ -135,11 +138,12 @@
 
     @Inject
     ScrollCaptureController(Context context, @Background Executor bgExecutor,
-            ScrollCaptureClient client, ImageTileSet imageTileSet) {
+            ScrollCaptureClient client, ImageTileSet imageTileSet, UiEventLogger logger) {
         mContext = context;
         mBgExecutor = bgExecutor;
         mClient = client;
         mImageTileSet = imageTileSet;
+        mEventLogger = logger;
     }
 
     @VisibleForTesting
@@ -157,6 +161,7 @@
     ListenableFuture<LongScreenshot> run(ScrollCaptureResponse response) {
         return CallbackToFutureAdapter.getFuture(completer -> {
             mCaptureCompleter = completer;
+            mWindowOwner = response.getPackageName();
             mBgExecutor.execute(() -> {
                 float maxPages = Settings.Secure.getFloat(mContext.getContentResolver(),
                         SETTING_KEY_MAX_PAGES, MAX_PAGES_DEFAULT);
@@ -173,11 +178,13 @@
             if (LogConfig.DEBUG_SCROLL) {
                 Log.d(TAG, "got session " + mSession);
             }
+            mEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_STARTED, 0, mWindowOwner);
             requestNextTile(0);
         } catch (InterruptedException | ExecutionException e) {
             // Failure to start, propagate to caller
             Log.e(TAG, "session start failed!");
             mCaptureCompleter.setException(e);
+            mEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_FAILURE, 0, mWindowOwner);
         }
     }
 
@@ -297,6 +304,11 @@
         if (LogConfig.DEBUG_SCROLL) {
             Log.d(TAG, "finishCapture()");
         }
+        if (mImageTileSet.getHeight() > 0) {
+            mEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_COMPLETED, 0, mWindowOwner);
+        } else {
+            mEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_FAILURE, 0, mWindowOwner);
+        }
         mEndFuture = mSession.end();
         mEndFuture.addListener(() -> {
             if (LogConfig.DEBUG_SCROLL) {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
index 9e4a355..acc6ee1 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -30,7 +30,6 @@
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Handler;
-import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.RemoteException;
@@ -47,15 +46,16 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.RestrictedLockUtilsInternal;
-import com.android.systemui.Dependency;
 import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.settings.CurrentUserTracker;
+import com.android.systemui.statusbar.policy.BrightnessMirrorController;
 
 import java.util.ArrayList;
 
 import javax.inject.Inject;
 
-public class BrightnessController implements ToggleSlider.Listener {
+public class BrightnessController implements ToggleSlider.Listener, MirroredBrightnessController {
     private static final String TAG = "StatusBar.BrightnessController";
     private static final int SLIDER_ANIMATION_DURATION = 3000;
 
@@ -109,6 +109,11 @@
 
     private ValueAnimator mSliderAnimator;
 
+    @Override
+    public void setMirror(BrightnessMirrorController controller) {
+        mControl.setMirrorControllerAndMirror(controller);
+    }
+
     public interface BrightnessStateChangeCallback {
         /** Indicates that some of the brightness settings have changed */
         void onBrightnessLevelChanged();
@@ -282,12 +287,15 @@
         }
     };
 
-    public BrightnessController(Context context, ToggleSlider control,
-            BroadcastDispatcher broadcastDispatcher) {
+    public BrightnessController(
+            Context context,
+            ToggleSlider control,
+            BroadcastDispatcher broadcastDispatcher,
+            @Background Handler bgHandler) {
         mContext = context;
         mControl = control;
         mControl.setMax(GAMMA_SPACE_MAX);
-        mBackgroundHandler = new Handler((Looper) Dependency.get(Dependency.BG_LOOPER));
+        mBackgroundHandler = bgHandler;
         mUserTracker = new CurrentUserTracker(broadcastDispatcher) {
             @Override
             public void onUserSwitched(int newUserId) {
@@ -354,10 +362,9 @@
                 convertGammaToLinearFloat(value, minBacklight, maxBacklight),
                 maxBacklight);
         if (stopTracking) {
-            // Log brightness as a value between 0-1000 directly correlated to brightnesses 0-1.0
+            // TODO(brightnessfloat): change to use float value instead.
             MetricsLogger.action(mContext, metric,
-                    Math.round(MathUtils.constrainedMap(0, 1000, PowerManager.BRIGHTNESS_MIN,
-                        PowerManager.BRIGHTNESS_MAX, valFloat)));
+                    BrightnessSynchronizer.brightnessFloatToInt(valFloat));
 
         }
         setBrightness(valFloat);
@@ -459,16 +466,25 @@
     public static class Factory {
         private final Context mContext;
         private final BroadcastDispatcher mBroadcastDispatcher;
+        private final Handler mBackgroundHandler;
 
         @Inject
-        public Factory(Context context, BroadcastDispatcher broadcastDispatcher) {
+        public Factory(
+                Context context,
+                BroadcastDispatcher broadcastDispatcher,
+                @Background Handler bgHandler) {
             mContext = context;
             mBroadcastDispatcher = broadcastDispatcher;
+            mBackgroundHandler = bgHandler;
         }
 
         /** Create a {@link BrightnessController} */
         public BrightnessController create(ToggleSlider toggleSlider) {
-            return new BrightnessController(mContext, toggleSlider, mBroadcastDispatcher);
+            return new BrightnessController(
+                    mContext,
+                    toggleSlider,
+                    mBroadcastDispatcher,
+                    mBackgroundHandler);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
index 0f97e43..8fc831a 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
@@ -21,6 +21,7 @@
 
 import android.app.Activity;
 import android.os.Bundle;
+import android.os.Handler;
 import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.View;
@@ -32,6 +33,7 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.systemui.R;
 import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
 
 import javax.inject.Inject;
 
@@ -41,13 +43,16 @@
     private BrightnessController mBrightnessController;
     private final BrightnessSlider.Factory mToggleSliderFactory;
     private final BroadcastDispatcher mBroadcastDispatcher;
+    private final Handler mBackgroundHandler;
 
     @Inject
     public BrightnessDialog(
             BroadcastDispatcher broadcastDispatcher,
-            BrightnessSlider.Factory factory) {
+            BrightnessSlider.Factory factory,
+            @Background Handler bgHandler) {
         mBroadcastDispatcher = broadcastDispatcher;
         mToggleSliderFactory = factory;
+        mBackgroundHandler = bgHandler;
     }
 
 
@@ -76,7 +81,8 @@
         controller.init();
         frame.addView(controller.getRootView(), MATCH_PARENT, WRAP_CONTENT);
 
-        mBrightnessController = new BrightnessController(this, controller, mBroadcastDispatcher);
+        mBrightnessController = new BrightnessController(
+                this, controller, mBroadcastDispatcher, mBackgroundHandler);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessMirrorHandler.kt b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessMirrorHandler.kt
new file mode 100644
index 0000000..51aa339
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessMirrorHandler.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.settings.brightness
+
+import com.android.systemui.statusbar.policy.BrightnessMirrorController
+import com.android.systemui.statusbar.policy.BrightnessMirrorController.BrightnessMirrorListener
+
+class BrightnessMirrorHandler(private val brightnessController: MirroredBrightnessController) {
+
+    private var mirrorController: BrightnessMirrorController? = null
+
+    private val brightnessMirrorListener = BrightnessMirrorListener { updateBrightnessMirror() }
+
+    fun onQsPanelAttached() {
+        mirrorController?.addCallback(brightnessMirrorListener)
+    }
+
+    fun onQsPanelDettached() {
+        mirrorController?.removeCallback(brightnessMirrorListener)
+    }
+
+    fun setController(controller: BrightnessMirrorController) {
+        mirrorController?.removeCallback(brightnessMirrorListener)
+        mirrorController = controller
+        mirrorController?.addCallback(brightnessMirrorListener)
+        updateBrightnessMirror()
+    }
+
+    private fun updateBrightnessMirror() {
+        mirrorController?.let { brightnessController.setMirror(it) }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
index 896106a..b0e320a 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
@@ -140,13 +140,7 @@
     @Override
     public void setMirrorControllerAndMirror(BrightnessMirrorController c) {
         mMirrorController = c;
-        if (c != null) {
-            setMirror(c.getToggleSlider());
-        } else {
-            // If there's no mirror, we may be the ones dispatching, events but we should not mirror
-            // them
-            mView.setOnDispatchTouchEventListener(null);
-        }
+        setMirror(c.getToggleSlider());
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/MirroredBrightnessController.kt b/packages/SystemUI/src/com/android/systemui/settings/brightness/MirroredBrightnessController.kt
new file mode 100644
index 0000000..8d857de
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/MirroredBrightnessController.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.settings.brightness
+
+import com.android.systemui.statusbar.policy.BrightnessMirrorController
+
+/**
+ * Indicates controller that has brightness slider and uses [BrightnessMirrorController]
+ */
+interface MirroredBrightnessController {
+    fun setMirror(controller: BrightnessMirrorController)
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
index dce19cf..cfbe3b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
@@ -54,22 +54,22 @@
     /**
      * Translates a ratio from 0 to 1 to a blur radius in pixels.
      */
-    fun blurRadiusOfRatio(ratio: Float): Int {
+    fun blurRadiusOfRatio(ratio: Float): Float {
         if (ratio == 0f) {
-            return 0
+            return 0f
         }
-        return MathUtils.lerp(minBlurRadius.toFloat(), maxBlurRadius.toFloat(), ratio).toInt()
+        return MathUtils.lerp(minBlurRadius.toFloat(), maxBlurRadius.toFloat(), ratio)
     }
 
     /**
      * Translates a blur radius in pixels to a ratio between 0 to 1.
      */
-    fun ratioOfBlurRadius(blur: Int): Float {
-        if (blur == 0) {
+    fun ratioOfBlurRadius(blur: Float): Float {
+        if (blur == 0f) {
             return 0f
         }
         return MathUtils.map(minBlurRadius.toFloat(), maxBlurRadius.toFloat(),
-                0f /* maxStart */, 1f /* maxStop */, blur.toFloat())
+                0f /* maxStart */, 1f /* maxStop */, blur)
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
deleted file mode 100644
index b76cebf..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2019 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;
-
-import android.content.Context;
-import android.util.FeatureFlagUtils;
-
-import com.android.systemui.R;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.flags.FeatureFlagReader;
-
-import javax.inject.Inject;
-
-/**
- * Class to manage simple DeviceConfig-based feature flags.
- *
- * See {@link FeatureFlagReader} for instructions on defining and flipping flags.
- */
-@SysUISingleton
-public class FeatureFlags {
-    private final FeatureFlagReader mFlagReader;
-    private final Context mContext;
-
-    @Inject
-    public FeatureFlags(FeatureFlagReader flagReader, Context context) {
-        mFlagReader = flagReader;
-        mContext = context;
-    }
-
-    public boolean isNewNotifPipelineEnabled() {
-        return mFlagReader.isEnabled(R.bool.flag_notification_pipeline2);
-    }
-
-    public boolean isNewNotifPipelineRenderingEnabled() {
-        return mFlagReader.isEnabled(R.bool.flag_notification_pipeline2_rendering);
-    }
-
-    public boolean isKeyguardLayoutEnabled() {
-        return mFlagReader.isEnabled(R.bool.flag_keyguard_layout);
-    }
-
-    public boolean useNewLockscreenAnimations() {
-        return mFlagReader.isEnabled(R.bool.flag_lockscreen_animations);
-    }
-
-    public boolean isPeopleTileEnabled() {
-        return mFlagReader.isEnabled(R.bool.flag_conversations);
-    }
-
-    public boolean isMonetEnabled() {
-        return mFlagReader.isEnabled(R.bool.flag_monet);
-    }
-
-    public boolean isPMLiteEnabled() {
-        return mFlagReader.isEnabled(R.bool.flag_pm_lite);
-    }
-
-    public boolean isChargingRippleEnabled() {
-        return mFlagReader.isEnabled(R.bool.flag_charging_ripple);
-    }
-
-    public boolean isOngoingCallStatusBarChipEnabled() {
-        return mFlagReader.isEnabled(R.bool.flag_ongoing_call_status_bar_chip);
-    }
-
-    public boolean isSmartspaceEnabled() {
-        return mFlagReader.isEnabled(R.bool.flag_smartspace);
-    }
-
-    public boolean isSmartspaceDedupingEnabled() {
-        return isSmartspaceEnabled() && mFlagReader.isEnabled(R.bool.flag_smartspace_deduping);
-    }
-
-    public boolean isNewKeyguardSwipeAnimationEnabled() {
-        return mFlagReader.isEnabled(R.bool.flag_new_unlock_swipe_animation);
-    }
-
-    public boolean isSmartSpaceSharedElementTransitionEnabled() {
-        return mFlagReader.isEnabled(R.bool.flag_smartspace_shared_element_transition);
-    }
-
-    /** Whether or not to use the provider model behavior for the status bar icons */
-    public boolean isCombinedStatusBarSignalIconsEnabled() {
-        return mFlagReader.isEnabled(R.bool.flag_combined_status_bar_signal_icons);
-    }
-
-    /** System setting for provider model behavior */
-    public boolean isProviderModelSettingEnabled() {
-        return FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
-    }
-
-    /** static method for the system setting */
-    public static boolean isProviderModelSettingEnabled(Context context) {
-        return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
index acfd998..8e6cf36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
@@ -17,14 +17,10 @@
 package com.android.systemui.statusbar;
 
 import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.util.AttributeSet;
-import android.view.DisplayCutout;
 import android.view.View;
 import android.widget.TextView;
 
@@ -42,22 +38,14 @@
 public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout {
     private static final String HEADS_UP_STATUS_BAR_VIEW_SUPER_PARCELABLE =
             "heads_up_status_bar_view_super_parcelable";
-    private static final String FIRST_LAYOUT = "first_layout";
     private static final String VISIBILITY = "visibility";
     private static final String ALPHA = "alpha";
-    private int mAbsoluteStartPadding;
-    private int mEndMargin;
+    private final Rect mLayoutedIconRect = new Rect();
+    private final int[] mTmpPosition = new int[2];
+    private final Rect mIconDrawingRect = new Rect();
     private View mIconPlaceholder;
     private TextView mTextView;
     private NotificationEntry mShowingEntry;
-    private Rect mLayoutedIconRect = new Rect();
-    private int[] mTmpPosition = new int[2];
-    private boolean mFirstLayout = true;
-    private int mMaxWidth;
-    private int mSysWinInset;
-    private int mCutOutInset;
-    private Rect mIconDrawingRect = new Rect();
-    private Point mDisplaySize;
     private Runnable mOnDrawingRectChangedListener;
 
     public HeadsUpStatusBarView(Context context) {
@@ -75,40 +63,6 @@
     public HeadsUpStatusBarView(Context context, AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
-        Resources res = getResources();
-        mAbsoluteStartPadding = res.getDimensionPixelSize(R.dimen.notification_side_paddings)
-            + res.getDimensionPixelSize(
-                    com.android.internal.R.dimen.notification_content_margin_start);
-        mEndMargin = res.getDimensionPixelSize(
-                com.android.internal.R.dimen.notification_content_margin_end);
-        setPaddingRelative(mAbsoluteStartPadding, 0, mEndMargin, 0);
-        updateMaxWidth();
-    }
-
-    private void updateMaxWidth() {
-        int maxWidth = getResources().getDimensionPixelSize(R.dimen.qs_panel_width);
-        if (maxWidth != mMaxWidth) {
-            // maxWidth doesn't work with fill_parent, let's manually make it at most as big as the
-            // notification panel
-            mMaxWidth = maxWidth;
-            requestLayout();
-        }
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        if (mMaxWidth > 0) {
-            int newSize = Math.min(MeasureSpec.getSize(widthMeasureSpec), mMaxWidth);
-            widthMeasureSpec = MeasureSpec.makeMeasureSpec(newSize,
-                    MeasureSpec.getMode(widthMeasureSpec));
-        }
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-    }
-
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        updateMaxWidth();
     }
 
     @Override
@@ -116,7 +70,6 @@
         Bundle bundle = new Bundle();
         bundle.putParcelable(HEADS_UP_STATUS_BAR_VIEW_SUPER_PARCELABLE,
                 super.onSaveInstanceState());
-        bundle.putBoolean(FIRST_LAYOUT, mFirstLayout);
         bundle.putInt(VISIBILITY, getVisibility());
         bundle.putFloat(ALPHA, getAlpha());
 
@@ -125,7 +78,7 @@
 
     @Override
     public void onRestoreInstanceState(Parcelable state) {
-        if (state == null || !(state instanceof Bundle)) {
+        if (!(state instanceof Bundle)) {
             super.onRestoreInstanceState(state);
             return;
         }
@@ -133,7 +86,6 @@
         Bundle bundle = (Bundle) state;
         Parcelable superState = bundle.getParcelable(HEADS_UP_STATUS_BAR_VIEW_SUPER_PARCELABLE);
         super.onRestoreInstanceState(superState);
-        mFirstLayout = bundle.getBoolean(FIRST_LAYOUT, true);
         if (bundle.containsKey(VISIBILITY)) {
             setVisibility(bundle.getInt(VISIBILITY));
         }
@@ -185,70 +137,22 @@
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         super.onLayout(changed, l, t, r, b);
         mIconPlaceholder.getLocationOnScreen(mTmpPosition);
-        int left = (int) (mTmpPosition[0] - getTranslationX());
+        int left = mTmpPosition[0];
         int top = mTmpPosition[1];
         int right = left + mIconPlaceholder.getWidth();
         int bottom = top + mIconPlaceholder.getHeight();
         mLayoutedIconRect.set(left, top, right, bottom);
         updateDrawingRect();
-        int targetPadding = mAbsoluteStartPadding + mSysWinInset + mCutOutInset;
-        boolean isRtl = isLayoutRtl();
-        int start = isRtl ? (mDisplaySize.x - right) : left;
-        if (start != targetPadding) {
-            int newPadding = targetPadding - start + getPaddingStart();
-            setPaddingRelative(newPadding, 0, mEndMargin, 0);
-        }
-        if (mFirstLayout) {
-            // we need to do the padding calculation in the first frame, so the layout specified
-            // our visibility to be INVISIBLE in the beginning. let's correct that and set it
-            // to GONE.
-            setVisibility(GONE);
-            mFirstLayout = false;
-        }
-    }
-
-    /** In order to do UI alignment, this view will be notified by
-     * {@link com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout}.
-     * After scroller laid out, the scroller will tell this view about scroller's getX()
-     * @param translationX how to translate the horizontal position
-     */
-    public void setPanelTranslation(float translationX) {
-        setTranslationX(translationX);
-        updateDrawingRect();
     }
 
     private void updateDrawingRect() {
         float oldLeft = mIconDrawingRect.left;
         mIconDrawingRect.set(mLayoutedIconRect);
-        mIconDrawingRect.offset((int) getTranslationX(), 0);
         if (oldLeft != mIconDrawingRect.left && mOnDrawingRectChangedListener != null) {
             mOnDrawingRectChangedListener.run();
         }
     }
 
-    @Override
-    protected boolean fitSystemWindows(Rect insets) {
-        boolean isRtl = isLayoutRtl();
-        mSysWinInset = isRtl ? insets.right : insets.left;
-        DisplayCutout displayCutout = getRootWindowInsets().getDisplayCutout();
-        mCutOutInset = (displayCutout != null)
-                ? (isRtl ? displayCutout.getSafeInsetRight() : displayCutout.getSafeInsetLeft())
-                : 0;
-
-        getDisplaySize();
-
-        // For Double Cut Out mode, the System window navigation bar is at the right
-        // side of the left cut out. In this condition, mSysWinInset include the left cut
-        // out width so we set mCutOutInset to be 0. For RTL, the condition is the same.
-        // The navigation bar is at the left side of the right cut out and include the
-        // right cut out width.
-        if (mSysWinInset != 0) {
-            mCutOutInset = 0;
-        }
-
-        return super.fitSystemWindows(insets);
-    }
-
     public NotificationEntry getShowingEntry() {
         return mShowingEntry;
     }
@@ -264,17 +168,4 @@
     public void setOnDrawingRectChangedListener(Runnable onDrawingRectChangedListener) {
         mOnDrawingRectChangedListener = onDrawingRectChangedListener;
     }
-
-    private void getDisplaySize() {
-        if (mDisplaySize == null) {
-            mDisplaySize = new Point();
-        }
-        getDisplay().getRealSize(mDisplaySize);
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        getDisplaySize();
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 92922b6..eb5f82c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -811,13 +811,27 @@
                 mStatusBarKeyguardViewManager.showBouncerMessage(message, mInitialTextColorState);
             }
         } else if (mKeyguardUpdateMonitor.isScreenOn()) {
-            if (mKeyguardUpdateMonitor.isUdfpsAvailable()) {
-                showTransientIndication(mContext.getString(R.string.keyguard_unlock_press),
-                        false /* isError */, true /* hideOnScreenOff */);
+            showTransientIndication(mContext.getString(R.string.keyguard_unlock),
+                    false /* isError */, true /* hideOnScreenOff */);
+        }
+    }
+
+    private void showTryFingerprintMsg(String a11yString) {
+        if (mKeyguardUpdateMonitor.isUdfpsAvailable()) {
+            // if udfps available, there will always be a tappable affordance to unlock
+            // For example, the lock icon
+            if (mKeyguardBypassController.getUserHasDeviceEntryIntent()) {
+                showTransientIndication(R.string.keyguard_unlock_press);
             } else {
-                showTransientIndication(mContext.getString(R.string.keyguard_unlock),
-                        false /* isError */, true /* hideOnScreenOff */);
+                showTransientIndication(R.string.keyguard_face_failed_use_fp);
             }
+        } else {
+            showTransientIndication(R.string.keyguard_try_fingerprint);
+        }
+
+        // Although we suppress face auth errors visually, we still announce them for a11y
+        if (!TextUtils.isEmpty(a11yString)) {
+            mLockScreenIndicationView.announceForAccessibility(a11yString);
         }
     }
 
@@ -891,7 +905,7 @@
                 return;
             }
 
-            boolean showSwipeToUnlock =
+            boolean showActionToUnlock =
                     msgId == KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
             if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
                 mStatusBarKeyguardViewManager.showBouncerMessage(helpString,
@@ -899,13 +913,11 @@
             } else if (mKeyguardUpdateMonitor.isScreenOn()) {
                 if (biometricSourceType == BiometricSourceType.FACE
                         && shouldSuppressFaceMsgAndShowTryFingerprintMsg()) {
-                    // suggest trying fingerprint
-                    showTransientIndication(R.string.keyguard_try_fingerprint);
+                    showTryFingerprintMsg(helpString);
                     return;
                 }
-                showTransientIndication(helpString, false /* isError */, showSwipeToUnlock);
-            }
-            if (showSwipeToUnlock) {
+                showTransientIndication(helpString, false /* isError */, showActionToUnlock);
+            } else if (showActionToUnlock) {
                 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SHOW_ACTION_TO_UNLOCK),
                         TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
             }
@@ -921,8 +933,7 @@
                     && shouldSuppressFaceMsgAndShowTryFingerprintMsg()
                     && !mStatusBarKeyguardViewManager.isBouncerShowing()
                     && mKeyguardUpdateMonitor.isScreenOn()) {
-                // suggest trying fingerprint
-                showTransientIndication(R.string.keyguard_try_fingerprint);
+                showTryFingerprintMsg(errString);
                 return;
             }
             if (msgId == FaceManager.FACE_ERROR_TIMEOUT) {
@@ -931,11 +942,10 @@
                 if (!mStatusBarKeyguardViewManager.isBouncerShowing()
                         && mKeyguardUpdateMonitor.isUdfpsEnrolled()
                         && mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) {
-                    // suggest trying fingerprint
-                    showTransientIndication(R.string.keyguard_try_fingerprint);
+                    showTryFingerprintMsg(errString);
                 } else if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) {
                     mStatusBarKeyguardViewManager.showBouncerMessage(
-                            mContext.getResources().getString(R.string.keyguard_try_fingerprint),
+                            mContext.getResources().getString(R.string.keyguard_unlock_press),
                             mInitialTextColorState
                     );
                 } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index 8780ad8..03d8e7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -12,8 +12,10 @@
 import android.graphics.RadialGradient
 import android.graphics.Shader
 import android.util.AttributeSet
+import android.util.MathUtils.lerp
 import android.view.View
 import com.android.systemui.animation.Interpolators
+import com.android.systemui.statusbar.LightRevealEffect.Companion.getPercentPastThreshold
 import java.util.function.Consumer
 
 /**
@@ -63,12 +65,12 @@
     override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {
         val interpolatedAmount = INTERPOLATOR.getInterpolation(amount)
         val ovalWidthIncreaseAmount =
-                LightRevealEffect.getPercentPastThreshold(interpolatedAmount, WIDEN_OVAL_THRESHOLD)
+                getPercentPastThreshold(interpolatedAmount, WIDEN_OVAL_THRESHOLD)
 
         val initialWidthMultiplier = (1f - OVAL_INITIAL_WIDTH_PERCENT) / 2f
 
         with(scrim) {
-            revealGradientEndColorAlpha = 1f - LightRevealEffect.getPercentPastThreshold(
+            revealGradientEndColorAlpha = 1f - getPercentPastThreshold(
                     amount, FADE_END_COLOR_OUT_THRESHOLD)
             setRevealGradientBounds(
                     scrim.width * initialWidthMultiplier +
@@ -90,26 +92,49 @@
     override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {
         val interpolatedAmount = INTERPOLATOR.getInterpolation(amount)
 
-        // TODO(b/193801466): add alpha reveal in the beginning as well
+        scrim.startColorAlpha =
+            getPercentPastThreshold(1 - interpolatedAmount,
+                threshold = 1 - START_COLOR_REVEAL_PERCENTAGE)
+
         scrim.revealGradientEndColorAlpha =
-            1f - LightRevealEffect.getPercentPastThreshold(interpolatedAmount, threshold = 0.6f)
+            1f - getPercentPastThreshold(interpolatedAmount,
+                threshold = REVEAL_GRADIENT_END_COLOR_ALPHA_START_PERCENTAGE)
+
+        // Start changing gradient bounds later to avoid harsh gradient in the beginning
+        val gradientBoundsAmount = lerp(GRADIENT_START_BOUNDS_PERCENTAGE, 1.0f, interpolatedAmount)
 
         if (isVertical) {
             scrim.setRevealGradientBounds(
-                left = scrim.width / 2 - (scrim.width / 2) * interpolatedAmount,
+                left = scrim.width / 2 - (scrim.width / 2) * gradientBoundsAmount,
                 top = 0f,
-                right = scrim.width / 2 + (scrim.width / 2) * interpolatedAmount,
+                right = scrim.width / 2 + (scrim.width / 2) * gradientBoundsAmount,
                 bottom = scrim.height.toFloat()
             )
         } else {
             scrim.setRevealGradientBounds(
                 left = 0f,
-                top = scrim.height / 2 - (scrim.height / 2) * interpolatedAmount,
+                top = scrim.height / 2 - (scrim.height / 2) * gradientBoundsAmount,
                 right = scrim.width.toFloat(),
-                bottom = scrim.height / 2 + (scrim.height / 2) * interpolatedAmount
+                bottom = scrim.height / 2 + (scrim.height / 2) * gradientBoundsAmount
             )
         }
     }
+
+    private companion object {
+        // From which percentage we should start the gradient reveal width
+        // E.g. if 0 - starts with 0px width, 0.3f - starts with 30% width
+        private const val GRADIENT_START_BOUNDS_PERCENTAGE = 0.3f
+
+        // When to start changing alpha color of the gradient scrim
+        // E.g. if 0.6f - starts fading the gradient away at 60% and becomes completely
+        // transparent at 100%
+        private const val REVEAL_GRADIENT_END_COLOR_ALPHA_START_PERCENTAGE = 0.6f
+
+        // When to finish displaying start color fill that reveals the content
+        // E.g. if 0.3f - the content won't be visible at 0% and it will gradually
+        // reduce the alpha until 30% (at this point the color fill is invisible)
+        private const val START_COLOR_REVEAL_PERCENTAGE = 0.3f
+    }
 }
 
 class CircleReveal(
@@ -125,7 +150,7 @@
     override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {
         // reveal amount updates already have an interpolator, so we intentionally use the
         // non-interpolated amount
-        val fadeAmount = LightRevealEffect.getPercentPastThreshold(amount, 0.5f)
+        val fadeAmount = getPercentPastThreshold(amount, 0.5f)
         val radius = startRadius + ((endRadius - startRadius) * amount)
         scrim.revealGradientEndColorAlpha = 1f - fadeAmount
         scrim.setRevealGradientBounds(
@@ -153,8 +178,7 @@
 
     override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {
         val interpolatedAmount = Interpolators.FAST_OUT_SLOW_IN_REVERSE.getInterpolation(amount)
-        val fadeAmount =
-                LightRevealEffect.getPercentPastThreshold(interpolatedAmount, 0.5f)
+        val fadeAmount = getPercentPastThreshold(interpolatedAmount, 0.5f)
 
         with(scrim) {
             revealGradientEndColorAlpha = 1f - fadeAmount
@@ -178,7 +202,10 @@
  */
 class LightRevealScrim(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
 
-    lateinit var revealAmountListener: Consumer<Float>
+    /**
+     * Listener that is called if the scrim's opaqueness changes
+     */
+    lateinit var isScrimOpaqueChangedListener: Consumer<Boolean>
 
     /**
      * How much of the underlying views are revealed, in percent. 0 means they will be completely
@@ -190,7 +217,7 @@
                 field = value
 
                 revealEffect.setRevealAmountOnScrim(value, this)
-                revealAmountListener.accept(value)
+                updateScrimOpaque()
                 invalidate()
             }
         }
@@ -213,6 +240,23 @@
     var revealGradientWidth: Float = 0f
     var revealGradientHeight: Float = 0f
 
+    /**
+     * Alpha of the fill that can be used in the beginning of the animation to hide the content.
+     * Normally the gradient bounds are animated from small size so the content is not visible,
+     * but if the start gradient bounds allow to see some content this could be used to make the
+     * reveal smoother. It can help to add fade in effect in the beginning of the animation.
+     * The color of the fill is determined by [revealGradientEndColor].
+     *
+     * 0 - no fill and content is visible, 1 - the content is covered with the start color
+     */
+    var startColorAlpha = 0f
+        set(value) {
+            if (field != value) {
+                field = value
+                invalidate()
+            }
+        }
+
     var revealGradientEndColor: Int = Color.BLACK
         set(value) {
             if (field != value) {
@@ -230,6 +274,31 @@
         }
 
     /**
+     * Is the scrim currently fully opaque
+     */
+    var isScrimOpaque = false
+        private set(value) {
+            if (field != value) {
+                field = value
+                isScrimOpaqueChangedListener.accept(field)
+            }
+        }
+
+    private fun updateScrimOpaque() {
+        isScrimOpaque = revealAmount == 0.0f && alpha == 1.0f && visibility == VISIBLE
+    }
+
+    override fun setAlpha(alpha: Float) {
+        super.setAlpha(alpha)
+        updateScrimOpaque()
+    }
+
+    override fun setVisibility(visibility: Int) {
+        super.setVisibility(visibility)
+        updateScrimOpaque()
+    }
+
+    /**
      * Paint used to draw a transparent-to-white radial gradient. This will be scaled and translated
      * via local matrix in [onDraw] so we never need to construct a new shader.
      */
@@ -281,6 +350,10 @@
             return
         }
 
+        if (startColorAlpha > 0f) {
+            canvas.drawColor(updateColorAlpha(revealGradientEndColor, startColorAlpha))
+        }
+
         with(shaderGradientMatrix) {
             setScale(revealGradientWidth, revealGradientHeight, 0f, 0f)
             postTranslate(revealGradientCenter.x, revealGradientCenter.y)
@@ -294,11 +367,15 @@
 
     private fun setPaintColorFilter() {
         gradientPaint.colorFilter = PorterDuffColorFilter(
-                Color.argb(
-                        (revealGradientEndColorAlpha * 255).toInt(),
-                        Color.red(revealGradientEndColor),
-                        Color.green(revealGradientEndColor),
-                        Color.blue(revealGradientEndColor)),
-                PorterDuff.Mode.MULTIPLY)
+            updateColorAlpha(revealGradientEndColor, revealGradientEndColorAlpha),
+            PorterDuff.Mode.MULTIPLY)
     }
+
+    private fun updateColorAlpha(color: Int, alpha: Float): Int =
+        Color.argb(
+            (alpha * 255).toInt(),
+            Color.red(color),
+            Color.green(color),
+            Color.blue(color)
+        )
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 8969b4d..36d2d86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -52,6 +52,7 @@
 import com.android.systemui.animation.Interpolators;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.media.MediaData;
 import com.android.systemui.media.MediaDataManager;
 import com.android.systemui.media.SmartspaceMediaData;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index db553e4..b833427 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -68,7 +68,7 @@
         private const val VELOCITY_SCALE = 100f
         private const val MAX_VELOCITY = 3000f
         private const val MIN_VELOCITY = -MAX_VELOCITY
-        private const val INTERACTION_BLUR_FRACTION = 0.4f
+        private const val INTERACTION_BLUR_FRACTION = 0.8f
         private const val ANIMATION_BLUR_FRACTION = 1f - INTERACTION_BLUR_FRACTION
         private const val TAG = "DepthController"
     }
@@ -92,8 +92,6 @@
     // Only for dumpsys
     private var lastAppliedBlur = 0
 
-    @VisibleForTesting
-    var shadeSpring = DepthAnimation()
     var shadeAnimation = DepthAnimation()
 
     @VisibleForTesting
@@ -101,12 +99,16 @@
     var brightnessMirrorVisible: Boolean = false
         set(value) {
             field = value
-            brightnessMirrorSpring.animateTo(if (value) blurUtils.blurRadiusOfRatio(1f)
+            brightnessMirrorSpring.animateTo(if (value) blurUtils.blurRadiusOfRatio(1f).toInt()
                 else 0)
         }
 
     var qsPanelExpansion = 0f
         set(value) {
+            if (value.isNaN()) {
+                Log.w(TAG, "Invalid qs expansion")
+                return
+            }
             if (field == value) return
             field = value
             scheduleUpdate()
@@ -134,15 +136,13 @@
             field = value
             scheduleUpdate()
 
-            if (shadeSpring.radius == 0 && shadeAnimation.radius == 0) {
+            if (shadeExpansion == 0f && shadeAnimation.radius == 0f) {
                 return
             }
             // Do not remove blurs when we're re-enabling them
             if (!value) {
                 return
             }
-            shadeSpring.animateTo(0)
-            shadeSpring.finishIfRunning()
 
             shadeAnimation.animateTo(0)
             shadeAnimation.finishIfRunning()
@@ -161,7 +161,7 @@
     /**
      * Blur radius of the wake-up animation on this frame.
      */
-    private var wakeAndUnlockBlurRadius = 0
+    private var wakeAndUnlockBlurRadius = 0f
         set(value) {
             if (field == value) return
             field = value
@@ -174,25 +174,30 @@
     @VisibleForTesting
     val updateBlurCallback = Choreographer.FrameCallback {
         updateScheduled = false
-        val normalizedBlurRadius = MathUtils.constrain(shadeAnimation.radius,
-                blurUtils.minBlurRadius, blurUtils.maxBlurRadius)
-        var combinedBlur = (shadeSpring.radius * INTERACTION_BLUR_FRACTION +
-                normalizedBlurRadius * ANIMATION_BLUR_FRACTION).toInt()
-        combinedBlur = max(combinedBlur, blurUtils.blurRadiusOfRatio(qsPanelExpansion))
+        val animationRadius = MathUtils.constrain(shadeAnimation.radius,
+                blurUtils.minBlurRadius.toFloat(), blurUtils.maxBlurRadius.toFloat())
+        val expansionRadius = blurUtils.blurRadiusOfRatio(
+                Interpolators.getNotificationScrimAlpha(
+                        if (shouldApplyShadeBlur()) shadeExpansion else 0f, false))
+        var combinedBlur = (expansionRadius * INTERACTION_BLUR_FRACTION +
+                animationRadius * ANIMATION_BLUR_FRACTION)
+        val qsExpandedRatio = qsPanelExpansion * shadeExpansion
+        combinedBlur = max(combinedBlur, blurUtils.blurRadiusOfRatio(qsExpandedRatio))
         combinedBlur = max(combinedBlur, blurUtils.blurRadiusOfRatio(transitionToFullShadeProgress))
-        var shadeRadius = max(combinedBlur, wakeAndUnlockBlurRadius).toFloat()
+        var shadeRadius = max(combinedBlur, wakeAndUnlockBlurRadius)
 
         if (blursDisabledForAppLaunch) {
             shadeRadius = 0f
         }
 
+        var zoomOut = MathUtils.saturate(blurUtils.ratioOfBlurRadius(shadeRadius))
         var blur = shadeRadius.toInt()
 
         // Make blur be 0 if it is necessary to stop blur effect.
         if (scrimsVisible) {
             blur = 0
+            zoomOut = 0f
         }
-        val zoomOut = blurUtils.ratioOfBlurRadius(blur)
 
         if (!blurUtils.supportsBlursOnWindows()) {
             blur = 0
@@ -265,12 +270,11 @@
         override fun onStateChanged(newState: Int) {
             updateShadeAnimationBlur(
                     shadeExpansion, prevTracking, prevShadeVelocity, prevShadeDirection)
-            updateShadeBlur()
+            scheduleUpdate()
         }
 
         override fun onDozingChanged(isDozing: Boolean) {
             if (isDozing) {
-                shadeSpring.finishIfRunning()
                 shadeAnimation.finishIfRunning()
                 brightnessMirrorSpring.finishIfRunning()
             }
@@ -335,7 +339,7 @@
         prevTracking = tracking
         prevTimestamp = timestamp
 
-        updateShadeBlur()
+        scheduleUpdate()
     }
 
     private fun updateShadeAnimationBlur(
@@ -398,15 +402,7 @@
         }
 
         shadeAnimation.setStartVelocity(velocity)
-        shadeAnimation.animateTo(blurUtils.blurRadiusOfRatio(targetBlurNormalized))
-    }
-
-    private fun updateShadeBlur() {
-        var newBlur = 0
-        if (shouldApplyShadeBlur()) {
-            newBlur = blurUtils.blurRadiusOfRatio(shadeExpansion)
-        }
-        shadeSpring.animateTo(newBlur)
+        shadeAnimation.animateTo(blurUtils.blurRadiusOfRatio(targetBlurNormalized).toInt())
     }
 
     private fun scheduleUpdate(viewToBlur: View? = null) {
@@ -432,7 +428,8 @@
         IndentingPrintWriter(pw, "  ").let {
             it.println("StatusBarWindowBlurController:")
             it.increaseIndent()
-            it.println("shadeRadius: ${shadeSpring.radius}")
+            it.println("shadeExpansion: $shadeExpansion")
+            it.println("shouldApplyShaeBlur: ${shouldApplyShadeBlur()}")
             it.println("shadeAnimation: ${shadeAnimation.radius}")
             it.println("brightnessMirrorRadius: ${brightnessMirrorSpring.radius}")
             it.println("wakeAndUnlockBlur: $wakeAndUnlockBlurRadius")
@@ -451,7 +448,7 @@
         /**
          * Blur radius visible on the UI, in pixels.
          */
-        var radius = 0
+        var radius = 0f
 
         /**
          * Depth ratio of the current blur radius.
@@ -472,12 +469,12 @@
         private var springAnimation = SpringAnimation(this, object :
                 FloatPropertyCompat<DepthAnimation>("blurRadius") {
             override fun setValue(rect: DepthAnimation?, value: Float) {
-                radius = value.toInt()
+                radius = value
                 scheduleUpdate(view)
             }
 
             override fun getValue(rect: DepthAnimation?): Float {
-                return radius.toFloat()
+                return radius
             }
         })
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
index f0d779c..6ea79af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
@@ -182,10 +182,10 @@
     default void setFaceAuthDisplayBrightness(float brightness) {}
 
     /**
-     * How much {@link LightRevealScrim} obscures the UI.
-     * @param amount 0 when opaque, 1 when not transparent
+     * If {@link LightRevealScrim} obscures the UI.
+     * @param opaque if the scrim is opaque
      */
-    default void setLightRevealScrimAmount(float amount) {}
+    default void setLightRevealScrimOpaque(boolean opaque) {}
 
     /**
      * Custom listener to pipe data back to plugins about whether or not the status bar would be
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
index cc7a4f8..4a6d7e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java
@@ -15,46 +15,18 @@
 package com.android.systemui.statusbar;
 
 import android.content.Context;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.telephony.ServiceState;
-import android.telephony.SubscriptionInfo;
-import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.widget.TextView;
 
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.settingslib.WirelessUtils;
-import com.android.systemui.Dependency;
-import com.android.systemui.demomode.DemoModeCommandReceiver;
-import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
-import com.android.systemui.tuner.TunerService;
-import com.android.systemui.tuner.TunerService.Tunable;
 
 import java.util.List;
 
 /** Shows the operator name */
-public class OperatorNameView extends TextView implements DemoModeCommandReceiver, DarkReceiver,
-        SignalCallback, Tunable {
-
-    private static final String KEY_SHOW_OPERATOR_NAME = "show_operator_name";
-
-    private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+public class OperatorNameView extends TextView {
     private boolean mDemoMode;
 
-    private final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
-        @Override
-        public void onRefreshCarrierInfo() {
-            updateText();
-        }
-    };
-
     public OperatorNameView(Context context) {
         this(context, null);
     }
@@ -67,62 +39,14 @@
         super(context, attrs, defStyle);
     }
 
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
-        mKeyguardUpdateMonitor.registerCallback(mCallback);
-        Dependency.get(DarkIconDispatcher.class).addDarkReceiver(this);
-        Dependency.get(NetworkController.class).addCallback(this);
-        Dependency.get(TunerService.class).addTunable(this, KEY_SHOW_OPERATOR_NAME);
+    void setDemoMode(boolean demoMode) {
+        mDemoMode = demoMode;
     }
 
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        mKeyguardUpdateMonitor.removeCallback(mCallback);
-        Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(this);
-        Dependency.get(NetworkController.class).removeCallback(this);
-        Dependency.get(TunerService.class).removeTunable(this);
-    }
-
-    @Override
-    public void onDarkChanged(Rect area, float darkIntensity, int tint) {
-        setTextColor(DarkIconDispatcher.getTint(area, this, tint));
-    }
-
-    @Override
-    public void setIsAirplaneMode(IconState icon) {
-        update();
-    }
-
-    @Override
-    public void onTuningChanged(String key, String newValue) {
-        update();
-    }
-
-    @Override
-    public void dispatchDemoCommand(String command, Bundle args) {
-        setText(args.getString("name"));
-    }
-
-    @Override
-    public void onDemoModeStarted() {
-        mDemoMode = true;
-    }
-
-    @Override
-    public void onDemoModeFinished() {
-        mDemoMode = false;
-        update();
-    }
-
-    private void update() {
-        boolean showOperatorName = Dependency.get(TunerService.class)
-                .getValue(KEY_SHOW_OPERATOR_NAME, 1) != 0;
+    void update(boolean showOperatorName, boolean hasMobile,
+            List<OperatorNameViewController.SubInfo> subs) {
         setVisibility(showOperatorName ? VISIBLE : GONE);
 
-        boolean hasMobile = mContext.getSystemService(TelephonyManager.class).isDataCapable();
         boolean airplaneMode = WirelessUtils.isAirplaneModeOn(mContext);
         if (!hasMobile || airplaneMode) {
             setText(null);
@@ -131,22 +55,19 @@
         }
 
         if (!mDemoMode) {
-            updateText();
+            updateText(subs);
         }
     }
 
-    private void updateText() {
+    void updateText(List<OperatorNameViewController.SubInfo> subs) {
         CharSequence displayText = null;
-        List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false);
         final int N = subs.size();
         for (int i = 0; i < N; i++) {
-            int subId = subs.get(i).getSubscriptionId();
-            int simState = mKeyguardUpdateMonitor.getSimState(subId);
+            OperatorNameViewController.SubInfo subInfo = subs.get(i);
             CharSequence carrierName = subs.get(i).getCarrierName();
-            if (!TextUtils.isEmpty(carrierName) && simState == TelephonyManager.SIM_STATE_READY) {
-                ServiceState ss = mKeyguardUpdateMonitor.getServiceState(subId);
-                if (ss != null && ss.getState() == ServiceState.STATE_IN_SERVICE) {
-                    displayText = carrierName;
+            if (!TextUtils.isEmpty(carrierName) && subInfo.simReady()) {
+                if (subInfo.stateInService()) {
+                    displayText = subInfo.getCarrierName();
                     break;
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java
new file mode 100644
index 0000000..e49f48f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java
@@ -0,0 +1,201 @@
+/*
+ * 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;
+
+import android.os.Bundle;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionInfo;
+import android.telephony.TelephonyManager;
+import android.view.View;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.demomode.DemoModeCommandReceiver;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.ViewController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+/** Controller for {@link OperatorNameView}. */
+public class OperatorNameViewController extends ViewController<OperatorNameView> {
+    private static final String KEY_SHOW_OPERATOR_NAME = "show_operator_name";
+
+    private final DarkIconDispatcher mDarkIconDispatcher;
+    private final NetworkController mNetworkController;
+    private final TunerService mTunerService;
+    private final TelephonyManager mTelephonyManager;
+    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+
+    private OperatorNameViewController(OperatorNameView view,
+            DarkIconDispatcher darkIconDispatcher,
+            NetworkController networkController,
+            TunerService tunerService,
+            TelephonyManager telephonyManager,
+            KeyguardUpdateMonitor keyguardUpdateMonitor) {
+        super(view);
+        mDarkIconDispatcher = darkIconDispatcher;
+        mNetworkController = networkController;
+        mTunerService = tunerService;
+        mTelephonyManager = telephonyManager;
+        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+    }
+
+    @Override
+    protected void onViewAttached() {
+        mDarkIconDispatcher.addDarkReceiver(mDarkReceiver);
+        mNetworkController.addCallback(mSignalCallback);
+        mTunerService.addTunable(mTunable, KEY_SHOW_OPERATOR_NAME);
+        mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
+    }
+
+    @Override
+    protected void onViewDetached() {
+        mDarkIconDispatcher.removeDarkReceiver(mDarkReceiver);
+        mNetworkController.removeCallback(mSignalCallback);
+        mTunerService.removeTunable(mTunable);
+        mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
+    }
+
+    private void update() {
+        mView.update(mTunerService.getValue(KEY_SHOW_OPERATOR_NAME, 1) != 0,
+                mTelephonyManager.isDataCapable(), getSubInfos());
+    }
+
+    private List<SubInfo> getSubInfos() {
+        List<SubInfo> result = new ArrayList<>();
+        List<SubscriptionInfo> subscritionInfos =
+                mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false);
+
+        for (SubscriptionInfo subscriptionInfo : subscritionInfos) {
+            int subId = subscriptionInfo.getSubscriptionId();
+            result.add(new SubInfo(
+                    subscriptionInfo.getCarrierName(),
+                    mKeyguardUpdateMonitor.getSimState(subId),
+                    mKeyguardUpdateMonitor.getServiceState(subId)));
+        }
+
+        return result;
+    }
+
+    /** Factory for constructing an {@link OperatorNameViewController}. */
+    public static class Factory {
+        private final DarkIconDispatcher mDarkIconDispatcher;
+        private final NetworkController mNetworkController;
+        private final TunerService mTunerService;
+        private final TelephonyManager mTelephonyManager;
+        private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+
+        @Inject
+        public Factory(DarkIconDispatcher darkIconDispatcher, NetworkController networkController,
+                TunerService tunerService, TelephonyManager telephonyManager,
+                KeyguardUpdateMonitor keyguardUpdateMonitor) {
+            mDarkIconDispatcher = darkIconDispatcher;
+            mNetworkController = networkController;
+            mTunerService = tunerService;
+            mTelephonyManager = telephonyManager;
+            mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        }
+
+        /** Create an {@link OperatorNameViewController}. */
+        public OperatorNameViewController create(OperatorNameView view) {
+            return new OperatorNameViewController(view, mDarkIconDispatcher, mNetworkController,
+                    mTunerService, mTelephonyManager, mKeyguardUpdateMonitor);
+        }
+    }
+
+    /**
+     * Needed because of how {@link CollapsedStatusBarFragment} works.
+     *
+     * Ideally this can be done internally.
+     **/
+    public View getView() {
+        return mView;
+    }
+
+    private final DarkIconDispatcher.DarkReceiver mDarkReceiver =
+            (area, darkIntensity, tint) ->
+                    mView.setTextColor(DarkIconDispatcher.getTint(area, mView, tint));
+
+    private final NetworkController.SignalCallback mSignalCallback =
+            new NetworkController.SignalCallback() {
+        @Override
+        public void setIsAirplaneMode(NetworkController.IconState icon) {
+            update();
+        }
+    };
+
+    private final TunerService.Tunable mTunable = (key, newValue) -> update();
+
+
+    private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
+            new KeyguardUpdateMonitorCallback() {
+        @Override
+        public void onRefreshCarrierInfo() {
+            mView.updateText(getSubInfos());
+        }
+    };
+
+    // TODO: do we even register this anywhere?
+    private final DemoModeCommandReceiver mDemoModeCommandReceiver = new DemoModeCommandReceiver() {
+        @Override
+        public void onDemoModeStarted() {
+            mView.setDemoMode(true);
+        }
+
+        @Override
+        public void onDemoModeFinished() {
+            mView.setDemoMode(false);
+            update();
+        }
+
+        @Override
+        public void dispatchDemoCommand(String command, Bundle args) {
+            mView.setText(args.getString("name"));
+        }
+    };
+
+    static class SubInfo {
+        private final CharSequence mCarrierName;
+        private final int mSimState;
+        private final ServiceState mServiceState;
+
+        private SubInfo(CharSequence carrierName,
+                int simState, ServiceState serviceState) {
+            mCarrierName = carrierName;
+            mSimState = simState;
+            mServiceState = serviceState;
+        }
+
+        boolean simReady() {
+            return mSimState == TelephonyManager.SIM_STATE_READY;
+        }
+
+        CharSequence getCarrierName() {
+            return mCarrierName;
+        }
+
+        boolean stateInService() {
+            return mServiceState != null
+                    && mServiceState.getState() == ServiceState.STATE_IN_SERVICE;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SuperStatusBarViewFactory.java b/packages/SystemUI/src/com/android/systemui/statusbar/SuperStatusBarViewFactory.java
deleted file mode 100644
index e4ae560..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SuperStatusBarViewFactory.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2019 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;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-
-import com.android.systemui.R;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
-import com.android.systemui.statusbar.phone.StatusBarWindowView;
-import com.android.systemui.util.InjectionInflationController;
-
-import javax.inject.Inject;
-
-/**
- * Creates a single instance of super_status_bar and super_notification_shade that can be shared
- * across various system ui objects.
- */
-@SysUISingleton
-public class SuperStatusBarViewFactory {
-
-    private final Context mContext;
-    private final InjectionInflationController mInjectionInflationController;
-    private final NotificationShelfComponent.Builder mNotificationShelfComponentBuilder;
-
-    private NotificationShadeWindowView mNotificationShadeWindowView;
-    private StatusBarWindowView mStatusBarWindowView;
-    private NotificationShelfController mNotificationShelfController;
-
-    @Inject
-    public SuperStatusBarViewFactory(Context context,
-            InjectionInflationController injectionInflationController,
-            NotificationShelfComponent.Builder notificationShelfComponentBuilder) {
-        mContext = context;
-        mInjectionInflationController = injectionInflationController;
-        mNotificationShelfComponentBuilder = notificationShelfComponentBuilder;
-    }
-
-    /**
-     * Gets the inflated {@link NotificationShadeWindowView} from
-     * {@link R.layout#super_notification_shade}.
-     * Returns a cached instance, if it has already been inflated.
-     */
-    public NotificationShadeWindowView getNotificationShadeWindowView() {
-        if (mNotificationShadeWindowView != null) {
-            return mNotificationShadeWindowView;
-        }
-
-        mNotificationShadeWindowView = (NotificationShadeWindowView)
-                mInjectionInflationController.injectable(
-                LayoutInflater.from(mContext)).inflate(R.layout.super_notification_shade,
-                /* root= */ null);
-        if (mNotificationShadeWindowView == null) {
-            throw new IllegalStateException(
-                    "R.layout.super_notification_shade could not be properly inflated");
-        }
-
-        return mNotificationShadeWindowView;
-    }
-
-    /**
-     * Gets the inflated {@link StatusBarWindowView} from {@link R.layout#super_status_bar}.
-     * Returns a cached instance, if it has already been inflated.
-     */
-    public StatusBarWindowView getStatusBarWindowView() {
-        if (mStatusBarWindowView != null) {
-            return mStatusBarWindowView;
-        }
-
-        mStatusBarWindowView =
-                (StatusBarWindowView) mInjectionInflationController.injectable(
-                LayoutInflater.from(mContext)).inflate(R.layout.super_status_bar,
-                /* root= */ null);
-        if (mStatusBarWindowView == null) {
-            throw new IllegalStateException(
-                    "R.layout.super_status_bar could not be properly inflated");
-        }
-        return mStatusBarWindowView;
-    }
-
-    /**
-     * Gets the inflated {@link NotificationShelf} from
-     * {@link R.layout#status_bar_notification_shelf}.
-     * Returns a cached instance, if it has already been inflated.
-     *
-     * @param container the expected container to hold the {@link NotificationShelf}. The view
-     *                  isn't immediately attached, but the layout params of this view is used
-     *                  during inflation.
-     */
-    public NotificationShelfController getNotificationShelfController(ViewGroup container) {
-        if (mNotificationShelfController != null) {
-            return mNotificationShelfController;
-        }
-
-        NotificationShelf view = (NotificationShelf) LayoutInflater.from(mContext)
-                .inflate(R.layout.status_bar_notification_shelf, container, /* attachToRoot= */
-                        false);
-
-        if (view == null) {
-            throw new IllegalStateException(
-                    "R.layout.status_bar_notification_shelf could not be properly inflated");
-        }
-
-        NotificationShelfComponent component = mNotificationShelfComponentBuilder
-                .notificationShelf(view)
-                .build();
-        mNotificationShelfController = component.getNotificationShelfController();
-        mNotificationShelfController.init();
-
-        return mNotificationShelfController;
-    }
-
-    public NotificationPanelView getNotificationPanelView() {
-        NotificationShadeWindowView notificationShadeWindowView = getNotificationShadeWindowView();
-        if (notificationShadeWindowView == null) {
-            return null;
-        }
-
-        return mNotificationShadeWindowView.findViewById(R.id.notification_panel);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt
index 4a467ce..d01fc93 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt
@@ -104,7 +104,7 @@
         // the active effect area. Values here should be kept in sync with the
         // animation implementation in the ripple shader.
         val maskRadius = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
-                (1 - rippleShader.progress)) * radius * 1.5f
+                (1 - rippleShader.progress)) * radius * 2
         canvas?.drawCircle(origin.x, origin.y, maskRadius, ripplePaint)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
index 22bbb81b..d74297e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
@@ -29,7 +29,7 @@
 import com.android.internal.logging.UiEventLogger
 import com.android.settingslib.Utils
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.statusbar.commandline.Command
 import com.android.systemui.statusbar.commandline.CommandRegistry
 import com.android.systemui.statusbar.policy.BatteryController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index a05e950..f2cf93e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -24,13 +24,13 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.media.MediaDataManager;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.carrier.QSCarrierGroupController;
 import com.android.systemui.statusbar.ActionClickLogger;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.MediaArtworkProcessor;
 import com.android.systemui.statusbar.NotificationClickNotifier;
 import com.android.systemui.statusbar.NotificationListener;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
index 29cfb07..1037e57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
@@ -497,7 +497,12 @@
         }
 
         if (state.designatedCorner != currentViewState.designatedCorner) {
+            currentViewState.designatedCorner?.contentDescription = null
+            state.designatedCorner?.contentDescription = state.contentDescription
+
             updateDesignatedCorner(state.designatedCorner, state.shouldShowDot())
+        } else if (state.contentDescription != currentViewState.contentDescription) {
+            state.designatedCorner?.contentDescription = state.contentDescription
         }
 
         val shouldShow = state.shouldShowDot()
@@ -514,9 +519,13 @@
 
     private val systemStatusAnimationCallback: SystemStatusAnimationCallback =
             object : SystemStatusAnimationCallback {
-        override fun onSystemStatusAnimationTransitionToPersistentDot(): Animator? {
+        override fun onSystemStatusAnimationTransitionToPersistentDot(
+            contentDescr: String?
+        ): Animator? {
             synchronized(lock) {
-                nextViewState = nextViewState.copy(systemPrivacyEventIsActive = true)
+                nextViewState = nextViewState.copy(
+                        systemPrivacyEventIsActive = true,
+                        contentDescription = contentDescr)
             }
 
             return null
@@ -620,7 +629,9 @@
     val rotation: Int = 0,
     val height: Int = 0,
     val cornerIndex: Int = -1,
-    val designatedCorner: View? = null
+    val designatedCorner: View? = null,
+
+    val contentDescription: String? = null
 ) {
     fun shouldShowDot(): Boolean {
         return systemPrivacyEventIsActive && !shadeExpanded && !qsExpanded
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt
index 539020d..d4d84c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt
@@ -34,6 +34,7 @@
     // Whether or not to show an animation for this event
     val showAnimation: Boolean
     val viewCreator: (context: Context) -> View
+    var contentDescription: String?
 
     // Update this event with values from another event.
     fun updateFromEvent(other: StatusEvent?) {
@@ -50,6 +51,7 @@
     override val priority = 50
     override val forceVisible = false
     override val showAnimation = true
+    override var contentDescription: String? = ""
 
     override val viewCreator: (context: Context) -> View = { context ->
         val iv = ImageView(context)
@@ -62,7 +64,9 @@
         return javaClass.simpleName
     }
 }
+
 class PrivacyEvent(override val showAnimation: Boolean = true) : StatusEvent {
+    override var contentDescription: String? = null
     override val priority = 100
     override val forceVisible = true
     var privacyItems: List<PrivacyItem> = listOf()
@@ -72,6 +76,7 @@
         val v = LayoutInflater.from(context)
                 .inflate(R.layout.ongoing_privacy_chip, null) as OngoingPrivacyChip
         v.privacyList = privacyItems
+        v.contentDescription = contentDescription
         privacyChip = v
         v
     }
@@ -81,7 +86,9 @@
     }
 
     override fun shouldUpdateFromEvent(other: StatusEvent?): Boolean {
-        return other is PrivacyEvent && other.privacyItems != privacyItems
+        return other is PrivacyEvent &&
+                (other.privacyItems != privacyItems ||
+                other.contentDescription != contentDescription)
     }
 
     override fun updateFromEvent(other: StatusEvent?) {
@@ -90,6 +97,9 @@
         }
 
         privacyItems = other.privacyItems
+        contentDescription = other.contentDescription
+
+        privacyChip?.contentDescription = other.contentDescription
         privacyChip?.privacyList = other.privacyItems
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
index b861c1d..589446f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
@@ -24,22 +24,18 @@
 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.StatusBarLocationPublisher
 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.
+ * Controls the view for system event animations.
  */
 class SystemEventChipAnimationController @Inject constructor(
     private val context: Context,
-    private val statusBarViewFactory: SuperStatusBarViewFactory,
+    private val statusBarWindowView: StatusBarWindowView,
     private val statusBarWindowController: StatusBarWindowController,
     private val locationPublisher: StatusBarLocationPublisher
 ) : SystemStatusChipAnimationCallback {
@@ -52,7 +48,6 @@
 
     private lateinit var animationWindowView: FrameLayout
     private lateinit var animationDotView: View
-    private lateinit var statusBarWindowView: StatusBarWindowView
     private var currentAnimatedView: View? = null
 
     // TODO: move to dagger
@@ -126,7 +121,6 @@
 
     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)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
index ba50659..04f7492 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
@@ -16,9 +16,12 @@
 
 package com.android.systemui.statusbar.events
 
+import android.content.Context
 import android.provider.DeviceConfig
 import android.provider.DeviceConfig.NAMESPACE_PRIVACY
+import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.privacy.PrivacyChipBuilder
 import com.android.systemui.privacy.PrivacyItem
 import com.android.systemui.privacy.PrivacyItemController
 import com.android.systemui.statusbar.policy.BatteryController
@@ -33,7 +36,8 @@
 class SystemEventCoordinator @Inject constructor(
     private val systemClock: SystemClock,
     private val batteryController: BatteryController,
-    private val privacyController: PrivacyItemController
+    private val privacyController: PrivacyItemController,
+    private val context: Context
 ) {
     private lateinit var scheduler: SystemStatusAnimationScheduler
 
@@ -66,6 +70,11 @@
     fun notifyPrivacyItemsChanged(showAnimation: Boolean = true) {
         val event = PrivacyEvent(showAnimation)
         event.privacyItems = privacyStateListener.currentPrivacyItems
+        event.contentDescription = {
+            val items = PrivacyChipBuilder(context, event.privacyItems).joinTypes()
+            context.getString(
+                    R.string.ongoing_privacy_chip_content_multiple_apps, items)
+        }()
         scheduler.onStatusEvent(event)
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
index f30010c..dcf8e73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
@@ -100,14 +100,21 @@
 
         // Don't deal with threading for now (no need let's be honest)
         Assert.isMainThread()
-        if (event.priority > scheduledEvent?.priority ?: -1 ||
-            scheduledEvent?.shouldUpdateFromEvent(event) == true) {
+        if ((event.priority > scheduledEvent?.priority ?: -1) &&
+                animationState != ANIMATING_OUT &&
+                (animationState != SHOWING_PERSISTENT_DOT && event.forceVisible)) {
+            // events can only be scheduled if a higher priority or no other event is in progress
             if (DEBUG) {
                 Log.d(TAG, "scheduling event $event")
             }
-            if (event.showAnimation) {
-                scheduleEvent(event)
-            } else if (event.forceVisible) {
+
+            scheduleEvent(event)
+        } else if (scheduledEvent?.shouldUpdateFromEvent(event) == true) {
+            if (DEBUG) {
+                Log.d(TAG, "updating current event from: $event")
+            }
+            scheduledEvent?.updateFromEvent(event)
+            if (event.forceVisible) {
                 hasPersistentDot = true
                 notifyTransitionToPersistentDot()
             }
@@ -142,24 +149,19 @@
      * Clear the scheduled event (if any) and schedule a new one
      */
     private fun scheduleEvent(event: StatusEvent) {
-        if (animationState == ANIMATING_OUT ||
-            (animationState == SHOWING_PERSISTENT_DOT && event.forceVisible)) {
-            // do not schedule an event or change the current one
-            return
-        }
+        scheduledEvent = event
 
-        // If we are showing the chip, possibly update the current event, rather than replacing
-        if (scheduledEvent?.shouldUpdateFromEvent(event) == true) {
-            scheduledEvent?.updateFromEvent(event)
-            return
-        } else {
-            scheduledEvent = event
-        }
-
-        if (scheduledEvent!!.forceVisible) {
+        if (event.forceVisible) {
             hasPersistentDot = true
         }
 
+        // If animations are turned off, we'll transition directly to the dot
+        if (!event.showAnimation && event.forceVisible) {
+            notifyTransitionToPersistentDot()
+            scheduledEvent = null
+            return
+        }
+
         // Schedule the animation to start after a debounce period
         cancelExecutionRunnable = executor.executeDelayed({
             cancelExecutionRunnable = null
@@ -218,7 +220,7 @@
 
     private fun notifyTransitionToPersistentDot(): Animator? {
         val anims: List<Animator> = listeners.mapNotNull {
-            it.onSystemStatusAnimationTransitionToPersistentDot()
+            it.onSystemStatusAnimationTransitionToPersistentDot(scheduledEvent?.contentDescription)
         }
         if (anims.isNotEmpty()) {
             val aSet = AnimatorSet()
@@ -346,7 +348,10 @@
     @JvmDefault fun onSystemChromeAnimationEnd() {}
 
     // Best method name, change my mind
-    @JvmDefault fun onSystemStatusAnimationTransitionToPersistentDot(): Animator? { return null }
+    @JvmDefault
+    fun onSystemStatusAnimationTransitionToPersistentDot(contentDescription: String?): Animator? {
+        return null
+    }
     @JvmDefault fun onHidePersistentDot(): Animator? { return null }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index 71546ae..b053b5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -43,7 +43,7 @@
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.settings.UserTracker
-import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.concurrency.Execution
 import com.android.systemui.util.settings.SecureSettings
@@ -74,15 +74,27 @@
 ) {
     private var session: SmartspaceSession? = null
     private val plugin: BcSmartspaceDataPlugin? = optionalPlugin.orElse(null)
-    private lateinit var smartspaceView: SmartspaceView
 
-    lateinit var view: View
-        private set
+    // Smartspace can be used on multiple displays, such as when the user casts their screen
+    private var smartspaceViews = mutableSetOf<SmartspaceView>()
 
     private var showSensitiveContentForCurrentUser = false
     private var showSensitiveContentForManagedUser = false
     private var managedUserHandle: UserHandle? = null
 
+    var stateChangeListener = object : View.OnAttachStateChangeListener {
+        override fun onViewAttachedToWindow(v: View) {
+            smartspaceViews.add(v as SmartspaceView)
+
+            updateTextColorFromWallpaper()
+            statusBarStateListener.onDozeAmountChanged(0f, statusBarStateController.dozeAmount)
+        }
+
+        override fun onViewDetachedFromWindow(v: View) {
+            smartspaceViews.remove(v as SmartspaceView)
+        }
+    }
+
     fun isEnabled(): Boolean {
         execution.assertIsMainThread()
 
@@ -90,17 +102,16 @@
     }
 
     /**
-     * Constructs the smartspace view and connects it to the smartspace service. Subsequent calls
-     * are idempotent until [disconnect] is called.
+     * Constructs the smartspace view and connects it to the smartspace service.
      */
-    fun buildAndConnectView(parent: ViewGroup): View {
+    fun buildAndConnectView(parent: ViewGroup): View? {
         execution.assertIsMainThread()
 
         if (!isEnabled()) {
             throw RuntimeException("Cannot build view when not enabled")
         }
 
-        buildView(parent)
+        val view = buildView(parent)
         connectSession()
 
         return view
@@ -110,14 +121,9 @@
         session?.requestSmartspaceUpdate()
     }
 
-    private fun buildView(parent: ViewGroup) {
+    private fun buildView(parent: ViewGroup): View? {
         if (plugin == null) {
-            return
-        }
-        if (this::view.isInitialized) {
-            // Due to some oddities with a singleton smartspace view, allow reparenting
-            (view.getParent() as ViewGroup?)?.removeView(view)
-            return
+            return null
         }
 
         val ssView = plugin.getView(parent)
@@ -132,12 +138,7 @@
             }
         })
         ssView.setFalsingManager(falsingManager)
-
-        this.smartspaceView = ssView
-        this.view = ssView as View
-
-        updateTextColorFromWallpaper()
-        statusBarStateListener.onDozeAmountChanged(0f, statusBarStateController.dozeAmount)
+        return (ssView as View).apply { addOnAttachStateChangeListener(stateChangeListener) }
     }
 
     private fun connectSession() {
@@ -165,8 +166,6 @@
 
     /**
      * Disconnects the smartspace view from the smartspace service and cleans up any resources.
-     * Calling [buildAndConnectView] again will cause the same view to be reconnected to the
-     * service.
      */
     fun disconnect() {
         execution.assertIsMainThread()
@@ -231,7 +230,7 @@
     private val statusBarStateListener = object : StatusBarStateController.StateListener {
         override fun onDozeAmountChanged(linear: Float, eased: Float) {
             execution.assertIsMainThread()
-            smartspaceView.setDozeAmount(eased)
+            smartspaceViews.forEach { it.setDozeAmount(eased) }
         }
     }
 
@@ -256,7 +255,7 @@
 
     private fun updateTextColorFromWallpaper() {
         val wallpaperTextColor = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor)
-        smartspaceView.setPrimaryTextColor(wallpaperTextColor)
+        smartspaceViews.forEach { it.setPrimaryTextColor(wallpaperTextColor) }
     }
 
     private fun reloadSmartspace() {
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 2537b19..129fa5a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationActivityStarter.java
@@ -20,6 +20,7 @@
 import android.service.notification.StatusBarNotification;
 import android.view.View;
 
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
 /**
@@ -37,6 +38,9 @@
     /** Called when the user clicks "Manage" or "History" in the Shade. */
     void startHistoryIntent(View view, boolean showHistory);
 
+    /** Called when the user succeed to drop notification to proper target view. */
+    void onDragSuccess(NotificationEntry entry);
+
     default boolean isCollapsingToShowActivityOverLockscreen() {
         return false;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
index 0fb1c54..da70621 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -43,6 +43,14 @@
     private final Optional<Bubbles> mBubblesOptional;
     private final NotificationActivityStarter mNotificationActivityStarter;
 
+    private ExpandableNotificationRow.OnDragSuccessListener mOnDragSuccessListener =
+            new ExpandableNotificationRow.OnDragSuccessListener() {
+                @Override
+                public void onDragSuccess(NotificationEntry entry) {
+                    mNotificationActivityStarter.onDragSuccess(entry);
+                }
+            };
+
     private NotificationClicker(
             NotificationClickerLogger logger,
             Optional<StatusBar> statusBarOptional,
@@ -111,8 +119,10 @@
         if (notification.contentIntent != null || notification.fullScreenIntent != null
                 || row.getEntry().isBubble()) {
             row.setOnClickListener(this);
+            row.setOnDragSuccessListener(mOnDragSuccessListener);
         } else {
             row.setOnClickListener(null);
+            row.setOnDragSuccessListener(null);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 1ab2a9e..a65f3d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -37,7 +37,7 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.Dumpable;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLifetimeExtender;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 8ae31cb..277b4ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -63,7 +63,7 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.dump.LogBufferEulogizer;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.statusbar.notification.collection.coalescer.CoalescedEvent;
 import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer;
 import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer.BatchableNotificationHandler;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index a0ef1b6..e26fa04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -28,8 +28,6 @@
 import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_SORTING;
 import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_TRANSFORMING;
 
-import static java.util.Objects.requireNonNull;
-
 import android.annotation.MainThread;
 import android.annotation.Nullable;
 import android.util.ArrayMap;
@@ -344,14 +342,8 @@
         mPipelineState.incrementTo(STATE_GROUP_STABILIZING);
         stabilizeGroupingNotifs(mNotifList);
 
-        // Step 5: Sort
-        // Assign each top-level entry a section, then sort the list by section and then within
-        // section by our list of custom comparators
-        dispatchOnBeforeSort(mReadOnlyNotifList);
-        mPipelineState.incrementTo(STATE_SORTING);
-        sortList();
 
-        // Step 6: Filter out entries after pre-group filtering, grouping, promoting and sorting
+        // Step 5: Filter out entries after pre-group filtering, grouping and promoting
         // Now filters can see grouping information to determine whether to filter or not.
         dispatchOnBeforeFinalizeFilter(mReadOnlyNotifList);
         mPipelineState.incrementTo(STATE_FINALIZE_FILTERING);
@@ -359,6 +351,13 @@
         applyNewNotifList();
         pruneIncompleteGroups(mNotifList);
 
+        // Step 6: Sort
+        // Assign each top-level entry a section, then sort the list by section and then within
+        // section by our list of custom comparators
+        dispatchOnBeforeSort(mReadOnlyNotifList);
+        mPipelineState.incrementTo(STATE_SORTING);
+        sortList();
+
         // Step 7: Lock in our group structure and log anything that's changed since the last run
         mPipelineState.incrementTo(STATE_FINALIZING);
         logChanges();
@@ -837,8 +836,8 @@
     private final Comparator<ListEntry> mTopLevelComparator = (o1, o2) -> {
 
         int cmp = Integer.compare(
-                requireNonNull(o1.getSection()).getIndex(),
-                requireNonNull(o2.getSection()).getIndex());
+                o1.getSectionIndex(),
+                o2.getSectionIndex());
 
         if (cmp == 0) {
             for (int i = 0; i < mNotifComparators.size(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
index d80cc08..25b2019 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
@@ -19,7 +19,7 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java
index aec2647..518c3f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java
@@ -17,7 +17,7 @@
 package com.android.systemui.statusbar.notification.collection.inflation;
 
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.statusbar.notification.collection.GroupEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
index db49e44..168e086 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
@@ -21,7 +21,7 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java
index 798bfe7..027ac0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java
@@ -82,8 +82,8 @@
     public static final int STATE_GROUPING = 4;
     public static final int STATE_TRANSFORMING = 5;
     public static final int STATE_GROUP_STABILIZING = 6;
-    public static final int STATE_SORTING = 7;
-    public static final int STATE_FINALIZE_FILTERING = 8;
+    public static final int STATE_FINALIZE_FILTERING = 7;
+    public static final int STATE_SORTING = 8;
     public static final int STATE_FINALIZING = 9;
 
     @IntDef(prefix = { "STATE_" }, value = {
@@ -94,8 +94,8 @@
             STATE_GROUPING,
             STATE_TRANSFORMING,
             STATE_GROUP_STABILIZING,
-            STATE_SORTING,
             STATE_FINALIZE_FILTERING,
+            STATE_SORTING,
             STATE_FINALIZING,
     })
     @Retention(RetentionPolicy.SOURCE)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
index c1a63e9..ef1d75e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
@@ -53,6 +53,9 @@
             notifList.firstOrNull()?.section?.headerController?.let {
                 children.add(NodeSpecImpl(this, it))
             }
+            notifList.firstOrNull()?.let {
+                children.add(buildNotifNode(it, this))
+            }
             notifList.asSequence().zipWithNext().forEach { (prev, entry) ->
                 // Insert new header if the section has changed between two entries
                 entry.section.takeIf { it != prev.section }?.headerController?.let {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index f758114..55620b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -30,11 +30,11 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dagger.qualifiers.UiBackground;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.settings.UserContextProvider;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.notification.AssistantFeedbackController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 32d90d3..c5899ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -20,7 +20,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.people.widget.PeopleSpaceWidgetManager
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption
-import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.statusbar.NotificationListener
 import com.android.systemui.statusbar.NotificationPresenter
 import com.android.systemui.statusbar.notification.AnimatedImageNotificationManager
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 4f3406c..acb0e82 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
@@ -21,6 +21,7 @@
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Canvas;
+import android.graphics.Point;
 import android.graphics.RectF;
 import android.util.AttributeSet;
 import android.util.MathUtils;
@@ -139,6 +140,8 @@
     private boolean mIsHeadsUpAnimation;
     private int mHeadsUpAddStartLocation;
     private float mHeadsUpLocation;
+    /* In order to track headsup longpress coorindate. */
+    protected Point mTargetPoint;
     private boolean mIsAppearing;
     private boolean mDismissed;
     private boolean mRefocusOnDismiss;
@@ -521,8 +524,8 @@
             @Override
             public void onAnimationStart(Animator animation) {
                 mWasCancelled = false;
-                Configuration.Builder builder = new Configuration.Builder(getCujType(isAppearing))
-                        .setView(ActivatableNotificationView.this);
+                Configuration.Builder builder = Configuration.Builder
+                        .withView(getCujType(isAppearing), ActivatableNotificationView.this);
                 InteractionJankMonitor.getInstance().begin(builder);
             }
 
@@ -568,8 +571,19 @@
         final int actualHeight = getActualHeight();
         float bottom = actualHeight * interpolatedFraction;
 
-        setOutlineRect(0, mAppearAnimationTranslation, getWidth(),
-                bottom + mAppearAnimationTranslation);
+        if (mTargetPoint != null) {
+            int width = getWidth();
+            float fraction = 1 - mAppearAnimationFraction;
+
+            setOutlineRect(mTargetPoint.x * fraction,
+                    mAnimationTranslationY
+                            + (mAnimationTranslationY - mTargetPoint.y) * fraction,
+                    width - (width - mTargetPoint.x) * fraction,
+                    actualHeight - (actualHeight - mTargetPoint.y) * fraction);
+        } else {
+            setOutlineRect(0, mAppearAnimationTranslation, getWidth(),
+                    bottom + mAppearAnimationTranslation);
+        }
     }
 
     private float getInterpolatedAppearAnimationFraction() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 73bb6cd..0d8e850 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -37,6 +37,7 @@
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Path;
+import android.graphics.Point;
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.AnimationDrawable;
 import android.graphics.drawable.ColorDrawable;
@@ -260,6 +261,8 @@
     // Use #setLongPressPosition to optionally assign positional data with the long press.
     private LongPressListener mLongPressListener;
 
+    private ExpandableNotificationRowDragController mDragController;
+
     private boolean mGroupExpansionChanging;
 
     /**
@@ -331,6 +334,7 @@
                 }
             };
     private OnClickListener mOnClickListener;
+    private OnDragSuccessListener mOnDragSuccessListener;
     private boolean mHeadsupDisappearRunning;
     private View mChildAfterViewWhenDismissed;
     private View mGroupParentWhenDismissed;
@@ -1083,6 +1087,10 @@
         mLongPressListener = longPressListener;
     }
 
+    public void setDragController(ExpandableNotificationRowDragController dragController) {
+        mDragController = dragController;
+    }
+
     @Override
     public void setOnClickListener(@Nullable OnClickListener l) {
         super.setOnClickListener(l);
@@ -1329,6 +1337,7 @@
     public void dismiss(boolean refocusOnDismiss) {
         super.dismiss(refocusOnDismiss);
         setLongPressListener(null);
+        setDragController(null);
         mGroupParentWhenDismissed = mNotificationParent;
         mChildAfterViewWhenDismissed = null;
         mEntry.getIcons().getStatusBarIcon().setDismissed();
@@ -1637,6 +1646,8 @@
         }
         onHeightReset();
         requestLayout();
+
+        setTargetPoint(null);
     }
 
     public void showFeedbackIcon(boolean show, Pair<Integer, Integer> resIds) {
@@ -1727,6 +1738,29 @@
         mTranslateableViews.remove(mGutsStub);
     }
 
+    /**
+     * Called once when starting drag motion after opening notification guts,
+     * in case of notification that has {@link android.app.Notification#contentIntent}
+     * and it is to start an activity.
+     */
+    public void doDragCallback(float x, float y) {
+        if (mDragController != null) {
+            setTargetPoint(new Point((int) x, (int) y));
+            mDragController.startDragAndDrop(this);
+        }
+    }
+
+    public void setOnDragSuccessListener(OnDragSuccessListener listener) {
+        mOnDragSuccessListener = listener;
+    }
+
+    /**
+     * Called when a notification is dropped on proper target window.
+     */
+    public void dragAndDropSuccess() {
+        mOnDragSuccessListener.onDragSuccess(getEntry());
+    }
+
     private void doLongClickCallback() {
         doLongClickCallback(getWidth() / 2, getHeight() / 2);
     }
@@ -3255,6 +3289,16 @@
     }
 
     /**
+     * Called when notification drag and drop is finished successfully.
+     */
+    public interface OnDragSuccessListener {
+        /**
+         * @param entry NotificationEntry that succeed to drop on proper target window.
+         */
+        void onDragSuccess(NotificationEntry entry);
+    }
+
+    /**
      * Equivalent to View.OnClickListener with coordinates
      */
     public interface CoordinateOnClickListener {
@@ -3321,4 +3365,11 @@
             }
         }
     }
+
+    private void setTargetPoint(Point p) {
+        mTargetPoint = p;
+    }
+    public Point getTargetPoint() {
+        return mTargetPoint;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index c9fcdac8..0662a1e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -25,6 +25,7 @@
 
 import androidx.annotation.NonNull;
 
+import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -85,6 +86,8 @@
     private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
     private final Optional<BubblesManager> mBubblesManagerOptional;
 
+    private final ExpandableNotificationRowDragController mDragController;
+
     @Inject
     public ExpandableNotificationRowController(
             ExpandableNotificationRow view,
@@ -109,7 +112,8 @@
             FalsingManager falsingManager,
             FalsingCollector falsingCollector,
             PeopleNotificationIdentifier peopleNotificationIdentifier,
-            Optional<BubblesManager> bubblesManagerOptional) {
+            Optional<BubblesManager> bubblesManagerOptional,
+            ExpandableNotificationRowDragController dragController) {
         mView = view;
         mListContainer = listContainer;
         mActivatableNotificationViewController = activatableNotificationViewController;
@@ -134,6 +138,7 @@
         mFalsingCollector = falsingCollector;
         mPeopleNotificationIdentifier = peopleNotificationIdentifier;
         mBubblesManagerOptional = bubblesManagerOptional;
+        mDragController = dragController;
     }
 
     /**
@@ -164,6 +169,10 @@
         );
         mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
         if (mAllowLongPress) {
+            if (mView.getResources().getBoolean(R.bool.config_notificationToContents)) {
+                mView.setDragController(mDragController);
+            }
+
             mView.setLongPressListener((v, x, y, item) -> {
                 if (mView.isSummaryWithChildren()) {
                     mView.expandNotification();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
new file mode 100644
index 0000000..06b739b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
@@ -0,0 +1,171 @@
+/*
+ * 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.notification.row;
+
+import android.annotation.NonNull;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+import android.view.DragEvent;
+import android.view.HapticFeedbackConstants;
+import android.view.View;
+import android.widget.ImageView;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+
+import javax.inject.Inject;
+
+/**
+ * Controller for Notification to window.
+ */
+public class ExpandableNotificationRowDragController {
+    private static final String TAG = ExpandableNotificationRowDragController.class.getSimpleName();
+    private int mIconSize;
+
+    private final Context mContext;
+    private final HeadsUpManager mHeadsUpManager;
+
+    @Inject
+    public ExpandableNotificationRowDragController(Context context,
+            HeadsUpManager headsUpManager) {
+        mContext = context;
+        mHeadsUpManager = headsUpManager;
+
+        init();
+    }
+
+    private void init() {
+        mIconSize = mContext.getResources().getDimensionPixelSize(R.dimen.drag_and_drop_icon_size);
+    }
+
+    /**
+     * Called when drag event beyond the touchslop,
+     * and start drag and drop.
+     *
+     * @param view notification that was long pressed and started to drag and drop.
+     */
+    @VisibleForTesting
+    public void startDragAndDrop(View view) {
+        ExpandableNotificationRow enr = null;
+        if (view instanceof ExpandableNotificationRow) {
+            enr = (ExpandableNotificationRow) view;
+        }
+
+        StatusBarNotification sn = enr.getEntry().getSbn();
+        Notification notification = sn.getNotification();
+        final PendingIntent contentIntent = notification.contentIntent != null
+                ? notification.contentIntent
+                : notification.fullScreenIntent;
+        Bitmap iconBitmap = getBitmapFromDrawable(
+                getPkgIcon(enr.getEntry().getSbn().getPackageName()));
+
+        final ImageView snapshot = new ImageView(mContext);
+        snapshot.setImageBitmap(iconBitmap);
+        snapshot.layout(0, 0, mIconSize, mIconSize);
+
+        ClipDescription clipDescription = new ClipDescription("Drag And Drop",
+                new String[]{ClipDescription.MIMETYPE_APPLICATION_ACTIVITY});
+        Intent dragIntent = new Intent();
+        dragIntent.putExtra("android.intent.extra.PENDING_INTENT", contentIntent);
+        dragIntent.putExtra(Intent.EXTRA_USER, android.os.Process.myUserHandle());
+        ClipData.Item item = new ClipData.Item(dragIntent);
+        ClipData dragData = new ClipData(clipDescription, item);
+        View.DragShadowBuilder myShadow = new View.DragShadowBuilder(snapshot);
+        view.setOnDragListener(getDraggedViewDragListener());
+        view.startDragAndDrop(dragData, myShadow, null, View.DRAG_FLAG_GLOBAL);
+    }
+
+
+    private Drawable getPkgIcon(String pkgName) {
+        Drawable pkgicon = null;
+        PackageManager pm = mContext.getPackageManager();
+        ApplicationInfo info;
+        try {
+            info = pm.getApplicationInfo(
+                    pkgName,
+                    PackageManager.MATCH_UNINSTALLED_PACKAGES
+                            | PackageManager.MATCH_DISABLED_COMPONENTS
+                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                            | PackageManager.MATCH_DIRECT_BOOT_AWARE);
+            if (info != null) {
+                pkgicon = pm.getApplicationIcon(info);
+            } else {
+                Log.d(TAG, " application info is null ");
+                pkgicon = pm.getDefaultActivityIcon();
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.d(TAG, "can not find package with : " + pkgName);
+            pkgicon = pm.getDefaultActivityIcon();
+        }
+
+        return pkgicon;
+    }
+
+    private Bitmap getBitmapFromDrawable(@NonNull Drawable drawable) {
+        final Bitmap bmp = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
+                drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+        final Canvas canvas = new Canvas(bmp);
+        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+        drawable.draw(canvas);
+        return bmp;
+    }
+
+    private View.OnDragListener getDraggedViewDragListener() {
+        return (view, dragEvent) -> {
+            switch (dragEvent.getAction()) {
+                case DragEvent.ACTION_DRAG_STARTED:
+                    view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+                    if (view instanceof ExpandableNotificationRow) {
+                        ExpandableNotificationRow enr = (ExpandableNotificationRow) view;
+                        if (enr.isPinned()) {
+                            mHeadsUpManager.releaseAllImmediately();
+                        } else {
+                            Dependency.get(ShadeController.class).animateCollapsePanels(
+                                    CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
+                        }
+                    }
+                    return true;
+                case DragEvent.ACTION_DRAG_ENDED:
+                    if (dragEvent.getResult()) {
+                        if (view instanceof ExpandableNotificationRow) {
+                            ExpandableNotificationRow enr = (ExpandableNotificationRow) view;
+                            enr.dragAndDropSuccess();
+                        }
+                    }
+                    return true;
+            }
+            return false;
+        };
+    }
+}
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 86c90c7..9eb95c4 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
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.notification.row;
 
-import android.annotation.ColorInt;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -28,15 +27,12 @@
 import com.android.systemui.statusbar.notification.stack.ViewState;
 
 public class FooterView extends StackScrollerDecorView {
-    private final int mClearAllTopPadding;
     private FooterViewButton mDismissButton;
     private FooterViewButton mManageButton;
     private boolean mShowHistory;
 
     public FooterView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mClearAllTopPadding = context.getResources().getDimensionPixelSize(
-                R.dimen.clear_all_padding_top);
     }
 
     @Override
@@ -55,11 +51,6 @@
         mManageButton = findViewById(R.id.manage_text);
     }
 
-    public void setTextColor(@ColorInt int color) {
-        mManageButton.setTextColor(color);
-        mDismissButton.setTextColor(color);
-    }
-
     public void setManageButtonClickListener(OnClickListener listener) {
         mManageButton.setOnClickListener(listener);
     }
@@ -95,21 +86,25 @@
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
-        int textColor = getResources().getColor(R.color.notif_pill_text);
-        Resources.Theme theme = getContext().getTheme();
-        mDismissButton.setBackground(
-                getResources().getDrawable(R.drawable.notif_footer_btn_background, theme));
-        mDismissButton.setTextColor(textColor);
-        mManageButton.setBackground(
-                getResources().getDrawable(R.drawable.notif_footer_btn_background, theme));
-        mManageButton = findViewById(R.id.manage_text);
+        updateColors();
         mDismissButton.setText(R.string.clear_all_notifications_text);
-        mManageButton.setTextColor(textColor);
         mDismissButton.setContentDescription(
                 mContext.getString(R.string.accessibility_clear_all));
         showHistory(mShowHistory);
     }
 
+    /**
+     * Update the text and background colors for the current color palette and night mode setting.
+     */
+    public void updateColors() {
+        Resources.Theme theme = mContext.getTheme();
+        int textColor = getResources().getColor(R.color.notif_pill_text, theme);
+        mDismissButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background));
+        mDismissButton.setTextColor(textColor);
+        mManageButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background));
+        mManageButton.setTextColor(textColor);
+    }
+
     @Override
     public ExpandableViewState createExpandableViewState() {
         return new FooterViewState();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
index dba3401..9c755e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
@@ -85,23 +85,26 @@
     }
 
     /**
-     * Set the content of this view to be visible in an animated way.
-     *
-     * @param contentVisible True if the content should be visible or false if it should be hidden.
+     * @param visible True if we should animate contents visible
      */
-    public void setContentVisible(boolean contentVisible) {
-        setContentVisible(contentVisible, true /* animate */);
+    public void setContentVisible(boolean visible) {
+        setContentVisible(visible, true /* animate */, null /* runAfter */);
     }
+
     /**
-     * Set the content of this view to be visible.
-     * @param contentVisible True if the content should be visible or false if it should be hidden.
-     * @param animate Should an animation be performed.
+     * @param visible True if the contents should be visible
+     * @param animate True if we should fade to new visibility
+     * @param runAfter Runnable to run after visibility updates
      */
-    private void setContentVisible(boolean contentVisible, boolean animate) {
-        if (mContentVisible != contentVisible) {
+    public void setContentVisible(boolean visible, boolean animate, Runnable runAfter) {
+        if (mContentVisible != visible) {
             mContentAnimating = animate;
-            mContentVisible = contentVisible;
-            setViewVisible(mContent, contentVisible, animate, mContentVisibilityEndRunnable);
+            mContentVisible = visible;
+            Runnable endRunnable = runAfter == null ? mContentVisibilityEndRunnable : () -> {
+                mContentVisibilityEndRunnable.run();
+                runAfter.run();
+            };
+            setViewVisible(mContent, visible, animate, endRunnable);
         }
 
         if (!mContentAnimating) {
@@ -113,6 +116,10 @@
         return mContentVisible;
     }
 
+    public void setVisible(boolean nowVisible, boolean animate) {
+        setVisible(nowVisible, animate, null);
+    }
+
     /**
      * Make this view visible. If {@code false} is passed, the view will fade out it's content
      * and set the view Visibility to GONE. If only the content should be changed
@@ -121,7 +128,7 @@
      * @param nowVisible should the view be visible
      * @param animate should the change be animated.
      */
-    public void setVisible(boolean nowVisible, boolean animate) {
+    public void setVisible(boolean nowVisible, boolean animate, Runnable runAfter) {
         if (mIsVisible != nowVisible) {
             mIsVisible = nowVisible;
             if (animate) {
@@ -132,10 +139,10 @@
                 } else {
                     setWillBeGone(true);
                 }
-                setContentVisible(nowVisible, true /* animate */);
+                setContentVisible(nowVisible, true /* animate */, runAfter);
             } else {
                 setVisibility(nowVisible ? VISIBLE : GONE);
-                setContentVisible(nowVisible, false /* animate */);
+                setContentVisible(nowVisible, false /* animate */, runAfter);
                 setWillBeGone(false);
                 notifyHeightChanged(false /* needsAnimation */);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 4559e13..45fd5ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -153,15 +153,6 @@
         return mStackHeight;
     }
 
-    /**
-     * @return Height of notifications panel, with the animation from pulseHeight accounted for.
-     */
-    // TODO(b/192348384): move this logic to getStackHeight, and remove this and getInnerHeight
-    public float getPulseStackHeight() {
-        float pulseHeight = Math.min(mPulseHeight, mStackHeight);
-        return MathUtils.lerp(mStackHeight, pulseHeight, mDozeAmount);
-    }
-
     /** Tracks the state from AlertingNotificationManager#hasNotifications() */
     private boolean mHasAlertEntries;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index 23aefd9..594afce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -46,6 +46,7 @@
     private Runnable mRoundingChangedCallback;
     private ExpandableNotificationRow mTrackedHeadsUp;
     private float mAppearFraction;
+    private boolean mIsDismissAllInProgress;
 
     private ExpandableView mSwipedView = null;
     private ExpandableView mViewBeforeSwipedView = null;
@@ -162,6 +163,10 @@
         }
     }
 
+    void setDismissAllInProgress(boolean isClearingAll) {
+        mIsDismissAllInProgress = isClearingAll;
+    }
+
     private float getRoundness(ExpandableView view, boolean top) {
         if (view == null) {
             return 0f;
@@ -171,6 +176,11 @@
                 || view == mViewAfterSwipedView) {
             return 1f;
         }
+        if (view instanceof ExpandableNotificationRow
+                && ((ExpandableNotificationRow) view).canViewBeDismissed()
+                && mIsDismissAllInProgress) {
+            return 1.0f;
+        }
         if ((view.isPinned()
                 || (view.isHeadsUpAnimatingAway()) && !mExpanded)) {
             return 1.0f;
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 2033adf..7babcba 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
@@ -81,7 +81,6 @@
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.EmptyShadeView;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.NotificationShelfController;
 import com.android.systemui.statusbar.StatusBarState;
@@ -140,6 +139,10 @@
     private static final boolean DEBUG_REMOVE_ANIMATION = SystemProperties.getBoolean(
             "persist.debug.nssl.dismiss", false /* default */);
 
+    // Delay in milli-seconds before shade closes for clear all.
+    private final int DELAY_BEFORE_SHADE_CLOSE = 200;
+    private boolean mShadeNeedsToClose = false;
+
     private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
     private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
     private static final float RUBBER_BAND_FACTOR_ON_PANEL_EXPAND = 0.21f;
@@ -254,7 +257,6 @@
     protected FooterView mFooterView;
     protected EmptyShadeView mEmptyShadeView;
     private boolean mDismissAllInProgress;
-    private boolean mFadeNotificationsOnDismiss;
     private FooterDismissListener mFooterDismissListener;
     private boolean mFlingAfterUpEvent;
 
@@ -408,6 +410,7 @@
     private boolean mBackwardScrollable;
     private NotificationShelf mShelf;
     private int mMaxDisplayedNotifications = -1;
+    private float mKeyguardBottomPadding = -1;
     private int mStatusBarHeight;
     private int mMinInteractionHeight;
     private final Rect mClipRect = new Rect();
@@ -743,6 +746,16 @@
             mDebugPaint.setColor(Color.YELLOW);
             canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
 
+            y = (int) mMaxLayoutHeight;
+            mDebugPaint.setColor(Color.MAGENTA);
+            canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
+
+            if (mKeyguardBottomPadding >= 0) {
+                y = getHeight() - (int) mKeyguardBottomPadding;
+                mDebugPaint.setColor(Color.GRAY);
+                canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
+            }
+
             y = getHeight() - getEmptyBottomMargin();
             mDebugPaint.setColor(Color.GREEN);
             canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
@@ -1195,7 +1208,7 @@
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private void clampScrollPosition() {
         int scrollRange = getScrollRange();
-        if (scrollRange < mOwnScrollY) {
+        if (scrollRange < mOwnScrollY && !mAmbientState.isDismissAllInProgress()) {
             boolean animateStackY = false;
             if (scrollRange < getScrollAmountToScrollBoundary()
                     && mAnimateStackYForContentHeightChange) {
@@ -1711,6 +1724,11 @@
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void dismissViewAnimated(View child, Runnable endRunnable, int delay, long duration) {
+        if (child instanceof SectionHeaderView) {
+             ((StackScrollerDecorView) child).setContentVisible(
+                     false /* visible */, true /* animate */, endRunnable);
+             return;
+        }
         mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration,
                 true /* isDismissAll */);
     }
@@ -4052,6 +4070,19 @@
         runAnimationFinishedRunnables();
         clearTransient();
         clearHeadsUpDisappearRunning();
+
+        if (mAmbientState.isDismissAllInProgress()) {
+            setDismissAllInProgress(false);
+
+            if (mShadeNeedsToClose) {
+                mShadeNeedsToClose = false;
+                postDelayed(
+                        () -> {
+                            mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
+                        },
+                        DELAY_BEFORE_SHADE_CLOSE /* delayMillis */);
+            }
+        }
     }
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -4233,7 +4264,7 @@
         final @ColorInt int textColor =
                 Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorPrimary);
         mSectionsManager.setHeaderForegroundColor(textColor);
-        mFooterView.setTextColor(textColor);
+        mFooterView.updateColors();
         mEmptyShadeView.setTextColor(textColor);
     }
 
@@ -4420,6 +4451,7 @@
     public void setDismissAllInProgress(boolean dismissAllInProgress) {
         mDismissAllInProgress = dismissAllInProgress;
         mAmbientState.setDismissAllInProgress(dismissAllInProgress);
+        mController.getNoticationRoundessManager().setDismissAllInProgress(dismissAllInProgress);
         handleDismissAllClipping();
     }
 
@@ -4785,6 +4817,16 @@
         }
     }
 
+    /**
+     * This is used for debugging only; it will be used to draw the otherwise invisible line which
+     * NotificationPanelViewController treats as the bottom when calculating how many notifications
+     * appear on the keyguard.
+     * Setting a negative number will disable rendering this line.
+     */
+    public void setKeyguardBottomPadding(float keyguardBottomPadding) {
+        mKeyguardBottomPadding = keyguardBottomPadding;
+    }
+
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) {
         mShouldShowShelfOnly = shouldShowShelfOnly;
@@ -4953,64 +4995,129 @@
         mHeadsUpAppearanceController = headsUpAppearanceController;
     }
 
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    @VisibleForTesting
-    void clearNotifications(@SelectedRows int selection, boolean closeShade) {
-        // animate-swipe all dismissable notifications, then animate the shade closed
-        int numChildren = getChildCount();
+    private boolean isVisible(View child) {
+        boolean hasClipBounds = child.getClipBounds(mTmpRect);
+        return child.getVisibility() == View.VISIBLE
+                && (!hasClipBounds || mTmpRect.height() > 0);
+    }
 
-        final ArrayList<View> viewsToHide = new ArrayList<>(numChildren);
-        final ArrayList<ExpandableNotificationRow> viewsToRemove = new ArrayList<>(numChildren);
-        for (int i = 0; i < numChildren; i++) {
-            final View child = getChildAt(i);
-            if (child instanceof ExpandableNotificationRow) {
-                ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-                boolean parentVisible = false;
-                boolean hasClipBounds = child.getClipBounds(mTmpRect);
-                if (includeChildInDismissAll(row, selection)) {
-                    viewsToRemove.add(row);
-                    if (child.getVisibility() == View.VISIBLE
-                            && (!hasClipBounds || mTmpRect.height() > 0)) {
-                        viewsToHide.add(child);
-                        parentVisible = true;
-                    }
-                } else if (child.getVisibility() == View.VISIBLE
-                        && (!hasClipBounds || mTmpRect.height() > 0)) {
-                    parentVisible = true;
-                }
-                List<ExpandableNotificationRow> children = row.getAttachedChildren();
-                if (children != null) {
-                    for (ExpandableNotificationRow childRow : children) {
-                        if (includeChildInDismissAll(row, selection)) {
-                            viewsToRemove.add(childRow);
-                            if (parentVisible && row.areChildrenExpanded()) {
-                                hasClipBounds = childRow.getClipBounds(mTmpRect);
-                                if (childRow.getVisibility() == View.VISIBLE
-                                        && (!hasClipBounds || mTmpRect.height() > 0)) {
-                                    viewsToHide.add(childRow);
-                                }
-                            }
+    private boolean shouldHideParent(View view, @SelectedRows int selection) {
+        final boolean silentSectionWillBeGone =
+                !mController.hasNotifications(ROWS_GENTLE, false /* clearable */);
+
+        // The only SectionHeaderView we have is the silent section header.
+        if (view instanceof SectionHeaderView && silentSectionWillBeGone) {
+            return true;
+        }
+        if (view instanceof ExpandableNotificationRow) {
+            ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+            if (isVisible(row) && includeChildInDismissAll(row, selection)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isChildrenVisible(ExpandableNotificationRow parent) {
+        List<ExpandableNotificationRow> children = parent.getAttachedChildren();
+        return isVisible(parent)
+                && children != null
+                && parent.areChildrenExpanded();
+    }
+
+    // Similar to #getRowsToDismissInBackend, but filters for visible views.
+    private ArrayList<View> getVisibleViewsToAnimateAway(@SelectedRows int selection) {
+        final int viewCount = getChildCount();
+        final ArrayList<View> viewsToHide = new ArrayList<>(viewCount);
+
+        for (int i = 0; i < viewCount; i++) {
+            final View view = getChildAt(i);
+
+            if (shouldHideParent(view, selection)) {
+                viewsToHide.add(view);
+            }
+            if (view instanceof ExpandableNotificationRow) {
+                ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
+
+                if (isChildrenVisible(parent)) {
+                    for (ExpandableNotificationRow child : parent.getAttachedChildren()) {
+                        if (isVisible(child) && includeChildInDismissAll(child, selection)) {
+                            viewsToHide.add(child);
                         }
                     }
                 }
             }
         }
+        return viewsToHide;
+    }
 
+    private ArrayList<ExpandableNotificationRow> getRowsToDismissInBackend(
+            @SelectedRows int selection) {
+        final int childCount = getChildCount();
+        final ArrayList<ExpandableNotificationRow> viewsToRemove = new ArrayList<>(childCount);
+
+        for (int i = 0; i < childCount; i++) {
+            final View view = getChildAt(i);
+            if (!(view instanceof ExpandableNotificationRow)) {
+                continue;
+            }
+            ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
+            if (includeChildInDismissAll(parent, selection)) {
+                viewsToRemove.add(parent);
+            }
+            List<ExpandableNotificationRow> children = parent.getAttachedChildren();
+            if (isVisible(parent) && children != null) {
+                for (ExpandableNotificationRow child : children) {
+                    if (includeChildInDismissAll(parent, selection)) {
+                        viewsToRemove.add(child);
+                    }
+                }
+            }
+        }
+        return viewsToRemove;
+    }
+
+    /**
+     * Collects a list of visible rows, and animates them away in a staggered fashion as if they
+     * were dismissed. Notifications are dismissed in the backend via onDismissAllAnimationsEnd.
+     */
+    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+    @VisibleForTesting
+    void clearNotifications(@SelectedRows int selection, boolean closeShade) {
+        // Animate-swipe all dismissable notifications, then animate the shade closed
+        final ArrayList<View> viewsToAnimateAway = getVisibleViewsToAnimateAway(selection);
+        final ArrayList<ExpandableNotificationRow> rowsToDismissInBackend =
+                getRowsToDismissInBackend(selection);
         if (mDismissListener != null) {
             mDismissListener.onDismiss(selection);
         }
-
-        if (viewsToRemove.isEmpty()) {
-            if (closeShade && mShadeController != null) {
-                mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
-            }
+        final Runnable dismissInBackend = () -> {
+            onDismissAllAnimationsEnd(rowsToDismissInBackend, selection);
+        };
+        if (viewsToAnimateAway.isEmpty()) {
+            dismissInBackend.run();
             return;
         }
+        // Disable normal animations
+        setDismissAllInProgress(true);
+        mShadeNeedsToClose = closeShade;
 
-        performDismissAllAnimations(
-                viewsToHide,
-                closeShade,
-                () -> onDismissAllAnimationsEnd(viewsToRemove, selection));
+        // Decrease the delay for every row we animate to give the sense of
+        // accelerating the swipes
+        final int rowDelayDecrement = 5;
+        int currentDelay = 60;
+        int totalDelay = 0;
+        final int numItems = viewsToAnimateAway.size();
+        for (int i = numItems - 1; i >= 0; i--) {
+            View view = viewsToAnimateAway.get(i);
+            Runnable endRunnable = null;
+            if (i == 0) {
+                endRunnable = dismissInBackend;
+            }
+            dismissViewAnimated(view, endRunnable, totalDelay, ANIMATION_DURATION_SWIPE);
+            currentDelay = Math.max(30, currentDelay - rowDelayDecrement);
+            totalDelay += currentDelay;
+        }
     }
 
     private boolean includeChildInDismissAll(
@@ -5019,63 +5126,6 @@
         return canChildBeDismissed(row) && matchesSelection(row, selection);
     }
 
-    /**
-     * Given a list of rows, animates them away in a staggered fashion as if they were dismissed.
-     * Doesn't actually dismiss them, though -- that must be done in the onAnimationComplete
-     * handler.
-     *
-     * @param hideAnimatedList List of rows to animated away. Should only be views that are
-     *                         currently visible, or else the stagger will look funky.
-     * @param closeShade Whether to close the shade after the stagger animation completes.
-     * @param onAnimationComplete Called after the entire animation completes (including the shade
-     *                            closing if appropriate). The rows must be dismissed for real here.
-     */
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    private void performDismissAllAnimations(
-            final ArrayList<View> hideAnimatedList,
-            final boolean closeShade,
-            final Runnable onAnimationComplete) {
-
-        final Runnable onSlideAwayAnimationComplete = () -> {
-            if (closeShade) {
-                mShadeController.addPostCollapseAction(() -> {
-                    setDismissAllInProgress(false);
-                    onAnimationComplete.run();
-                });
-                mShadeController.animateCollapsePanels(
-                        CommandQueue.FLAG_EXCLUDE_NONE);
-            } else {
-                setDismissAllInProgress(false);
-                onAnimationComplete.run();
-            }
-        };
-
-        if (hideAnimatedList.isEmpty()) {
-            onSlideAwayAnimationComplete.run();
-            return;
-        }
-
-        // let's disable our normal animations
-        setDismissAllInProgress(true);
-
-        // Decrease the delay for every row we animate to give the sense of
-        // accelerating the swipes
-        int rowDelayDecrement = 10;
-        int currentDelay = 140;
-        int totalDelay = 180;
-        int numItems = hideAnimatedList.size();
-        for (int i = numItems - 1; i >= 0; i--) {
-            View view = hideAnimatedList.get(i);
-            Runnable endRunnable = null;
-            if (i == 0) {
-                endRunnable = onSlideAwayAnimationComplete;
-            }
-            dismissViewAnimated(view, endRunnable, totalDelay, ANIMATION_DURATION_SWIPE);
-            currentDelay = Math.max(50, currentDelay - rowDelayDecrement);
-            totalDelay += currentDelay;
-        }
-    }
-
     /** Register a {@link View.OnClickListener} to be invoked when the Manage button is clicked. */
     public void setManageButtonClickListener(@Nullable OnClickListener listener) {
         mManageButtonClickListener = listener;
@@ -5094,6 +5144,7 @@
                 mFooterDismissListener.onDismiss();
             }
             clearNotifications(ROWS_ALL, true /* closeShade */);
+            footerView.setSecondaryVisible(false /* visible */, true /* animate */);
         });
         setFooterView(footerView);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index a160e10..f85c749 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -68,13 +68,13 @@
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.media.KeyguardMediaController;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
@@ -183,6 +183,8 @@
     private int mBarState;
     private HeadsUpAppearanceController mHeadsUpAppearanceController;
 
+    private View mLongPressedView;
+
     private final NotificationListContainerImpl mNotificationListContainer =
             new NotificationListContainerImpl();
 
@@ -492,6 +494,11 @@
                 }
 
                 @Override
+                public void onLongPressSent(View v) {
+                    mLongPressedView = v;
+                }
+
+                @Override
                 public void onBeginDrag(View v) {
                     mFalsingCollector.onNotificationStartDismissing();
                     mView.onSwipeBegin(v);
@@ -1145,6 +1152,10 @@
                 mZenModeController.areNotificationsHiddenInShade());
     }
 
+    public boolean areNotificationsHiddenInShade() {
+        return mZenModeController.areNotificationsHiddenInShade();
+    }
+
     public boolean isShowingEmptyShadeView() {
         return mShowEmptyShadeView;
     }
@@ -1193,6 +1204,10 @@
      * Return whether there are any clearable notifications
      */
     public boolean hasActiveClearableNotifications(@SelectedRows int selection) {
+        return hasNotifications(selection, true /* clearable */);
+    }
+
+    public boolean hasNotifications(@SelectedRows int selection, boolean isClearable) {
         if (mDynamicPrivacyController.isInLockedDownShade()) {
             return false;
         }
@@ -1203,8 +1218,11 @@
                 continue;
             }
             final ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-            if (row.canViewBeDismissed() &&
-                    NotificationStackScrollLayout.matchesSelection(row, selection)) {
+            final boolean matchClearable =
+                    isClearable ? row.canViewBeDismissed() : !row.canViewBeDismissed();
+            final boolean inSection =
+                    NotificationStackScrollLayout.matchesSelection(row, selection);
+            if (matchClearable && inSection) {
                 return true;
             }
         }
@@ -1218,6 +1236,16 @@
         mNotificationListContainer.setMaxDisplayedNotifications(maxNotifications);
     }
 
+    /**
+     * This is used for debugging only; it will be used to draw the otherwise invisible line which
+     * NotificationPanelViewController treats as the bottom when calculating how many notifications
+     * appear on the keyguard.
+     * Setting a negative number will disable rendering this line.
+     */
+    public void setKeyguardBottomPadding(float keyguardBottomPadding) {
+        mView.setKeyguardBottomPadding(keyguardBottomPadding);
+    }
+
     public RemoteInputController.Delegate createDelegate() {
         return new RemoteInputController.Delegate() {
             public void setRemoteInputActive(NotificationEntry entry,
@@ -1425,6 +1453,10 @@
         return mDynamicPrivacyController.isInLockedDownShade();
     }
 
+    public boolean isLongPressInProgress() {
+        return mLongPressedView != null;
+    }
+
     /**
      * Set the dimmed state for all of the notification views.
      */
@@ -1462,6 +1494,11 @@
         mView.setExtraTopInsetForFullShadeTransition(extraTopInset);
     }
 
+    /** */
+    public void setWillExpand(boolean willExpand) {
+        mView.setWillExpand(willExpand);
+    }
+
     /**
      * Set a listener to when scrolling changes.
      */
@@ -1674,17 +1711,23 @@
             mView.handleEmptySpaceClick(ev);
 
             NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
+
+            boolean longPressWantsIt = false;
+            if (mLongPressedView != null) {
+                longPressWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
+            }
             boolean expandWantsIt = false;
-            if (!mSwipeHelper.isSwiping()
+            if (mLongPressedView == null && !mSwipeHelper.isSwiping()
                     && !mView.getOnlyScrollingInThisMotion() && guts == null) {
                 expandWantsIt = mView.getExpandHelper().onInterceptTouchEvent(ev);
             }
             boolean scrollWantsIt = false;
-            if (!mSwipeHelper.isSwiping() && !mView.isExpandingNotification()) {
+            if (mLongPressedView == null && !mSwipeHelper.isSwiping()
+                    && !mView.isExpandingNotification()) {
                 scrollWantsIt = mView.onInterceptTouchEventScroll(ev);
             }
             boolean swipeWantsIt = false;
-            if (!mView.isBeingDragged()
+            if (mLongPressedView == null && !mView.isBeingDragged()
                     && !mView.isExpandingNotification()
                     && !mView.getExpandedInThisMotion()
                     && !mView.getOnlyScrollingInThisMotion()
@@ -1712,7 +1755,7 @@
                 InteractionJankMonitor.getInstance().begin(mView,
                         CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
             }
-            return swipeWantsIt || scrollWantsIt || expandWantsIt;
+            return swipeWantsIt || scrollWantsIt || expandWantsIt || longPressWantsIt;
         }
 
         @Override
@@ -1721,11 +1764,15 @@
             boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
                     || ev.getActionMasked() == MotionEvent.ACTION_UP;
             mView.handleEmptySpaceClick(ev);
+            boolean longPressWantsIt = false;
+            if (guts != null && mLongPressedView != null) {
+                longPressWantsIt = mSwipeHelper.onTouchEvent(ev);
+            }
             boolean expandWantsIt = false;
             boolean onlyScrollingInThisMotion = mView.getOnlyScrollingInThisMotion();
             boolean expandingNotification = mView.isExpandingNotification();
-            if (mView.getIsExpanded() && !mSwipeHelper.isSwiping() && !onlyScrollingInThisMotion
-                    && guts == null) {
+            if (mLongPressedView == null && mView.getIsExpanded()
+                    && !mSwipeHelper.isSwiping() && !onlyScrollingInThisMotion && guts == null) {
                 ExpandHelper expandHelper = mView.getExpandHelper();
                 if (isCancelOrUp) {
                     expandHelper.onlyObserveMovements(false);
@@ -1739,12 +1786,12 @@
                 }
             }
             boolean scrollerWantsIt = false;
-            if (mView.isExpanded() && !mSwipeHelper.isSwiping() && !expandingNotification
-                    && !mView.getDisallowScrollingInThisMotion()) {
+            if (mLongPressedView == null && mView.isExpanded() && !mSwipeHelper.isSwiping()
+                    && !expandingNotification && !mView.getDisallowScrollingInThisMotion()) {
                 scrollerWantsIt = mView.onScrollTouch(ev);
             }
             boolean horizontalSwipeWantsIt = false;
-            if (!mView.isBeingDragged()
+            if (mLongPressedView == null && !mView.isBeingDragged()
                     && !expandingNotification
                     && !mView.getExpandedInThisMotion()
                     && !onlyScrollingInThisMotion
@@ -1770,7 +1817,7 @@
                 mView.setCheckForLeaveBehind(true);
             }
             traceJankOnTouchEvent(ev.getActionMasked(), scrollerWantsIt);
-            return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt;
+            return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || longPressWantsIt;
         }
 
         private void traceJankOnTouchEvent(int action, boolean scrollerWantsIt) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index b36dc56..8be5de7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -157,15 +157,13 @@
         // After the shelf has updated its yTranslation, explicitly set alpha=0 for view below shelf
         // to skip rendering them in the hardware layer. We do not set them invisible because that
         // runs invalidate & onDraw when these views return onscreen, which is more expensive.
+        if (shelf.getViewState().hidden) {
+            // When the shelf is hidden, it won't clip views, so we don't hide rows
+            return;
+        }
         final float shelfTop = shelf.getViewState().yTranslation;
 
         for (ExpandableView view : algorithmState.visibleChildren) {
-            if (view instanceof ExpandableNotificationRow) {
-                ExpandableNotificationRow row = (ExpandableNotificationRow) view;
-                if (row.isHeadsUp() || row.isHeadsUpAnimatingAway()) {
-                    continue;
-                }
-            }
             final float viewTop = view.getViewState().yTranslation;
             if (viewTop >= shelfTop) {
                 view.getViewState().alpha = 0;
@@ -368,6 +366,20 @@
         return stackHeight / stackEndHeight;
     }
 
+    public boolean hasOngoingNotifs(StackScrollAlgorithmState algorithmState) {
+        for (int i = 0; i < algorithmState.visibleChildren.size(); i++) {
+            View child = algorithmState.visibleChildren.get(i);
+            if (!(child instanceof ExpandableNotificationRow)) {
+                continue;
+            }
+            final ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+            if (!row.canViewBeDismissed()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     // TODO(b/172289889) polish shade open from HUN
     /**
      * Populates the {@link ExpandableViewState} for a single child.
@@ -432,7 +444,9 @@
                         + view.getIntrinsicHeight();
                 final boolean noSpaceForFooter = footerEnd > ambientState.getStackEndHeight();
                 ((FooterView.FooterViewState) viewState).hideContent =
-                        isShelfShowing || noSpaceForFooter;
+                        isShelfShowing || noSpaceForFooter
+                                || (ambientState.isDismissAllInProgress()
+                                && !hasOngoingNotifs(algorithmState));
             }
         } else {
             if (view != ambientState.getTrackedHeadsUpRow()) {
@@ -447,19 +461,16 @@
                     // to shelf start, thereby hiding all notifications (except the first one, which
                     // we later unhide in updatePulsingState)
                     // TODO(b/192348384): merge InnerHeight with StackHeight
-                    final int stackBottom;
-                    if (ambientState.isBypassEnabled()) {
-                        // We want to use the stackHeight when pulse expanding, since the animation
-                        // isn't currently optimized if the pulseHeight is continuously changing
-                        // Let's improve this when we're merging the heights above
-                        stackBottom = ambientState.isPulseExpanding()
-                                ? (int) ambientState.getStackHeight()
-                                : ambientState.getInnerHeight();
-                    } else {
-                        stackBottom = !ambientState.isShadeExpanded() || ambientState.isDozing()
-                                        ? ambientState.getInnerHeight()
-                                        : (int) ambientState.getPulseStackHeight();
-                    }
+                    // Note: Bypass pulse looks different, but when it is not expanding, we need
+                    //  to use the innerHeight which doesn't update continuously, otherwise we show
+                    //  more notifications than we should during this special transitional states.
+                    boolean bypassPulseNotExpanding = ambientState.isBypassEnabled()
+                            && ambientState.isOnKeyguard() && !ambientState.isPulseExpanding();
+                    final int stackBottom =
+                            !ambientState.isShadeExpanded() || ambientState.isDozing()
+                                    || bypassPulseNotExpanding
+                                    ? ambientState.getInnerHeight()
+                                    : (int) ambientState.getStackHeight();
                     final int shelfStart =
                             stackBottom - ambientState.getShelf().getIntrinsicHeight();
                     viewState.yTranslation = Math.min(viewState.yTranslation, shelfStart);
@@ -472,7 +483,6 @@
                     }
                 }
             }
-
             // Clip height of view right before shelf.
             viewState.height = (int) (getMaxAllowedChildHeight(view) * expansionFraction);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index ee12b4b..e3a4bf0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -46,7 +46,7 @@
     public static final int ANIMATION_DURATION_WAKEUP = 500;
     public static final int ANIMATION_DURATION_GO_TO_FULL_SHADE = 448;
     public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464;
-    public static final int ANIMATION_DURATION_SWIPE = 260;
+    public static final int ANIMATION_DURATION_SWIPE = 200;
     public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220;
     public static final int ANIMATION_DURATION_CLOSE_REMOTE_INPUT = 150;
     public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 400;
@@ -433,6 +433,7 @@
                     if (row.isDismissed()) {
                         needsAnimation = false;
                     }
+
                     NotificationEntry entry = row.getEntry();
                     StatusBarIconView icon = entry.getIcons().getStatusBarIcon();
                     final StatusBarIconView centeredIcon = entry.getIcons().getCenteredIcon();
@@ -442,7 +443,8 @@
                     if (icon.getParent() != null) {
                         icon.getLocationOnScreen(mTmpLocation);
                         float iconPosition = mTmpLocation[0] - icon.getTranslationX()
-                                + ViewState.getFinalTranslationX(icon) + icon.getWidth() * 0.25f;
+                                + ViewState.getFinalTranslationX(icon)
+                                + icon.getWidth() * 0.25f;
                         mHostLayout.getLocationOnScreen(mTmpLocation);
                         targetLocation = iconPosition - mTmpLocation[0];
                     }
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 00341b5..96c4058 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -39,9 +39,11 @@
 
 import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.OperatorNameView;
+import com.android.systemui.statusbar.OperatorNameViewController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
@@ -90,8 +92,8 @@
     private int mDisabled2;
     private Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
     private DarkIconManager mDarkIconManager;
-    private View mOperatorNameFrame;
     private final CommandQueue mCommandQueue;
+    private final OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
     private final OngoingCallController mOngoingCallController;
     private final SystemStatusAnimationScheduler mAnimationScheduler;
     private final StatusBarLocationPublisher mLocationPublisher;
@@ -114,6 +116,7 @@
             disable(getContext().getDisplayId(), mDisabled1, mDisabled2, animate);
         }
     };
+    private OperatorNameViewController mOperatorNameViewController;
 
     @Inject
     public CollapsedStatusBarFragment(
@@ -127,7 +130,8 @@
             KeyguardStateController keyguardStateController,
             NetworkController networkController,
             StatusBarStateController statusBarStateController,
-            CommandQueue commandQueue
+            CommandQueue commandQueue,
+            OperatorNameViewController.Factory operatorNameViewControllerFactory
     ) {
         mOngoingCallController = ongoingCallController;
         mAnimationScheduler = animationScheduler;
@@ -140,6 +144,7 @@
         mNetworkController = networkController;
         mStatusBarStateController = statusBarStateController;
         mCommandQueue = commandQueue;
+        mOperatorNameViewControllerFactory = operatorNameViewControllerFactory;
     }
 
     @Override
@@ -411,14 +416,14 @@
     }
 
     public void hideOperatorName(boolean animate) {
-        if (mOperatorNameFrame != null) {
-            animateHide(mOperatorNameFrame, animate);
+        if (mOperatorNameViewController != null) {
+            animateHide(mOperatorNameViewController.getView(), animate);
         }
     }
 
     public void showOperatorName(boolean animate) {
-        if (mOperatorNameFrame != null) {
-            animateShow(mOperatorNameFrame, animate);
+        if (mOperatorNameViewController != null) {
+            animateShow(mOperatorNameViewController.getView(), animate);
         }
     }
 
@@ -495,7 +500,9 @@
     private void initOperatorName() {
         if (getResources().getBoolean(R.bool.config_showOperatorNameInStatusBar)) {
             ViewStub stub = mStatusBar.findViewById(R.id.operator_name);
-            mOperatorNameFrame = stub.inflate();
+            mOperatorNameViewController =
+                    mOperatorNameViewControllerFactory.create((OperatorNameView) stub.inflate());
+            mOperatorNameViewController.init();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
index 07618da..12ae3f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
@@ -17,6 +17,7 @@
 import android.content.Context
 import android.content.pm.ActivityInfo
 import android.content.res.Configuration
+import android.graphics.Rect
 import android.os.LocaleList
 import android.view.View.LAYOUT_DIRECTION_RTL
 import com.android.systemui.statusbar.policy.ConfigurationController
@@ -29,6 +30,7 @@
     private val lastConfig = Configuration()
     private var density: Int = 0
     private var smallestScreenWidth: Int = 0
+    private var maxBounds: Rect? = null
     private var fontScale: Float = 0.toFloat()
     private val inCarMode: Boolean
     private var uiMode: Int = 0
@@ -85,6 +87,14 @@
             }
         }
 
+        val maxBounds = newConfig.windowConfiguration.maxBounds
+        if (maxBounds != this.maxBounds) {
+            this.maxBounds = maxBounds
+            listeners.filterForEach({ this.listeners.contains(it) }) {
+                it.onMaxBoundsChanged()
+            }
+        }
+
         val localeList = newConfig.locales
         if (localeList != this.localeList) {
             this.localeList = localeList
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
index b4f8126..908cd34 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -29,9 +29,9 @@
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.R;
 import com.android.systemui.demomode.DemoMode;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.StatusBarMobileView;
 import com.android.systemui.statusbar.StatusBarWifiView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 6802472..84b8f52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -33,7 +33,7 @@
 import com.android.systemui.doze.AlwaysOnDisplayPolicy;
 import com.android.systemui.doze.DozeScreenState;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.tuner.TunerService;
 
@@ -231,7 +231,9 @@
      * possible if AOD isn't even enabled or if the flag is disabled.
      */
     public boolean canControlUnlockedScreenOff() {
-        return getAlwaysOn() && mFeatureFlags.useNewLockscreenAnimations();
+        return getAlwaysOn()
+                && mFeatureFlags.useNewLockscreenAnimations()
+                && !getDisplayNeedsBlanking();
     }
 
     private boolean getBoolean(String propName, int resId) {
@@ -266,6 +268,13 @@
     }
 
     /**
+     * Whether the brightness sensor uses the proximity sensor.
+     */
+    public boolean brightnessUsesProx() {
+        return mResources.getBoolean(R.bool.doze_brightness_uses_prox);
+    }
+
+    /**
      * Callback to listen for DozeParameter changes.
      */
     public void addCallback(Callback callback) {
@@ -301,6 +310,7 @@
         pw.print("getPickupVibrationThreshold(): "); pw.println(getPickupVibrationThreshold());
         pw.print("getSelectivelyRegisterSensorsUsingProx(): ");
         pw.println(getSelectivelyRegisterSensorsUsingProx());
+        pw.print("brightnessUsesProx(): "); pw.println(brightnessUsesProx());
     }
 
     interface Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index b2cf72a..21c3e5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -116,11 +116,18 @@
 
         if (!mDozing || mPulseCallback != null) {
             if (DEBUG) {
-                Log.d(TAG, "Pulse supressed. Dozing: " + mDozeParameters + " had callback? "
+                Log.d(TAG, "Pulse suppressed. Dozing: " + mDozeParameters + " had callback? "
                         + (mPulseCallback != null));
             }
             // Pulse suppressed.
             callback.onPulseFinished();
+            if (!mDozing) {
+                mDozeLog.tracePulseDropped("device isn't dozing");
+            } else {
+                mDozeLog.tracePulseDropped("already has pulse callback mPulseCallback="
+                        + mPulseCallback);
+            }
+
             return;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index f4830fb..878fbbf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -18,9 +18,7 @@
 
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.view.DisplayCutout;
 import android.view.View;
-import android.view.WindowInsets;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.widget.ViewClippingUtil;
@@ -61,7 +59,6 @@
     private final NotificationPanelViewController mNotificationPanelViewController;
     private final Consumer<ExpandableNotificationRow>
             mSetTrackingHeadsUp = this::setTrackingHeadsUp;
-    private final Runnable mUpdatePanelTranslation = this::updatePanelTranslation;
     private final BiConsumer<Float, Float> mSetExpandedHeight = this::setAppearFraction;
     private final KeyguardBypassController mBypassController;
     private final StatusBarStateController mStatusBarStateController;
@@ -75,9 +72,6 @@
     float mAppearFraction;
     private ExpandableNotificationRow mTrackedChild;
     private boolean mShown;
-    private final View.OnLayoutChangeListener mStackScrollLayoutChangeListener =
-            (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom)
-                    -> updatePanelTranslation();
     private final ViewClippingUtil.ClippingParameters mParentClippingParams =
             new ViewClippingUtil.ClippingParameters() {
                 @Override
@@ -134,10 +128,8 @@
         mStackScrollerController = stackScrollerController;
         mNotificationPanelViewController = notificationPanelViewController;
         notificationPanelViewController.addTrackingHeadsUpListener(mSetTrackingHeadsUp);
-        notificationPanelViewController.setVerticalTranslationListener(mUpdatePanelTranslation);
         notificationPanelViewController.setHeadsUpAppearanceController(this);
         mStackScrollerController.addOnExpandedHeightChangedListener(mSetExpandedHeight);
-        mStackScrollerController.addOnLayoutChangeListener(mStackScrollLayoutChangeListener);
         mStackScrollerController.setHeadsUpAppearanceController(this);
         mClockView = clockView;
         mOperatorNameView = operatorNameView;
@@ -174,7 +166,6 @@
         mNotificationPanelViewController.setVerticalTranslationListener(null);
         mNotificationPanelViewController.setHeadsUpAppearanceController(null);
         mStackScrollerController.removeOnExpandedHeightChangedListener(mSetExpandedHeight);
-        mStackScrollerController.removeOnLayoutChangeListener(mStackScrollLayoutChangeListener);
         mDarkIconDispatcher.removeDarkReceiver(this);
     }
 
@@ -189,63 +180,6 @@
         updateHeader(entry);
     }
 
-    /** To count the distance from the window right boundary to scroller right boundary. The
-     * distance formula is the following:
-     *     Y = screenSize - (SystemWindow's width + Scroller.getRight())
-     * There are four modes MUST to be considered in Cut Out of RTL.
-     * No Cut Out:
-     *   Scroller + NB
-     *   NB + Scroller
-     *     => SystemWindow = NavigationBar's width
-     *     => Y = screenSize - (SystemWindow's width + Scroller.getRight())
-     * Corner Cut Out or Tall Cut Out:
-     *   cut out + Scroller + NB
-     *   NB + Scroller + cut out
-     *     => SystemWindow = NavigationBar's width
-     *     => Y = screenSize - (SystemWindow's width + Scroller.getRight())
-     * Double Cut Out:
-     *   cut out left + Scroller + (NB + cut out right)
-     *     SystemWindow = NavigationBar's width + cut out right width
-     *     => Y = screenSize - (SystemWindow's width + Scroller.getRight())
-     *   (cut out left + NB) + Scroller + cut out right
-     *     SystemWindow = NavigationBar's width + cut out left width
-     *     => Y = screenSize - (SystemWindow's width + Scroller.getRight())
-     * @return the translation X value for RTL. In theory, it should be negative. i.e. -Y
-     */
-    private int getRtlTranslation() {
-        if (mPoint == null) {
-            mPoint = new Point();
-        }
-
-        int realDisplaySize = 0;
-        if (mStackScrollerController.getDisplay() != null) {
-            mStackScrollerController.getDisplay().getRealSize(mPoint);
-            realDisplaySize = mPoint.x;
-        }
-
-        WindowInsets windowInset = mStackScrollerController.getRootWindowInsets();
-        DisplayCutout cutout = (windowInset != null) ? windowInset.getDisplayCutout() : null;
-        int sysWinLeft = (windowInset != null) ? windowInset.getStableInsetLeft() : 0;
-        int sysWinRight = (windowInset != null) ? windowInset.getStableInsetRight() : 0;
-        int cutoutLeft = (cutout != null) ? cutout.getSafeInsetLeft() : 0;
-        int cutoutRight = (cutout != null) ? cutout.getSafeInsetRight() : 0;
-        int leftInset = Math.max(sysWinLeft, cutoutLeft);
-        int rightInset = Math.max(sysWinRight, cutoutRight);
-
-        return leftInset + mStackScrollerController.getRight() + rightInset - realDisplaySize;
-    }
-
-    public void updatePanelTranslation() {
-        float newTranslation;
-        if (mStackScrollerController.isLayoutRtl()) {
-            newTranslation = getRtlTranslation();
-        } else {
-            newTranslation = mStackScrollerController.getLeft();
-        }
-        newTranslation += mStackScrollerController.getTranslationX();
-        mHeadsUpStatusBarView.setPanelTranslation(newTranslation);
-    }
-
     private void updateTopEntry() {
         NotificationEntry newEntry = null;
         if (shouldBeVisible()) {
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 0a4e59c..c639eec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -80,6 +80,7 @@
 import com.android.systemui.ActivityIntentHelper;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.animation.Interpolators;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.camera.CameraIntents;
@@ -203,13 +204,15 @@
     private ControlsListingController.ControlsListingCallback mListingCallback =
             new ControlsListingController.ControlsListingCallback() {
                 public void onServicesUpdated(List<ControlsServiceInfo> serviceInfos) {
-                    boolean available = !serviceInfos.isEmpty();
+                    post(() -> {
+                        boolean available = !serviceInfos.isEmpty();
 
-                    if (available != mControlServicesAvailable) {
-                        mControlServicesAvailable = available;
-                        updateControlsVisibility();
-                        updateAffordanceColors();
-                    }
+                        if (available != mControlServicesAvailable) {
+                            mControlServicesAvailable = available;
+                            updateControlsVisibility();
+                            updateAffordanceColors();
+                        }
+                    });
                 }
             };
 
@@ -1044,11 +1047,13 @@
             return;
         }
 
+        ActivityLaunchAnimator.Controller animationController = createLaunchAnimationController(v);
         if (mHasCard) {
             Intent intent = new Intent(mContext, WalletActivity.class)
                     .setAction(Intent.ACTION_VIEW)
                     .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
-            mContext.startActivity(intent);
+            mActivityStarter.startActivity(intent, true /* dismissShade */, animationController,
+                    true /* showOverLockscreenWhenLocked */);
         } else {
             if (mQuickAccessWalletController.getWalletClient().createWalletIntent() == null) {
                 Log.w(TAG, "Could not get intent of the wallet app.");
@@ -1056,10 +1061,14 @@
             }
             mActivityStarter.postStartActivityDismissingKeyguard(
                     mQuickAccessWalletController.getWalletClient().createWalletIntent(),
-                    /* delay= */ 0);
+                    /* delay= */ 0, animationController);
         }
     }
 
+    protected ActivityLaunchAnimator.Controller createLaunchAnimationController(View view) {
+        return ActivityLaunchAnimator.Controller.fromView(view, null);
+    }
+
     private void onControlsClick(View v) {
         if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index a326437..3d5f755 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -232,6 +232,7 @@
         pw.println("  launchingAffordance: $launchingAffordance")
         pw.println("  qSExpanded: $qSExpanded")
         pw.println("  hasFaceFeature: $hasFaceFeature")
+        pw.println("  userHasDeviceEntryIntent: $userHasDeviceEntryIntent")
     }
 
     /** Registers a listener for bypass state changes. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index f77c052..d348954 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -32,12 +32,6 @@
  * Utility class to calculate the clock position and top padding of notifications on Keyguard.
  */
 public class KeyguardClockPositionAlgorithm {
-    /**
-     * How much the clock height influences the shade position.
-     * 0 means nothing, 1 means move the shade up by the height of the clock
-     * 0.5f means move the shade up by half of the size of the clock.
-     */
-    private static float CLOCK_HEIGHT_WEIGHT = 0.7f;
 
     /**
      * Margin between the bottom of the status view and the notification shade.
@@ -45,11 +39,6 @@
     private int mStatusViewBottomMargin;
 
     /**
-     * Height of the parent view - display size in px.
-     */
-    private int mHeight;
-
-    /**
      * Height of {@link KeyguardStatusView}.
      */
     private int mKeyguardStatusHeight;
@@ -68,21 +57,6 @@
     private int mUserSwitchPreferredY;
 
     /**
-     * Whether or not there is a custom clock face on keyguard.
-     */
-    private boolean mHasCustomClock;
-
-    /**
-     * Whether or not the NSSL contains any visible notifications.
-     */
-    private boolean mHasVisibleNotifs;
-
-    /**
-     * Height of notification stack: Sum of height of each notification.
-     */
-    private int mNotificationStackHeight;
-
-    /**
      * Minimum top margin to avoid overlap with status bar, lock icon, or multi-user switcher
      * avatar.
      */
@@ -94,12 +68,6 @@
     private int mCutoutTopInset = 0;
 
     /**
-     * Maximum bottom padding to avoid overlap with {@link KeyguardBottomAreaView} or
-     * the ambient indication.
-     */
-    private int mMaxShadeBottom;
-
-    /**
      * Recommended distance from the status bar.
      */
     private int mContainerTopPadding;
@@ -115,14 +83,9 @@
     private int mBurnInPreventionOffsetX;
 
     /**
-     * Burn-in prevention y translation.
+     * Burn-in prevention y translation for clock layouts.
      */
-    private int mBurnInPreventionOffsetY;
-
-    /**
-     * Burn-in prevention y translation for large clock layouts.
-     */
-    private int mBurnInPreventionOffsetYLargeClock;
+    private int mBurnInPreventionOffsetYClock;
 
     /**
      * Doze/AOD transition amount.
@@ -160,34 +123,26 @@
                 res.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) / 2;
         mBurnInPreventionOffsetX = res.getDimensionPixelSize(
                 R.dimen.burn_in_prevention_offset_x);
-        mBurnInPreventionOffsetY = res.getDimensionPixelSize(
-                R.dimen.burn_in_prevention_offset_y);
-        mBurnInPreventionOffsetYLargeClock = res.getDimensionPixelSize(
-                R.dimen.burn_in_prevention_offset_y_large_clock);
+        mBurnInPreventionOffsetYClock = res.getDimensionPixelSize(
+                R.dimen.burn_in_prevention_offset_y_clock);
     }
 
     /**
      * Sets up algorithm values.
      */
-    public void setup(int keyguardStatusBarHeaderHeight, int maxShadeBottom,
-            int notificationStackHeight, float panelExpansion, int parentHeight,
+    public void setup(int keyguardStatusBarHeaderHeight, float panelExpansion,
             int keyguardStatusHeight, int userSwitchHeight, int userSwitchPreferredY,
-            boolean hasCustomClock, boolean hasVisibleNotifs, float dark,
-            float overStrechAmount, boolean bypassEnabled, int unlockedStackScrollerPadding,
-            float qsExpansion, int cutoutTopInset, boolean isSplitShade) {
+            float dark, float overStretchAmount, boolean bypassEnabled,
+            int unlockedStackScrollerPadding, float qsExpansion, int cutoutTopInset,
+            boolean isSplitShade) {
         mMinTopMargin = keyguardStatusBarHeaderHeight + Math.max(mContainerTopPadding,
                 userSwitchHeight);
-        mMaxShadeBottom = maxShadeBottom;
-        mNotificationStackHeight = notificationStackHeight;
         mPanelExpansion = panelExpansion;
-        mHeight = parentHeight;
         mKeyguardStatusHeight = keyguardStatusHeight + mStatusViewBottomMargin;
         mUserSwitchHeight = userSwitchHeight;
         mUserSwitchPreferredY = userSwitchPreferredY;
-        mHasCustomClock = hasCustomClock;
-        mHasVisibleNotifs = hasVisibleNotifs;
         mDarkAmount = dark;
-        mOverStretchAmount = overStrechAmount;
+        mOverStretchAmount = overStretchAmount;
         mBypassEnabled = bypassEnabled;
         mUnlockedStackScrollerPadding = unlockedStackScrollerPadding;
         mQsExpansion = qsExpansion;
@@ -244,8 +199,8 @@
 
         // This will keep the clock at the top but out of the cutout area
         float shift = 0;
-        if (clockY - mBurnInPreventionOffsetYLargeClock < mCutoutTopInset) {
-            shift = mCutoutTopInset - (clockY - mBurnInPreventionOffsetYLargeClock);
+        if (clockY - mBurnInPreventionOffsetYClock < mCutoutTopInset) {
+            shift = mCutoutTopInset - (clockY - mBurnInPreventionOffsetYClock);
         }
         float clockYDark = clockY + burnInPreventionOffsetY() + shift;
 
@@ -281,7 +236,7 @@
     }
 
     private float burnInPreventionOffsetY() {
-        int offset = mBurnInPreventionOffsetYLargeClock;
+        int offset = mBurnInPreventionOffsetYClock;
 
         return getBurnInOffset(offset * 2, false /* xAxis */) - offset;
     }
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 222ed63..df4bbcf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -42,9 +42,9 @@
 import android.widget.TextView;
 
 import com.android.settingslib.Utils;
-import com.android.systemui.BatteryMeterView;
 import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
+import com.android.systemui.battery.BatteryMeterView;
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 
 import java.io.FileDescriptor;
@@ -321,7 +321,7 @@
         }
     }
 
-    public void setKeyguardUserSwitcherEnabled(boolean enabled) {
+    void setKeyguardUserSwitcherEnabled(boolean enabled) {
         mKeyguardUserSwitcherEnabled = enabled;
     }
 
@@ -475,8 +475,10 @@
 
     /**
      * Set the clipping on the top of the view.
+     *
+     * Should only be called from {@link KeyguardStatusBarViewController}.
      */
-    public void setTopClipping(int topClipping) {
+    void setTopClipping(int topClipping) {
         if (topClipping != mTopClipping) {
             mTopClipping = topClipping;
             updateClipping();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index 5d8d36e..af9d921 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -16,20 +16,34 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
 import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_IN;
 import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_OUT;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.content.res.Resources;
+import android.hardware.biometrics.BiometricSourceType;
+import android.view.View;
 
 import androidx.annotation.NonNull;
 
 import com.android.keyguard.CarrierTextController;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.R;
+import com.android.systemui.animation.Interpolators;
+import com.android.systemui.battery.BatteryMeterViewController;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.UserInfoController;
 import com.android.systemui.util.ViewController;
 
@@ -50,6 +64,13 @@
     private final UserInfoController mUserInfoController;
     private final StatusBarIconController mStatusBarIconController;
     private final StatusBarIconController.TintedIconManager.Factory mTintedIconManagerFactory;
+    private final BatteryMeterViewController mBatteryMeterViewController;
+    private final ViewStateProvider mViewStateProvider;
+    private final KeyguardStateController mKeyguardStateController;
+    private final KeyguardBypassController mKeyguardBypassController;
+    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private final BiometricUnlockController mBiometricUnlockController;
+    private final SysuiStatusBarStateController mStatusBarStateController;
 
     private final ConfigurationController.ConfigurationListener mConfigurationListener =
             new ConfigurationController.ConfigurationListener() {
@@ -101,11 +122,78 @@
     private final UserInfoController.OnUserInfoChangedListener mOnUserInfoChangedListener =
             (name, picture, userAccount) -> mView.onUserInfoChanged(picture);
 
+    private final ValueAnimator.AnimatorUpdateListener mAnimatorUpdateListener =
+            animation -> {
+                mKeyguardStatusBarAnimateAlpha = (float) animation.getAnimatedValue();
+                updateViewState();
+            };
+
+    private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
+            new KeyguardUpdateMonitorCallback() {
+                @Override
+                public void onBiometricAuthenticated(
+                        int userId,
+                        BiometricSourceType biometricSourceType,
+                        boolean isStrongBiometric) {
+                    if (mFirstBypassAttempt
+                            && mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
+                                    isStrongBiometric)) {
+                        mDelayShowingKeyguardStatusBar = true;
+                    }
+                }
+
+                @Override
+                public void onBiometricRunningStateChanged(
+                        boolean running,
+                        BiometricSourceType biometricSourceType) {
+                    boolean keyguardOrShadeLocked =
+                            mStatusBarState == KEYGUARD
+                                    || mStatusBarState == StatusBarState.SHADE_LOCKED;
+                    if (!running
+                            && mFirstBypassAttempt
+                            && keyguardOrShadeLocked
+                            && !mDozing
+                            && !mDelayShowingKeyguardStatusBar
+                            && !mBiometricUnlockController.isBiometricUnlock()) {
+                        mFirstBypassAttempt = false;
+                        animateKeyguardStatusBarIn();
+                    }
+                }
+
+                @Override
+                public void onFinishedGoingToSleep(int why) {
+                    mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
+                    mDelayShowingKeyguardStatusBar = false;
+                }
+            };
+
+    private final StatusBarStateController.StateListener mStatusBarStateListener =
+            new StatusBarStateController.StateListener() {
+                @Override
+                public void onStateChanged(int newState) {
+                    mStatusBarState = newState;
+                }
+            };
+
     private final List<String> mBlockedIcons;
 
     private boolean mBatteryListening;
     private StatusBarIconController.TintedIconManager mTintedIconManager;
 
+    private float mKeyguardStatusBarAnimateAlpha = 1f;
+    /**
+     * If face auth with bypass is running for the first time after you turn on the screen.
+     * (From aod or screen off)
+     */
+    private boolean mFirstBypassAttempt;
+    /**
+     * If auth happens successfully during {@code mFirstBypassAttempt}, and we should wait until
+     * the keyguard is dismissed to show the status bar.
+     */
+    private boolean mDelayShowingKeyguardStatusBar;
+    private int mStatusBarState;
+    private boolean mDozing;
+
     @Inject
     public KeyguardStatusBarViewController(
             KeyguardStatusBarView view,
@@ -115,7 +203,14 @@
             BatteryController batteryController,
             UserInfoController userInfoController,
             StatusBarIconController statusBarIconController,
-            StatusBarIconController.TintedIconManager.Factory tintedIconManagerFactory) {
+            StatusBarIconController.TintedIconManager.Factory tintedIconManagerFactory,
+            BatteryMeterViewController batteryMeterViewController,
+            ViewStateProvider viewStateProvider,
+            KeyguardStateController keyguardStateController,
+            KeyguardBypassController bypassController,
+            KeyguardUpdateMonitor keyguardUpdateMonitor,
+            BiometricUnlockController biometricUnlockController,
+            SysuiStatusBarStateController statusBarStateController) {
         super(view);
         mCarrierTextController = carrierTextController;
         mConfigurationController = configurationController;
@@ -124,6 +219,26 @@
         mUserInfoController = userInfoController;
         mStatusBarIconController = statusBarIconController;
         mTintedIconManagerFactory = tintedIconManagerFactory;
+        mBatteryMeterViewController = batteryMeterViewController;
+        mViewStateProvider = viewStateProvider;
+        mKeyguardStateController = keyguardStateController;
+        mKeyguardBypassController = bypassController;
+        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        mBiometricUnlockController = biometricUnlockController;
+        mStatusBarStateController = statusBarStateController;
+
+        mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
+        mKeyguardStateController.addCallback(
+                new KeyguardStateController.Callback() {
+                    @Override
+                    public void onKeyguardFadingAwayChanged() {
+                        if (!mKeyguardStateController.isKeyguardFadingAway()) {
+                            mFirstBypassAttempt = false;
+                            mDelayShowingKeyguardStatusBar = false;
+                        }
+                    }
+                }
+        );
 
         Resources r = getResources();
         mBlockedIcons = Collections.unmodifiableList(Arrays.asList(
@@ -136,6 +251,7 @@
     protected void onInit() {
         super.onInit();
         mCarrierTextController.init();
+        mBatteryMeterViewController.init();
     }
 
     @Override
@@ -143,6 +259,8 @@
         mConfigurationController.addCallback(mConfigurationListener);
         mAnimationScheduler.addCallback(mAnimationCallback);
         mUserInfoController.addCallback(mOnUserInfoChangedListener);
+        mStatusBarStateController.addCallback(mStatusBarStateListener);
+        mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
         if (mTintedIconManager == null) {
             mTintedIconManager =
                     mTintedIconManagerFactory.create(mView.findViewById(R.id.statusIcons));
@@ -154,14 +272,11 @@
 
     @Override
     protected void onViewDetached() {
-        // Don't receive future #onViewAttached calls so that we don't accidentally have two
-        // controllers registered for the same view.
-        // TODO(b/194181195): This shouldn't be necessary.
-        destroy();
-
         mConfigurationController.removeCallback(mConfigurationListener);
         mAnimationScheduler.removeCallback(mAnimationCallback);
         mUserInfoController.removeCallback(mOnUserInfoChangedListener);
+        mStatusBarStateController.removeCallback(mStatusBarStateListener);
+        mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
         if (mTintedIconManager != null) {
             mStatusBarIconController.removeIconGroup(mTintedIconManager);
         }
@@ -172,6 +287,11 @@
         mView.onThemeChanged(mTintedIconManager);
     }
 
+    /** Sets whether user switcher is enabled. */
+    public void setKeyguardUserSwitcherEnabled(boolean enabled) {
+        mView.setKeyguardUserSwitcherEnabled(enabled);
+    }
+
     /** Sets whether this controller should listen to battery updates. */
     public void setBatteryListening(boolean listening) {
         if (listening == mBatteryListening) {
@@ -185,10 +305,107 @@
         }
     }
 
+    /** Set the view to have no top clipping. */
+    public void setNoTopClipping() {
+        mView.setTopClipping(0);
+    }
+
+    /**
+     * Update the view's top clipping based on the value of notificationPanelTop and the view's
+     * current top.
+     *
+     * @param notificationPanelTop the current top of the notification panel view.
+     */
+    public void updateTopClipping(int notificationPanelTop) {
+        mView.setTopClipping(notificationPanelTop - mView.getTop());
+    }
+
+    /** Sets the dozing state. */
+    public void setDozing(boolean dozing) {
+        mDozing = dozing;
+    }
+
+    /** Animate the keyguard status bar in. */
+    public void animateKeyguardStatusBarIn() {
+        mView.setVisibility(View.VISIBLE);
+        mView.setAlpha(0f);
+        ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
+        anim.addUpdateListener(mAnimatorUpdateListener);
+        anim.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+        anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+        anim.start();
+    }
+
+    /** Animate the keyguard status bar out. */
+    public void animateKeyguardStatusBarOut(long startDelay, long duration) {
+        ValueAnimator anim = ValueAnimator.ofFloat(mView.getAlpha(), 0f);
+        anim.addUpdateListener(mAnimatorUpdateListener);
+        anim.setStartDelay(startDelay);
+        anim.setDuration(duration);
+        anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+        anim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mView.setVisibility(View.INVISIBLE);
+                mView.setAlpha(1f);
+                mKeyguardStatusBarAnimateAlpha = 1f;
+            }
+        });
+        anim.start();
+    }
+
+    /**
+     * Updates the {@link KeyguardStatusBarView} state based on what the {@link ViewStateProvider}
+     * and other controllers provide.
+     */
+    public void updateViewState() {
+        ViewState newViewState = mViewStateProvider.provideViewState();
+        if (!isKeyguardShowing()) {
+            return;
+        }
+
+        float newAlpha = newViewState.mAlpha * mKeyguardStatusBarAnimateAlpha;
+
+        boolean hideForBypass =
+                mFirstBypassAttempt && mKeyguardUpdateMonitor.shouldListenForFace()
+                        || mDelayShowingKeyguardStatusBar;
+        int newVisibility = newAlpha != 0f && !mDozing && !hideForBypass
+                ? View.VISIBLE : View.INVISIBLE;
+
+        updateViewState(newAlpha, newVisibility);
+    }
+
+    /**
+     * Updates the {@link KeyguardStatusBarView} state based on the provided values.
+     */
+    public void updateViewState(float alpha, int visibility) {
+        mView.setAlpha(alpha);
+        mView.setVisibility(visibility);
+    }
+
+    private boolean isKeyguardShowing() {
+        return mStatusBarState == KEYGUARD;
+    }
+
     /** */
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("KeyguardStatusBarView:");
         pw.println("  mBatteryListening: " + mBatteryListening);
         mView.dump(fd, pw, args);
     }
+
+    /** An interface that provides the desired state of {@link KeyguardStatusBarView}. */
+    public interface ViewStateProvider {
+        /** Provides the state. */
+        ViewState provideViewState();
+    }
+
+    /** A POJO for the desired state of {@link KeyguardStatusBarView}. */
+    static class ViewState {
+        final float mAlpha;
+
+        ViewState(float alpha) {
+            this.mAlpha = alpha;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java
index 094ebb9..5f222af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java
@@ -24,7 +24,6 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.UiEventLoggerImpl;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Dependency;
 import com.android.systemui.EventLogConstants;
 import com.android.systemui.EventLogTags;
 import com.android.systemui.dagger.SysUISingleton;
@@ -88,10 +87,11 @@
     }
 
     private ArrayMap<Integer, Integer> mLegacyMap;
-    private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
+    private final MetricsLogger mMetricsLogger;
 
     @Inject
-    public LockscreenGestureLogger() {
+    public LockscreenGestureLogger(MetricsLogger metricsLogger) {
+        mMetricsLogger = metricsLogger;
         mLegacyMap = new ArrayMap<>(EventLogConstants.METRICS_GESTURE_TYPE_MAP.length);
         for (int i = 0; i < EventLogConstants.METRICS_GESTURE_TYPE_MAP.length ; i++) {
             mLegacyMap.put(EventLogConstants.METRICS_GESTURE_TYPE_MAP[i], i);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index b8995f0..e31871a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -55,7 +55,6 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.drawable.Drawable;
-import android.hardware.biometrics.BiometricSourceType;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.PowerManager;
@@ -91,7 +90,6 @@
 import com.android.keyguard.KeyguardStatusView;
 import com.android.keyguard.KeyguardStatusViewController;
 import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.keyguard.LockIconViewController;
 import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent;
 import com.android.keyguard.dagger.KeyguardStatusBarViewComponent;
@@ -232,7 +230,6 @@
 
     @VisibleForTesting final StatusBarStateListener mStatusBarStateListener =
             new StatusBarStateListener();
-    private final BiometricUnlockController mBiometricUnlockController;
     private final NotificationPanelView mView;
     private final VibratorHelper mVibratorHelper;
     private final MetricsLogger mMetricsLogger;
@@ -270,42 +267,6 @@
     private static final AnimationProperties
             KEYGUARD_HUN_PROPERTIES =
             new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-    @VisibleForTesting
-    final KeyguardUpdateMonitorCallback
-            mKeyguardUpdateCallback =
-            new KeyguardUpdateMonitorCallback() {
-
-                @Override
-                public void onBiometricAuthenticated(int userId,
-                        BiometricSourceType biometricSourceType,
-                        boolean isStrongBiometric) {
-                    if (mFirstBypassAttempt
-                            && mUpdateMonitor.isUnlockingWithBiometricAllowed(isStrongBiometric)) {
-                        mDelayShowingKeyguardStatusBar = true;
-                    }
-                }
-
-                @Override
-                public void onBiometricRunningStateChanged(boolean running,
-                        BiometricSourceType biometricSourceType) {
-                    boolean
-                            keyguardOrShadeLocked =
-                            mBarState == KEYGUARD
-                                    || mBarState == StatusBarState.SHADE_LOCKED;
-                    if (!running && mFirstBypassAttempt && keyguardOrShadeLocked && !mDozing
-                            && !mDelayShowingKeyguardStatusBar
-                            && !mBiometricUnlockController.isBiometricUnlock()) {
-                        mFirstBypassAttempt = false;
-                        animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-                    }
-                }
-
-                @Override
-                public void onFinishedGoingToSleep(int why) {
-                    mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
-                    mDelayShowingKeyguardStatusBar = false;
-                }
-    };
 
     private final LayoutInflater mLayoutInflater;
     private final PowerManager mPowerManager;
@@ -338,6 +299,8 @@
     private final SplitShadeHeaderController mSplitShadeHeaderController;
     private final RecordingController mRecordingController;
     private boolean mShouldUseSplitNotificationShade;
+    // The bottom padding reserved for elements of the keyguard measuring notifications
+    private float mKeyguardNotificationBottomPadding;
     // Current max allowed keyguard notifications determined by measuring the panel
     private int mMaxAllowedKeyguardNotifications;
 
@@ -430,7 +393,6 @@
     private boolean mQsTouchAboveFalsingThreshold;
     private int mQsFalsingThreshold;
 
-    private float mKeyguardStatusBarAnimateAlpha = 1f;
     private HeadsUpTouchHelper mHeadsUpTouchHelper;
     private boolean mListenForHeadsUp;
     private int mNavigationBarBottomHeight;
@@ -476,7 +438,6 @@
     private float mLinearDarkAmount;
 
     private boolean mPulsing;
-    private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
     private boolean mUserSetupComplete;
     private int mQsNotificationTopPadding;
     private boolean mHideIconsDuringLaunchAnimation = true;
@@ -587,17 +548,6 @@
      */
     private boolean mIsPanelCollapseOnQQS;
 
-    /**
-     * If face auth with bypass is running for the first time after you turn on the screen.
-     * (From aod or screen off)
-     */
-    private boolean mFirstBypassAttempt;
-    /**
-     * If auth happens successfully during {@code mFirstBypassAttempt}, and we should wait until
-     * the keyguard is dismissed to show the status bar.
-     */
-    private boolean mDelayShowingKeyguardStatusBar;
-
     private boolean mAnimatingQS;
 
     /**
@@ -637,6 +587,9 @@
     private int mScreenCornerRadius;
     private boolean mQSAnimatingHiddenFromCollapsed;
 
+    private int mQsClipTop;
+    private int mQsClipBottom;
+    private boolean mQsVisible;
     private final ContentResolver mContentResolver;
 
     private final Executor mUiExecutor;
@@ -644,7 +597,7 @@
 
     private KeyguardMediaController mKeyguardMediaController;
 
-    private boolean mStatusViewCentered = false;
+    private boolean mStatusViewCentered = true;
 
     private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() {
         @Override
@@ -679,6 +632,17 @@
         }
     };
 
+    private final KeyguardStatusBarViewController.ViewStateProvider mViewStateProvider =
+            new KeyguardStatusBarViewController.ViewStateProvider() {
+                @Override
+                public KeyguardStatusBarViewController.ViewState provideViewState() {
+                    float alphaQsExpansion = 1 - Math.min(1, computeQsExpansionFraction() * 2);
+                    float newAlpha = Math.min(getKeyguardContentsAlpha(), alphaQsExpansion)
+                            * (1.0f - mKeyguardHeadsUpShowingAmount);
+                    return new KeyguardStatusBarViewController.ViewState(newAlpha);
+                }
+            };
+
     @Inject
     public NotificationPanelViewController(NotificationPanelView view,
             @Main Resources resources,
@@ -702,7 +666,6 @@
             StatusBarTouchableRegionManager statusBarTouchableRegionManager,
             ConversationNotificationManager conversationNotificationManager,
             MediaHierarchyManager mediaHierarchyManager,
-            BiometricUnlockController biometricUnlockController,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             NotificationStackScrollLayoutController notificationStackScrollLayoutController,
             KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory,
@@ -732,12 +695,21 @@
             SecureSettings secureSettings,
             SplitShadeHeaderController splitShadeHeaderController,
             UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
+            LockscreenGestureLogger lockscreenGestureLogger,
             NotificationRemoteInputManager remoteInputManager,
             ControlsComponent controlsComponent) {
-        super(view, falsingManager, dozeLog, keyguardStateController,
-                (SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
-                statusBarKeyguardViewManager, latencyTracker, flingAnimationUtilsBuilder.get(),
-                statusBarTouchableRegionManager, ambientState);
+        super(view,
+                falsingManager,
+                dozeLog,
+                keyguardStateController,
+                (SysuiStatusBarStateController) statusBarStateController,
+                vibratorHelper,
+                statusBarKeyguardViewManager,
+                latencyTracker,
+                flingAnimationUtilsBuilder.get(),
+                statusBarTouchableRegionManager,
+                lockscreenGestureLogger,
+                ambientState);
         mView = view;
         mVibratorHelper = vibratorHelper;
         mKeyguardMediaController = keyguardMediaController;
@@ -779,7 +751,6 @@
         mDisplayId = displayId;
         mPulseExpansionHandler = pulseExpansionHandler;
         mDozeParameters = dozeParameters;
-        mBiometricUnlockController = biometricUnlockController;
         mScrimController = scrimController;
         mScrimController.setClipsQsScrim(!mShouldUseSplitNotificationShade);
         mUserManager = userManager;
@@ -795,21 +766,8 @@
         mThemeResId = mView.getContext().getThemeResId();
         mKeyguardBypassController = bypassController;
         mUpdateMonitor = keyguardUpdateMonitor;
-        mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
-        KeyguardStateController.Callback
-                keyguardMonitorCallback =
-                new KeyguardStateController.Callback() {
-                    @Override
-                    public void onKeyguardFadingAwayChanged() {
-                        if (!mKeyguardStateController.isKeyguardFadingAway()) {
-                            mFirstBypassAttempt = false;
-                            mDelayShowingKeyguardStatusBar = false;
-                        }
-                    }
-                };
         mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
         lockscreenShadeTransitionController.setNotificationPanelController(this);
-        mKeyguardStateController.addCallback(keyguardMonitorCallback);
         DynamicPrivacyControlListener
                 dynamicPrivacyControlListener =
                 new DynamicPrivacyControlListener();
@@ -870,10 +828,16 @@
             }
         }
 
+        mKeyguardStatusBarViewController =
+                mKeyguardStatusBarViewComponentFactory.build(
+                        mKeyguardStatusBar,
+                        mViewStateProvider)
+                        .getKeyguardStatusBarViewController();
+        mKeyguardStatusBarViewController.init();
+
         updateViewControllers(
                 mView.findViewById(R.id.keyguard_status_view),
                 userAvatarView,
-                mKeyguardStatusBar,
                 keyguardUserSwitcherView);
         mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent);
         NotificationStackScrollLayout stackScrollLayout = mView.findViewById(
@@ -965,7 +929,6 @@
 
     private void updateViewControllers(KeyguardStatusView keyguardStatusView,
             UserAvatarView userAvatarView,
-            KeyguardStatusBarView keyguardStatusBarView,
             KeyguardUserSwitcherView keyguardUserSwitcherView) {
         // Re-associate the KeyguardStatusViewController
         KeyguardStatusViewComponent statusViewComponent =
@@ -973,16 +936,6 @@
         mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController();
         mKeyguardStatusViewController.init();
 
-        KeyguardStatusBarViewComponent statusBarViewComponent =
-                mKeyguardStatusBarViewComponentFactory.build(keyguardStatusBarView);
-        if (mKeyguardStatusBarViewController != null) {
-            // TODO(b/194181195): This shouldn't be necessary.
-            mKeyguardStatusBarViewController.onViewDetached();
-        }
-        mKeyguardStatusBarViewController =
-                statusBarViewComponent.getKeyguardStatusBarViewController();
-        mKeyguardStatusBarViewController.init();
-
         if (mKeyguardUserSwitcherController != null) {
             // Try to close the switcher so that callbacks are triggered if necessary.
             // Otherwise, NPV can get into a state where some of the views are still hidden
@@ -1000,16 +953,16 @@
                     userSwitcherComponent.getKeyguardQsUserSwitchController();
             mKeyguardQsUserSwitchController.setNotificationPanelViewController(this);
             mKeyguardQsUserSwitchController.init();
-            mKeyguardStatusBar.setKeyguardUserSwitcherEnabled(true);
+            mKeyguardStatusBarViewController.setKeyguardUserSwitcherEnabled(true);
         } else if (keyguardUserSwitcherView != null) {
             KeyguardUserSwitcherComponent userSwitcherComponent =
                     mKeyguardUserSwitcherComponentFactory.build(keyguardUserSwitcherView);
             mKeyguardUserSwitcherController =
                     userSwitcherComponent.getKeyguardUserSwitcherController();
             mKeyguardUserSwitcherController.init();
-            mKeyguardStatusBar.setKeyguardUserSwitcherEnabled(true);
+            mKeyguardStatusBarViewController.setKeyguardUserSwitcherEnabled(true);
         } else {
-            mKeyguardStatusBar.setKeyguardUserSwitcherEnabled(false);
+            mKeyguardStatusBarViewController.setKeyguardUserSwitcherEnabled(false);
         }
     }
 
@@ -1067,7 +1020,7 @@
         constraintSet.applyTo(mNotificationContainerParent);
         mNotificationContainerParent.setSplitShadeEnabled(mShouldUseSplitNotificationShade);
 
-        updateKeyguardStatusViewAlignment(false /* animate */);
+        updateKeyguardStatusViewAlignment(/* animate= */false);
 
         mKeyguardMediaController.refreshMediaPosition();
     }
@@ -1114,6 +1067,9 @@
         keyguardStatusView = (KeyguardStatusView) mLayoutInflater.inflate(
                 R.layout.keyguard_status_view, mNotificationContainerParent, false);
         mNotificationContainerParent.addView(keyguardStatusView, statusIndex);
+        // When it's reinflated, this is centered by default. If it shouldn't be, this will update
+        // below when resources are updated.
+        mStatusViewCentered = true;
         attachSplitShadeMediaPlayerContainer(
                 keyguardStatusView.findViewById(R.id.status_view_media_container));
 
@@ -1142,7 +1098,7 @@
 
         mBigClockContainer.removeAllViews();
         updateViewControllers(mView.findViewById(R.id.keyguard_status_view), userAvatarView,
-                mKeyguardStatusBar, keyguardUserSwitcherView);
+                keyguardUserSwitcherView);
 
         // Update keyguard bottom area
         int index = mView.indexOfChild(mKeyguardBottomArea);
@@ -1203,9 +1159,12 @@
         if (mKeyguardShowing && !mKeyguardBypassController.getBypassEnabled()) {
             mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(
                     mMaxAllowedKeyguardNotifications);
+            mNotificationStackScrollLayoutController.setKeyguardBottomPadding(
+                    mKeyguardNotificationBottomPadding);
         } else {
             // no max when not on the keyguard
             mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(-1);
+            mNotificationStackScrollLayoutController.setKeyguardBottomPadding(-1f);
         }
     }
 
@@ -1305,8 +1264,6 @@
     }
 
     private void updateClockAppearance() {
-        int totalHeight = mView.getHeight();
-        int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
         int userSwitcherPreferredY = mStatusBarHeaderHeightKeyguard;
         boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
         final boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
@@ -1327,14 +1284,11 @@
                 mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()
                         ? 1.0f : mInterpolatedDarkAmount;
         mClockPositionAlgorithm.setup(mStatusBarHeaderHeightKeyguard,
-                totalHeight - bottomPadding,
-                mNotificationStackScrollLayoutController.getIntrinsicContentHeight(),
                 expandedFraction,
-                totalHeight,
                 mKeyguardStatusViewController.getLockscreenHeight(),
                 userIconHeight,
-                userSwitcherPreferredY, hasCustomClock(),
-                hasVisibleNotifications, darkamount, mOverStretchAmount,
+                userSwitcherPreferredY,
+                darkamount, mOverStretchAmount,
                 bypassEnabled, getUnlockedStackScrollerPadding(),
                 computeQsExpansionFraction(),
                 mDisplayTopInset,
@@ -1408,6 +1362,7 @@
 
         float bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
         bottomPadding = Math.max(lockIconPadding, bottomPadding);
+        mKeyguardNotificationBottomPadding = bottomPadding;
 
         float availableSpace =
                 mNotificationStackScrollLayoutController.getHeight()
@@ -1873,6 +1828,9 @@
 
 
     private boolean handleQsTouch(MotionEvent event) {
+        if (mShouldUseSplitNotificationShade && touchXOutsideOfQs(event.getX())) {
+            return false;
+        }
         final int action = event.getActionMasked();
         if (action == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f
                 && mBarState != KEYGUARD && !mQsExpanded && isQsExpansionEnabled()) {
@@ -1914,8 +1872,12 @@
         return false;
     }
 
+    private boolean touchXOutsideOfQs(float touchX) {
+        return touchX < mQsFrame.getX() || touchX > mQsFrame.getX() + mQsFrame.getWidth();
+    }
+
     private boolean isInQsArea(float x, float y) {
-        if (x < mQsFrame.getX() || x > mQsFrame.getX() + mQsFrame.getWidth()) {
+        if (touchXOutsideOfQs(x)) {
             return false;
         }
         // Let's reject anything at the very bottom around the home handle in gesture nav
@@ -2152,59 +2114,6 @@
         }
     }
 
-    private final Runnable mAnimateKeyguardStatusBarInvisibleEndRunnable = new Runnable() {
-        @Override
-        public void run() {
-            mKeyguardStatusBar.setVisibility(View.INVISIBLE);
-            mKeyguardStatusBar.setAlpha(1f);
-            mKeyguardStatusBarAnimateAlpha = 1f;
-        }
-    };
-
-    private void animateKeyguardStatusBarOut() {
-        ValueAnimator anim = ValueAnimator.ofFloat(mKeyguardStatusBar.getAlpha(), 0f);
-        anim.addUpdateListener(mStatusBarAnimateAlphaListener);
-        anim.setStartDelay(mKeyguardStateController.isKeyguardFadingAway()
-                ? mKeyguardStateController.getKeyguardFadingAwayDelay() : 0);
-
-        long duration;
-        if (mKeyguardStateController.isKeyguardFadingAway()) {
-            duration = mKeyguardStateController.getShortenedFadingAwayDuration();
-        } else {
-            duration = StackStateAnimator.ANIMATION_DURATION_STANDARD;
-        }
-        anim.setDuration(duration);
-
-        anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
-        anim.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mAnimateKeyguardStatusBarInvisibleEndRunnable.run();
-            }
-        });
-        anim.start();
-    }
-
-    private final ValueAnimator.AnimatorUpdateListener
-            mStatusBarAnimateAlphaListener =
-            new ValueAnimator.AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    mKeyguardStatusBarAnimateAlpha = (float) animation.getAnimatedValue();
-                    updateHeaderKeyguardAlpha();
-                }
-            };
-
-    private void animateKeyguardStatusBarIn(long duration) {
-        mKeyguardStatusBar.setVisibility(View.VISIBLE);
-        mKeyguardStatusBar.setAlpha(0f);
-        ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
-        anim.addUpdateListener(mStatusBarAnimateAlphaListener);
-        anim.setDuration(duration);
-        anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
-        anim.start();
-    }
-
     private final Runnable mAnimateKeyguardBottomAreaInvisibleEndRunnable = new Runnable() {
         @Override
         public void run() {
@@ -2257,7 +2166,7 @@
         mQsExpansionHeight = height;
         updateQsExpansion();
         requestScrollerTopPaddingUpdate(false /* animate */);
-        updateHeaderKeyguardAlpha();
+        mKeyguardStatusBarViewController.updateViewState();
         if (mBarState == StatusBarState.SHADE_LOCKED || mBarState == KEYGUARD) {
             updateKeyguardBottomAreaAlpha();
             positionClockAndNotifications();
@@ -2386,6 +2295,9 @@
             final int startTop = mKeyguardStatusAreaClipBounds.top;
             final int startRight = mKeyguardStatusAreaClipBounds.right;
             final int startBottom = mKeyguardStatusAreaClipBounds.bottom;
+            if (mQsClippingAnimation != null) {
+                mQsClippingAnimation.cancel();
+            }
             mQsClippingAnimation = ValueAnimator.ofFloat(0.0f, 1.0f);
             mQsClippingAnimation.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
             mQsClippingAnimation.setDuration(mNotificationBoundsAnimationDuration);
@@ -2421,7 +2333,6 @@
             boolean qsVisible) {
         // Fancy clipping for quick settings
         int radius = mScrimCornerRadius;
-        int statusBarClipTop = 0;
         boolean clipStatusView = false;
         if (!mShouldUseSplitNotificationShade) {
             // The padding on this area is large enough that we can use a cheaper clipping strategy
@@ -2430,7 +2341,6 @@
             float screenCornerRadius = mRecordingController.isRecording() ? 0 : mScreenCornerRadius;
             radius = (int) MathUtils.lerp(screenCornerRadius, mScrimCornerRadius,
                     Math.min(top / (float) mScrimCornerRadius, 1f));
-            statusBarClipTop = top - mKeyguardStatusBar.getTop();
         }
         if (mQs != null) {
             float qsTranslation = 0;
@@ -2449,9 +2359,12 @@
             mQsTranslationForFullShadeTransition = qsTranslation;
             updateQsFrameTranslation();
             float currentTranslation = mQsFrame.getTranslationY();
-            mQs.setFancyClipping((
-                    int) (top - currentTranslation),
-                    (int) (bottom - currentTranslation),
+            mQsClipTop = (int) (top - currentTranslation);
+            mQsClipBottom = (int) (bottom - currentTranslation);
+            mQsVisible = qsVisible;
+            mQs.setFancyClipping(
+                    mQsClipTop,
+                    mQsClipBottom,
                     radius, qsVisible
                     && !mShouldUseSplitNotificationShade);
         }
@@ -2465,8 +2378,13 @@
             mScrimController.setNotificationsBounds(left, top, right, bottom);
         }
 
+        if (mShouldUseSplitNotificationShade) {
+            mKeyguardStatusBarViewController.setNoTopClipping();
+        } else {
+            mKeyguardStatusBarViewController.updateTopClipping(top);
+        }
+
         mScrimController.setScrimCornerRadius(radius);
-        mKeyguardStatusBar.setTopClipping(statusBarClipTop);
         int nsslLeft = left - mNotificationStackScrollLayoutController.getLeft();
         int nsslRight = right - mNotificationStackScrollLayoutController.getLeft();
         int nsslTop = top - mNotificationStackScrollLayoutController.getTop();
@@ -2972,7 +2890,7 @@
      */
     private void updateHeader() {
         if (mBarState == KEYGUARD) {
-            updateHeaderKeyguardAlpha();
+            mKeyguardStatusBarViewController.updateViewState();
         }
         updateQsExpansion();
     }
@@ -3020,23 +2938,6 @@
         return alpha;
     }
 
-    private void updateHeaderKeyguardAlpha() {
-        if (!mKeyguardShowing) {
-            return;
-        }
-        float alphaQsExpansion = 1 - Math.min(1, computeQsExpansionFraction() * 2);
-        float newAlpha = Math.min(getKeyguardContentsAlpha(), alphaQsExpansion)
-                * mKeyguardStatusBarAnimateAlpha;
-        newAlpha *= 1.0f - mKeyguardHeadsUpShowingAmount;
-        mKeyguardStatusBar.setAlpha(newAlpha);
-        boolean
-                hideForBypass =
-                mFirstBypassAttempt && mUpdateMonitor.shouldListenForFace()
-                        || mDelayShowingKeyguardStatusBar;
-        mKeyguardStatusBar.setVisibility(
-                newAlpha != 0f && !mDozing && !hideForBypass ? View.VISIBLE : View.INVISIBLE);
-    }
-
     private void updateKeyguardBottomAreaAlpha() {
         // There are two possible panel expansion behaviors:
         // • User dragging up to unlock: we want to fade out as quick as possible
@@ -3263,7 +3164,7 @@
     private void updateDozingVisibilities(boolean animate) {
         mKeyguardBottomArea.setDozing(mDozing, animate);
         if (!mDozing && animate) {
-            animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+            mKeyguardStatusBarViewController.animateKeyguardStatusBarIn();
         }
     }
 
@@ -3289,7 +3190,10 @@
         switch (mBarState) {
             case KEYGUARD:
                 if (!mDozingOnDown) {
-                    if (mKeyguardBypassController.getBypassEnabled()) {
+                    if (mUpdateMonitor.isFaceEnrolled()
+                            && !mUpdateMonitor.isFaceDetectionRunning()
+                            && !mUpdateMonitor.getUserCanSkipBouncer(
+                                    KeyguardUpdateMonitor.getCurrentUser())) {
                         mUpdateMonitor.requestFaceAuth(true);
                     } else {
                         mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_HINT,
@@ -3347,7 +3251,7 @@
 
     private void setKeyguardHeadsUpShowingAmount(float amount) {
         mKeyguardHeadsUpShowingAmount = amount;
-        updateHeaderKeyguardAlpha();
+        mKeyguardStatusBarViewController.updateViewState();
     }
 
     private float getKeyguardHeadsUpShowingAmount() {
@@ -3656,6 +3560,7 @@
         mDozing = dozing;
         mNotificationStackScrollLayoutController.setDozing(mDozing, animate, wakeUpTouchLocation);
         mKeyguardBottomArea.setDozing(mDozing, animate);
+        mKeyguardStatusBarViewController.setDozing(mDozing);
 
         if (dozing) {
             mBottomAreaShadeAlphaAnimator.cancel();
@@ -3780,7 +3685,9 @@
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         super.dump(fd, pw, args);
-        pw.println("    gestureExclusionRect: " + calculateGestureExclusionRect());
+        pw.println("    gestureExclusionRect: " + calculateGestureExclusionRect()
+                + " applyQSClippingImmediately: top(" + mQsClipTop + ") bottom(" + mQsClipBottom
+                + ") qsVisible(" + mQsVisible);
         if (mKeyguardStatusBarViewController != null) {
             mKeyguardStatusBarViewController.dump(fd, pw, args);
         }
@@ -3914,7 +3821,9 @@
                 if (mStatusBar.isBouncerShowing()) {
                     return true;
                 }
-                if (mBar.panelEnabled() && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
+                if (mBar.panelEnabled()
+                        && !mNotificationStackScrollLayoutController.isLongPressInProgress()
+                        && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
                     mMetricsLogger.count(COUNTER_PANEL_OPEN, 1);
                     mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1);
                     return true;
@@ -3966,6 +3875,7 @@
                     return true;
                 }
                 if (mListenForHeadsUp && !mHeadsUpTouchHelper.isTrackingHeadsUp()
+                        && !mNotificationStackScrollLayoutController.isLongPressInProgress()
                         && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
                     mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1);
                 }
@@ -4117,9 +4027,7 @@
         @Override
         public void flingTopOverscroll(float velocity, boolean open) {
             // in split shade mode we want to expand/collapse QS only when touch happens within QS
-            if (mShouldUseSplitNotificationShade
-                    && (mInitialTouchX < mQsFrame.getX()
-                        || mInitialTouchX > mQsFrame.getX() + mQsFrame.getWidth())) {
+            if (mShouldUseSplitNotificationShade && touchXOutsideOfQs(mInitialTouchX)) {
                 return;
             }
             mLastOverscroll = 0f;
@@ -4438,11 +4346,22 @@
 
             if (oldState == KEYGUARD && (goingToFullShade
                     || statusBarState == StatusBarState.SHADE_LOCKED)) {
-                animateKeyguardStatusBarOut();
+
+                long startDelay;
+                long duration;
+                if (mKeyguardStateController.isKeyguardFadingAway()) {
+                    startDelay = mKeyguardStateController.getKeyguardFadingAwayDelay();
+                    duration = mKeyguardStateController.getShortenedFadingAwayDuration();
+                } else {
+                    startDelay = 0;
+                    duration = StackStateAnimator.ANIMATION_DURATION_STANDARD;
+                }
+                mKeyguardStatusBarViewController.animateKeyguardStatusBarOut(startDelay, duration);
                 updateQSMinHeight();
             } else if (oldState == StatusBarState.SHADE_LOCKED
                     && statusBarState == KEYGUARD) {
-                animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+                mKeyguardStatusBarViewController.animateKeyguardStatusBarIn();
+
                 mNotificationStackScrollLayoutController.resetScrollPosition();
                 // Only animate header if the header is visible. If not, it will partially
                 // animate out
@@ -4454,8 +4373,9 @@
                     }
                 }
             } else {
-                mKeyguardStatusBar.setAlpha(1f);
-                mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE);
+                mKeyguardStatusBarViewController.updateViewState(
+                        /* alpha= */ 1f,
+                        keyguardShowing ? View.VISIBLE : View.INVISIBLE);
                 if (keyguardShowing && oldState != mBarState) {
                     if (mQs != null) {
                         mQs.hideImmediately();
@@ -4519,7 +4439,6 @@
                             .addTagListener(QS.TAG, mFragmentListener);
             mStatusBarStateController.addCallback(mStatusBarStateListener);
             mConfigurationController.addCallback(mConfigurationListener);
-            mUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
             // Theme might have changed between inflating this view and attaching it to the
             // window, so
             // force a call to onThemeChanged
@@ -4536,7 +4455,6 @@
                             .removeTagListener(QS.TAG, mFragmentListener);
             mStatusBarStateController.removeCallback(mStatusBarStateListener);
             mConfigurationController.removeCallback(mConfigurationListener);
-            mUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
             mFalsingManager.removeTapListener(mFalsingTapListener);
         }
     }
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 1a404dc..030a895 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
@@ -84,8 +84,8 @@
     private final WindowManager mWindowManager;
     private final IActivityManager mActivityManager;
     private final DozeParameters mDozeParameters;
+    private final KeyguardStateController mKeyguardStateController;
     private final LayoutParams mLpChanged;
-    private final boolean mKeyguardScreenRotation;
     private final long mLockScreenDisplayTimeout;
     private final float mKeyguardPreferredRefreshRate; // takes precedence over max
     private final float mKeyguardMaxRefreshRate;
@@ -123,8 +123,8 @@
         mContext = context;
         mWindowManager = windowManager;
         mActivityManager = activityManager;
-        mKeyguardScreenRotation = keyguardStateController.isKeyguardScreenRotationAllowed();
         mDozeParameters = dozeParameters;
+        mKeyguardStateController = keyguardStateController;
         mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze();
         mLpChanged = new LayoutParams();
         mKeyguardViewMediator = keyguardViewMediator;
@@ -296,8 +296,7 @@
 
         if (mKeyguardPreferredRefreshRate > 0) {
             boolean onKeyguard = state.mStatusBarState == StatusBarState.KEYGUARD
-                    && !state.mKeyguardFadingAway && !state.mKeyguardGoingAway
-                    && !state.mDozing;
+                    && !state.mKeyguardFadingAway && !state.mKeyguardGoingAway;
             if (onKeyguard
                     && mAuthController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser())) {
                 mLpChanged.preferredMaxDisplayRefreshRate = mKeyguardPreferredRefreshRate;
@@ -324,7 +323,7 @@
 
     private void adjustScreenOrientation(State state) {
         if (state.isKeyguardShowingAndNotOccluded() || state.mDozing) {
-            if (mKeyguardScreenRotation) {
+            if (mKeyguardStateController.isKeyguardScreenRotationAllowed()) {
                 mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER;
             } else {
                 mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
@@ -607,12 +606,11 @@
     }
 
     @Override
-    public void setLightRevealScrimAmount(float amount) {
-        boolean lightRevealScrimOpaque = amount == 0;
-        if (mCurrentState.mLightRevealScrimOpaque == lightRevealScrimOpaque) {
+    public void setLightRevealScrimOpaque(boolean opaque) {
+        if (mCurrentState.mLightRevealScrimOpaque == opaque) {
             return;
         }
-        mCurrentState.mLightRevealScrimOpaque = lightRevealScrimOpaque;
+        mCurrentState.mLightRevealScrimOpaque = opaque;
         apply(mCurrentState);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index b5d9bd6..147ebfb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -47,7 +47,6 @@
 import com.android.systemui.statusbar.NotificationShadeDepthController;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -106,7 +105,7 @@
     private boolean mExpandingBelowNotch;
     private final DockManager mDockManager;
     private final NotificationPanelViewController mNotificationPanelViewController;
-    private final SuperStatusBarViewFactory mStatusBarViewFactory;
+    private final StatusBarWindowView mStatusBarWindowView;
 
     // Used for determining view / touch intersection
     private int[] mTempLocation = new int[2];
@@ -136,7 +135,7 @@
             NotificationShadeDepthController depthController,
             NotificationShadeWindowView notificationShadeWindowView,
             NotificationPanelViewController notificationPanelViewController,
-            SuperStatusBarViewFactory statusBarViewFactory,
+            StatusBarWindowView statusBarWindowView,
             NotificationStackScrollLayoutController notificationStackScrollLayoutController,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
         mInjectionInflationController = injectionInflationController;
@@ -160,7 +159,7 @@
         mDockManager = dockManager;
         mNotificationPanelViewController = notificationPanelViewController;
         mDepthController = depthController;
-        mStatusBarViewFactory = statusBarViewFactory;
+        mStatusBarWindowView = statusBarWindowView;
         mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
 
@@ -311,6 +310,12 @@
                     // Capture all touch events in always-on.
                     return true;
                 }
+
+                if (mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()) {
+                    // capture all touches if the alt auth bouncer is showing
+                    return true;
+                }
+
                 boolean intercept = false;
                 if (mNotificationPanelViewController.isFullyExpanded()
                         && mDragDownHelper.isDragDownEnabled()
@@ -338,6 +343,12 @@
                 if (mStatusBarStateController.isDozing()) {
                     handled = !mService.isPulsing();
                 }
+
+                if (mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()) {
+                    // eat the touch
+                    handled = true;
+                }
+
                 if ((mDragDownHelper.isDragDownEnabled() && !handled)
                         || mDragDownHelper.isDraggingDown()) {
                     // we still want to finish our drag down gesture when locking the screen
@@ -465,11 +476,10 @@
 
     public void setStatusBarView(PhoneStatusBarView statusBarView) {
         mStatusBarView = statusBarView;
-        if (statusBarView != null && mStatusBarViewFactory != null) {
+        if (statusBarView != null) {
             mBarTransitions = new PhoneStatusBarTransitions(
                     statusBarView,
-                    mStatusBarViewFactory.getStatusBarWindowView()
-                            .findViewById(R.id.status_bar_container));
+                    mStatusBarWindowView.findViewById(R.id.status_bar_container));
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 323a112..a3877b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -79,7 +79,6 @@
     protected long mDownTime;
     protected boolean mTouchSlopExceededBeforeDown;
     private float mMinExpandHeight;
-    private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
     private boolean mPanelUpdateWhenAnimatorEnds;
     private boolean mVibrateOnOpening;
     protected boolean mIsLaunchAnimationRunning;
@@ -182,6 +181,7 @@
     protected final KeyguardStateController mKeyguardStateController;
     protected final SysuiStatusBarStateController mStatusBarStateController;
     protected final AmbientState mAmbientState;
+    protected final LockscreenGestureLogger mLockscreenGestureLogger;
 
     protected void onExpandingFinished() {
         mBar.onExpandingFinished();
@@ -217,10 +217,12 @@
             LatencyTracker latencyTracker,
             FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
             StatusBarTouchableRegionManager statusBarTouchableRegionManager,
+            LockscreenGestureLogger lockscreenGestureLogger,
             AmbientState ambientState) {
         mAmbientState = ambientState;
         mView = view;
         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+        mLockscreenGestureLogger = lockscreenGestureLogger;
         mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
             @Override
             public void onViewAttachedToWindow(View v) {
@@ -1211,10 +1213,14 @@
                 case MotionEvent.ACTION_MOVE:
                     final float h = y - mInitialTouchY;
                     addMovement(event);
-                    if (canCollapsePanel || mTouchStartedInEmptyArea || mAnimatingOnDown) {
+                    final boolean openShadeWithoutHun =
+                            mPanelClosedOnDown && !mCollapsedAndHeadsUpOnDown;
+                    if (canCollapsePanel || mTouchStartedInEmptyArea || mAnimatingOnDown
+                            || openShadeWithoutHun) {
                         float hAbs = Math.abs(h);
                         float touchSlop = getTouchSlop(event);
-                        if ((h < -touchSlop || (mAnimatingOnDown && hAbs > touchSlop))
+                        if ((h < -touchSlop
+                                || ((openShadeWithoutHun || mAnimatingOnDown) && hAbs > touchSlop))
                                 && hAbs > Math.abs(x - mInitialTouchX)) {
                             cancelHeightAnimator();
                             startExpandMotion(x, y, true /* startTracking */, mExpandedHeight);
@@ -1227,10 +1233,7 @@
                     mVelocityTracker.clear();
                     break;
             }
-
-            // Finally, if none of the above cases applies, ensure that touches do not get handled
-            // by the contents of a panel that is not showing (a bit of a hack to avoid b/178277858)
-            return (mView.getVisibility() != View.VISIBLE);
+            return false;
         }
 
         @Override
@@ -1397,8 +1400,7 @@
 
     private void beginJankMonitoring(int cuj) {
         InteractionJankMonitor.Configuration.Builder builder =
-                new InteractionJankMonitor.Configuration.Builder(cuj)
-                        .setView(mView)
+                InteractionJankMonitor.Configuration.Builder.withView(cuj, mView)
                         .setTag(isFullyCollapsed() ? "Expand" : "Collapse");
         InteractionJankMonitor.getInstance().begin(builder);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index c300b11..70a46b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -27,6 +27,7 @@
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.util.EventLog;
+import android.util.Log;
 import android.util.Pair;
 import android.view.DisplayCutout;
 import android.view.Gravity;
@@ -42,7 +43,6 @@
 import com.android.systemui.R;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.util.leak.RotationUtils;
 
 import java.util.List;
@@ -52,7 +52,6 @@
     private static final String TAG = "PhoneStatusBarView";
     private static final boolean DEBUG = StatusBar.DEBUG;
     private static final boolean DEBUG_GESTURES = false;
-    private final CommandQueue mCommandQueue;
     private final StatusBarContentInsetsProvider mContentInsetsProvider;
 
     StatusBar mBar;
@@ -81,6 +80,8 @@
     @Nullable
     private List<StatusBar.ExpansionChangedListener> mExpansionChangedListeners;
 
+    private PanelEnabledProvider mPanelEnabledProvider;
+
     /**
      * Draw this many pixels into the left/right side of the cutout to optimally use the space
      */
@@ -89,7 +90,6 @@
 
     public PhoneStatusBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mCommandQueue = Dependency.get(CommandQueue.class);
         mContentInsetsProvider = Dependency.get(StatusBarContentInsetsProvider.class);
     }
 
@@ -177,7 +177,11 @@
 
     @Override
     public boolean panelEnabled() {
-        return mCommandQueue.panelsEnabled();
+        if (mPanelEnabledProvider == null) {
+            Log.e(TAG, "panelEnabledProvider is null; defaulting to super class.");
+            return super.panelEnabled();
+        }
+        return mPanelEnabledProvider.panelEnabled();
     }
 
     @Override
@@ -294,6 +298,11 @@
         }
     }
 
+    /** Set the {@link PanelEnabledProvider} to use. */
+    public void setPanelEnabledProvider(PanelEnabledProvider panelEnabledProvider) {
+        mPanelEnabledProvider = panelEnabledProvider;
+    }
+
     private void updateScrimFraction() {
         float scrimFraction = mPanelFraction;
         if (mMinFraction < 1.0f) {
@@ -391,4 +400,10 @@
     protected boolean shouldPanelBeVisible() {
         return mHeadsUpVisible || super.shouldPanelBeVisible();
     }
+
+    /** An interface that will provide whether panel is enabled. */
+    interface PanelEnabledProvider {
+        /** Returns true if the panel is enabled and false otherwise. */
+        boolean panelEnabled();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.java
new file mode 100644
index 0000000..b36b67d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.java
@@ -0,0 +1,39 @@
+/*
+ * 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.phone;
+
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.util.ViewController;
+
+/** Controller for {@link PhoneStatusBarView}. */
+public class PhoneStatusBarViewController extends ViewController<PhoneStatusBarView> {
+
+    protected PhoneStatusBarViewController(
+            PhoneStatusBarView view,
+            CommandQueue commandQueue) {
+        super(view);
+        mView.setPanelEnabledProvider(commandQueue::panelsEnabled);
+    }
+
+    @Override
+    protected void onViewAttached() {
+    }
+
+    @Override
+    protected void onViewDetached() {
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 44ed279..87d4543 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -111,6 +111,20 @@
      */
     private boolean mTransitioningToFullShade;
 
+    /**
+     * Is there currently an unocclusion animation running. Used to avoid bright flickers
+     * of the notification scrim.
+     */
+    private boolean mUnOcclusionAnimationRunning;
+
+    /**
+     * Set whether an unocclusion animation is currently running on the notification panel. Used
+     * to avoid bright flickers of the notification scrim.
+     */
+    public void setUnocclusionAnimationRunning(boolean unocclusionAnimationRunning) {
+        mUnOcclusionAnimationRunning = unocclusionAnimationRunning;
+    }
+
     @IntDef(prefix = {"VISIBILITY_"}, value = {
             TRANSPARENT,
             SEMI_TRANSPARENT,
@@ -136,12 +150,6 @@
     public static final float BUSY_SCRIM_ALPHA = 1f;
 
     /**
-     * The default scrim under the expanded bubble stack.
-     * This should not be lower than 0.54, otherwise we won't pass GAR.
-     */
-    public static final float BUBBLE_SCRIM_ALPHA = 0.6f;
-
-    /**
      * Scrim opacity that can have text on top.
      */
     public static final float GAR_SCRIM_ALPHA = 0.6f;
@@ -156,8 +164,6 @@
     private ScrimView mScrimInFront;
     private ScrimView mNotificationsScrim;
     private ScrimView mScrimBehind;
-    @Nullable
-    private ScrimView mScrimForBubble;
 
     private Runnable mScrimBehindChangeRunnable;
 
@@ -195,12 +201,10 @@
     private float mInFrontAlpha = NOT_INITIALIZED;
     private float mBehindAlpha = NOT_INITIALIZED;
     private float mNotificationsAlpha = NOT_INITIALIZED;
-    private float mBubbleAlpha = NOT_INITIALIZED;
 
     private int mInFrontTint;
     private int mBehindTint;
     private int mNotificationsTint;
-    private int mBubbleTint;
 
     private boolean mWallpaperVisibilityTimedOut;
     private int mScrimsVisibility;
@@ -229,7 +233,6 @@
             UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) {
         mScrimStateListener = lightBarController::setScrimState;
         mDefaultScrimAlpha = BUSY_SCRIM_ALPHA;
-        ScrimState.BUBBLE_EXPANDED.setBubbleAlpha(BUBBLE_SCRIM_ALPHA);
 
         mKeyguardStateController = keyguardStateController;
         mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen();
@@ -276,11 +279,10 @@
      * Attach the controller to the supplied views.
      */
     public void attachViews(ScrimView behindScrim, ScrimView notificationsScrim,
-                            ScrimView scrimInFront, @Nullable ScrimView scrimForBubble) {
+                            ScrimView scrimInFront) {
         mNotificationsScrim = notificationsScrim;
         mScrimBehind = behindScrim;
         mScrimInFront = scrimInFront;
-        mScrimForBubble = scrimForBubble;
         updateThemeColors();
 
         behindScrim.enableBottomEdgeConcave(mClipsQsScrim);
@@ -293,8 +295,7 @@
 
         final ScrimState[] states = ScrimState.values();
         for (int i = 0; i < states.length; i++) {
-            states[i].init(mScrimInFront, mScrimBehind, mScrimForBubble, mDozeParameters,
-                    mDockManager);
+            states[i].init(mScrimInFront, mScrimBehind, mDozeParameters, mDockManager);
             states[i].setScrimBehindAlphaKeyguard(mScrimBehindAlphaKeyguard);
             states[i].setDefaultScrimAlpha(mDefaultScrimAlpha);
         }
@@ -302,9 +303,6 @@
         mScrimBehind.setDefaultFocusHighlightEnabled(false);
         mNotificationsScrim.setDefaultFocusHighlightEnabled(false);
         mScrimInFront.setDefaultFocusHighlightEnabled(false);
-        if (mScrimForBubble != null) {
-            mScrimForBubble.setDefaultFocusHighlightEnabled(false);
-        }
         updateScrims();
         mKeyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
     }
@@ -404,7 +402,7 @@
         if (mKeyguardUpdateMonitor.needsSlowUnlockTransition() && mState == ScrimState.UNLOCKED) {
             mAnimationDelay = StatusBar.FADE_KEYGUARD_START_DELAY;
             scheduleUpdate();
-        } else if ((oldState == ScrimState.AOD  // leaving doze
+        } else if (((oldState == ScrimState.AOD || oldState == ScrimState.PULSING)  // leaving doze
                 && (!mDozeParameters.getAlwaysOn() || mState == ScrimState.UNLOCKED))
                 || (mState == ScrimState.AOD && !mDozeParameters.getDisplayNeedsBlanking())) {
             // Scheduling a frame isn't enough when:
@@ -452,6 +450,7 @@
 
     public void onExpandingFinished() {
         mTracking = false;
+        setUnocclusionAnimationRunning(false);
     }
 
     @VisibleForTesting
@@ -496,8 +495,7 @@
             boolean relevantState = (mState == ScrimState.UNLOCKED
                     || mState == ScrimState.KEYGUARD
                     || mState == ScrimState.SHADE_LOCKED
-                    || mState == ScrimState.PULSING
-                    || mState == ScrimState.BUBBLE_EXPANDED);
+                    || mState == ScrimState.PULSING);
             if (!(relevantState && mExpansionAffectsAlpha)) {
                 return;
             }
@@ -564,8 +562,7 @@
             mQsBottomVisible = qsBottomVisible;
             boolean relevantState = (mState == ScrimState.SHADE_LOCKED
                     || mState == ScrimState.KEYGUARD
-                    || mState == ScrimState.PULSING
-                    || mState == ScrimState.BUBBLE_EXPANDED);
+                    || mState == ScrimState.PULSING);
             if (!(relevantState && mExpansionAffectsAlpha)) {
                 return;
             }
@@ -627,11 +624,9 @@
         mInFrontTint = mState.getFrontTint();
         mBehindTint = mState.getBehindTint();
         mNotificationsTint = mState.getNotifTint();
-        mBubbleTint = mState.getBubbleTint();
 
         mInFrontAlpha = mState.getFrontAlpha();
         mBehindAlpha = mState.getBehindAlpha();
-        mBubbleAlpha = mState.getBubbleAlpha();
         mNotificationsAlpha = mState.getNotifAlpha();
 
         assertAlphasValid();
@@ -640,7 +635,7 @@
             return;
         }
 
-        if (mState == ScrimState.UNLOCKED || mState == ScrimState.BUBBLE_EXPANDED) {
+        if (mState == ScrimState.UNLOCKED) {
             // Darken scrim as you pull down the shade when unlocked, unless the shade is expanding
             // because we're doing the screen off animation.
             if (!mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()) {
@@ -692,6 +687,18 @@
                 mNotificationsTint = mState.getNotifTint();
                 mBehindTint = behindTint;
             }
+
+            // At the end of a launch animation over the lockscreen, the state is either KEYGUARD or
+            // SHADE_LOCKED and this code is called. We have to set the notification alpha to 0
+            // otherwise there is a flicker to its previous value.
+            if (mKeyguardOccluded) {
+                mNotificationsAlpha = 0;
+            }
+            if (mUnOcclusionAnimationRunning && mState == ScrimState.KEYGUARD) {
+                // We're unoccluding the keyguard and don't want to have a bright flash.
+                mNotificationsAlpha = KEYGUARD_SCRIM_ALPHA;
+                mNotificationsTint = ScrimState.KEYGUARD.getNotifTint();
+            }
         }
 
         assertAlphasValid();
@@ -744,7 +751,6 @@
         setOrAdaptCurrentAnimation(mScrimBehind);
         setOrAdaptCurrentAnimation(mNotificationsScrim);
         setOrAdaptCurrentAnimation(mScrimInFront);
-        setOrAdaptCurrentAnimation(mScrimForBubble);
         dispatchBackScrimState(mScrimBehind.getViewAlpha());
 
         // Reset wallpaper timeout if it's already timeout like expanding panel while PULSING
@@ -852,11 +858,6 @@
         setScrimAlpha(mScrimBehind, mBehindAlpha);
         setScrimAlpha(mNotificationsScrim, mNotificationsAlpha);
 
-        if (mScrimForBubble != null) {
-            boolean animateScrimForBubble = mScrimForBubble.getViewAlpha() != 0 && !mBlankScreen;
-            mScrimForBubble.setColors(mColors, animateScrimForBubble);
-            setScrimAlpha(mScrimForBubble, mBubbleAlpha);
-        }
         // The animation could have all already finished, let's call onFinished just in case
         onFinished(mState);
         dispatchScrimsVisible();
@@ -909,8 +910,6 @@
             return "behind_scrim";
         } else if (scrim == mNotificationsScrim) {
             return "notifications_scrim";
-        } else if (scrim == mScrimForBubble) {
-            return "bubble_scrim";
         }
         return "unknown_scrim";
     }
@@ -983,8 +982,6 @@
             return mBehindAlpha;
         } else if (scrim == mNotificationsScrim) {
             return mNotificationsAlpha;
-        } else if (scrim == mScrimForBubble) {
-            return mBubbleAlpha;
         } else {
             throw new IllegalArgumentException("Unknown scrim view");
         }
@@ -997,8 +994,6 @@
             return mBehindTint;
         } else if (scrim == mNotificationsScrim) {
             return mNotificationsTint;
-        } else if (scrim == mScrimForBubble) {
-            return mBubbleTint;
         } else {
             throw new IllegalArgumentException("Unknown scrim view");
         }
@@ -1030,8 +1025,7 @@
         }
         if (isAnimating(mScrimBehind)
                 || isAnimating(mNotificationsScrim)
-                || isAnimating(mScrimInFront)
-                || isAnimating(mScrimForBubble)) {
+                || isAnimating(mScrimInFront)) {
             if (callback != null && callback != mCallback) {
                 // Since we only notify the callback that we're finished once everything has
                 // finished, we need to make sure that any changing callbacks are also invoked
@@ -1058,13 +1052,9 @@
             mInFrontTint = Color.TRANSPARENT;
             mBehindTint = mState.getBehindTint();
             mNotificationsTint = mState.getNotifTint();
-            mBubbleTint = Color.TRANSPARENT;
             updateScrimColor(mScrimInFront, mInFrontAlpha, mInFrontTint);
             updateScrimColor(mScrimBehind, mBehindAlpha, mBehindTint);
             updateScrimColor(mNotificationsScrim, mNotificationsAlpha, mNotificationsTint);
-            if (mScrimForBubble != null) {
-                updateScrimColor(mScrimForBubble, mBubbleAlpha, mBubbleTint);
-            }
         }
     }
 
@@ -1232,14 +1222,6 @@
         pw.print(" tint=0x");
         pw.println(Integer.toHexString(mNotificationsScrim.getTint()));
 
-        pw.print("  bubbleScrim:");
-        pw.print(" viewAlpha=");
-        pw.print(mScrimForBubble.getViewAlpha());
-        pw.print(" alpha=");
-        pw.print(mBubbleAlpha);
-        pw.print(" tint=0x");
-        pw.println(Integer.toHexString(mScrimForBubble.getTint()));
-
         pw.print("  mTracking=");
         pw.println(mTracking);
         pw.print("  mDefaultScrimAlpha=");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 0681193..850b986 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -19,8 +19,6 @@
 import android.graphics.Color;
 import android.os.Trace;
 
-import androidx.annotation.Nullable;
-
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.scrim.ScrimView;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
@@ -43,11 +41,9 @@
         public void prepare(ScrimState previousState) {
             mFrontTint = Color.BLACK;
             mBehindTint = Color.BLACK;
-            mBubbleTint = previousState.mBubbleTint;
 
             mFrontAlpha = 1f;
             mBehindAlpha = 1f;
-            mBubbleAlpha = previousState.mBubbleAlpha;
 
             mAnimationDuration = ScrimController.ANIMATION_DURATION_LONG;
         }
@@ -81,12 +77,10 @@
             mFrontTint = Color.BLACK;
             mBehindTint = Color.BLACK;
             mNotifTint = mClipQsScrim ? Color.BLACK : Color.TRANSPARENT;
-            mBubbleTint = Color.TRANSPARENT;
 
             mFrontAlpha = 0;
             mBehindAlpha = mClipQsScrim ? 1 : mScrimBehindAlphaKeyguard;
             mNotifAlpha = mClipQsScrim ? mScrimBehindAlphaKeyguard : 0;
-            mBubbleAlpha = 0;
             if (mClipQsScrim) {
                 updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK);
             }
@@ -118,7 +112,6 @@
             mNotifAlpha = mClipQsScrim ? mDefaultScrimAlpha : 0;
             mNotifTint = Color.TRANSPARENT;
             mFrontAlpha = 0f;
-            mBubbleAlpha = 0f;
         }
     },
 
@@ -129,7 +122,6 @@
         @Override
         public void prepare(ScrimState previousState) {
             mBehindAlpha = 0;
-            mBubbleAlpha = 0f;
             mFrontAlpha = mDefaultScrimAlpha;
         }
     },
@@ -139,7 +131,6 @@
         public void prepare(ScrimState previousState) {
             mBehindAlpha = mClipQsScrim ? 1 : mDefaultScrimAlpha;
             mNotifAlpha = 1f;
-            mBubbleAlpha = 0f;
             mFrontAlpha = 0f;
             mBehindTint = Color.BLACK;
 
@@ -163,7 +154,6 @@
         public void prepare(ScrimState previousState) {
             mBehindAlpha = 0;
             mFrontAlpha = 0;
-            mBubbleAlpha = 0;
         }
     },
 
@@ -185,9 +175,6 @@
             mBehindTint = Color.BLACK;
             mBehindAlpha = ScrimController.TRANSPARENT;
 
-            mBubbleTint = Color.TRANSPARENT;
-            mBubbleAlpha = ScrimController.TRANSPARENT;
-
             mAnimationDuration = ScrimController.ANIMATION_DURATION_LONG;
             // DisplayPowerManager may blank the screen for us, or we might blank it for ourselves
             // by animating the screen off via the LightRevelScrim. In either case we just need to
@@ -214,7 +201,6 @@
         @Override
         public void prepare(ScrimState previousState) {
             mFrontAlpha = mAodFrontScrimAlpha;
-            mBubbleAlpha = 0f;
             mBehindTint = Color.BLACK;
             mFrontTint = Color.BLACK;
             mBlankScreen = mDisplayRequiresBlanking;
@@ -238,31 +224,26 @@
             mBehindAlpha = mClipQsScrim ? 1 : 0;
             mNotifAlpha = 0;
             mFrontAlpha = 0;
-            mBubbleAlpha = 0;
 
             mAnimationDuration = mKeyguardFadingAway
                     ? mKeyguardFadingAwayDuration
                     : StatusBar.FADE_KEYGUARD_DURATION;
 
-            mAnimateChange = !mLaunchingAffordanceWithPreview;
+            boolean fromAod = previousState == AOD || previousState == PULSING;
+            mAnimateChange = !mLaunchingAffordanceWithPreview && !fromAod;
 
             mFrontTint = Color.TRANSPARENT;
             mBehindTint = Color.BLACK;
-            mBubbleTint = Color.TRANSPARENT;
             mBlankScreen = false;
 
             if (previousState == ScrimState.AOD) {
                 // Set all scrims black, before they fade transparent.
                 updateScrimColor(mScrimInFront, 1f /* alpha */, Color.BLACK /* tint */);
                 updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK /* tint */);
-                if (mScrimForBubble != null) {
-                    updateScrimColor(mScrimForBubble, 1f /* alpha */, Color.BLACK /* tint */);
-                }
 
                 // Scrims should still be black at the end of the transition.
                 mFrontTint = Color.BLACK;
                 mBehindTint = Color.BLACK;
-                mBubbleTint = Color.BLACK;
                 mBlankScreen = true;
             }
 
@@ -270,45 +251,24 @@
                 updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK);
             }
         }
-    },
-
-    /**
-     * Unlocked with a bubble expanded.
-     */
-    BUBBLE_EXPANDED {
-        @Override
-        public void prepare(ScrimState previousState) {
-            mFrontTint = Color.TRANSPARENT;
-            mBehindTint = Color.TRANSPARENT;
-            mBubbleTint = Color.BLACK;
-
-            mFrontAlpha = 0f;
-            mBehindAlpha = mDefaultScrimAlpha;
-
-            mAnimationDuration = ScrimController.ANIMATION_DURATION;
-            mBlankScreen = false;
-        }
     };
 
     boolean mBlankScreen = false;
     long mAnimationDuration = ScrimController.ANIMATION_DURATION;
     int mFrontTint = Color.TRANSPARENT;
     int mBehindTint = Color.TRANSPARENT;
-    int mBubbleTint = Color.TRANSPARENT;
     int mNotifTint = Color.TRANSPARENT;
 
     boolean mAnimateChange = true;
     float mAodFrontScrimAlpha;
     float mFrontAlpha;
     float mBehindAlpha;
-    float mBubbleAlpha;
     float mNotifAlpha;
 
     float mScrimBehindAlphaKeyguard;
     float mDefaultScrimAlpha;
     ScrimView mScrimInFront;
     ScrimView mScrimBehind;
-    @Nullable ScrimView mScrimForBubble;
 
     DozeParameters mDozeParameters;
     DockManager mDockManager;
@@ -321,11 +281,10 @@
     long mKeyguardFadingAwayDuration;
     boolean mClipQsScrim;
 
-    public void init(ScrimView scrimInFront, ScrimView scrimBehind, ScrimView scrimForBubble,
-            DozeParameters dozeParameters, DockManager dockManager) {
+    public void init(ScrimView scrimInFront, ScrimView scrimBehind, DozeParameters dozeParameters,
+            DockManager dockManager) {
         mScrimInFront = scrimInFront;
         mScrimBehind = scrimBehind;
-        mScrimForBubble = scrimForBubble;
 
         mDozeParameters = dozeParameters;
         mDockManager = dockManager;
@@ -352,10 +311,6 @@
         return mNotifAlpha;
     }
 
-    public float getBubbleAlpha() {
-        return mBubbleAlpha;
-    }
-
     public int getFrontTint() {
         return mFrontTint;
     }
@@ -368,10 +323,6 @@
         return mNotifTint;
     }
 
-    public int getBubbleTint() {
-        return mBubbleTint;
-    }
-
     public long getAnimationDuration() {
         return mAnimationDuration;
     }
@@ -409,10 +360,6 @@
         mDefaultScrimAlpha = defaultScrimAlpha;
     }
 
-    public void setBubbleAlpha(float alpha) {
-        mBubbleAlpha = alpha;
-    }
-
     public void setWallpaperSupportsAmbientMode(boolean wallpaperSupportsAmbientMode) {
         mWallpaperSupportsAmbientMode = wallpaperSupportsAmbientMode;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
index edacbe1..768222d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
@@ -118,10 +118,6 @@
                     + " flags=" + flags);
         }
 
-        if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) {
-            getStatusBar().postHideRecentApps();
-        }
-
         // TODO(b/62444020): remove when this bug is fixed
         Log.v(TAG, "NotificationShadeWindow: " + getNotificationShadeWindowView()
                 + " canPanelBeCollapsed(): "
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
index 70f3436..4b7fe4e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
@@ -17,10 +17,11 @@
 package com.android.systemui.statusbar.phone
 
 import android.view.View
-import com.android.systemui.BatteryMeterView
 import com.android.systemui.R
+import com.android.systemui.battery.BatteryMeterView
+import com.android.systemui.battery.BatteryMeterViewController
+import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.qs.carrier.QSCarrierGroupController
-import com.android.systemui.statusbar.FeatureFlags
 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
 import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.SPLIT_SHADE_HEADER
 import javax.inject.Inject
@@ -31,7 +32,8 @@
     @Named(SPLIT_SHADE_HEADER) private val statusBar: View,
     private val statusBarIconController: StatusBarIconController,
     qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder,
-    featureFlags: FeatureFlags
+    featureFlags: FeatureFlags,
+    batteryMeterViewController: BatteryMeterViewController
 ) {
 
     // TODO(b/194178072) Handle RSSI hiding when multi carrier
@@ -52,9 +54,11 @@
         }
 
     init {
+        batteryMeterViewController.init()
         val batteryIcon: BatteryMeterView = statusBar.findViewById(R.id.batteryRemainingIcon)
+
         // battery settings same as in QS icons
-        batteryIcon.setIgnoreTunerUpdates(true)
+        batteryMeterViewController.ignoreTunerUpdates()
         batteryIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
 
         val iconContainer: StatusIconContainer = statusBar.findViewById(R.id.statusIcons)
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 3b5d69d..32b5cb2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -18,7 +18,6 @@
 
 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
-import static android.app.StatusBarManager.WindowType;
 import static android.app.StatusBarManager.WindowVisibleState;
 import static android.app.StatusBarManager.windowStateToString;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
@@ -32,8 +31,6 @@
 import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
 import static com.android.systemui.charging.WirelessChargingLayout.UNKNOWN_BATTERY_LEVEL;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
-import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
-import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
 import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
@@ -54,6 +51,7 @@
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
 import android.app.TaskInfo;
+import android.app.TaskStackBuilder;
 import android.app.UiModeManager;
 import android.app.WallpaperInfo;
 import android.app.WallpaperManager;
@@ -69,17 +67,13 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.PointF;
-import android.media.AudioAttributes;
 import android.metrics.LogMaker;
 import android.net.Uri;
-import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
-import android.os.Message;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -87,8 +81,6 @@
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
-import android.os.VibrationEffect;
-import android.os.Vibrator;
 import android.provider.Settings;
 import android.service.dreams.DreamService;
 import android.service.dreams.IDreamManager;
@@ -103,8 +95,6 @@
 import android.view.Display;
 import android.view.IRemoteAnimationRunner;
 import android.view.IWindowManager;
-import android.view.InsetsState.InternalInsetsType;
-import android.view.InsetsVisibilities;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.RemoteAnimationAdapter;
@@ -112,7 +102,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowInsetsController.Appearance;
-import android.view.WindowInsetsController.Behavior;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityManager;
@@ -132,7 +121,6 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.RegisterStatusBarResult;
-import com.android.internal.view.AppearanceRegion;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.keyguard.ViewMediatorCallback;
@@ -145,22 +133,23 @@
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.SystemUI;
-import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
 import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.animation.DelegateLaunchAnimatorController;
 import com.android.systemui.assist.AssistManager;
+import com.android.systemui.battery.BatteryMeterViewController;
 import com.android.systemui.biometrics.AuthRippleController;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.camera.CameraIntents;
 import com.android.systemui.charging.WirelessChargingAnimation;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.demomode.DemoModeController;
 import com.android.systemui.emergency.EmergencyGesture;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.fragments.ExtensionFragmentListener;
 import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.keyguard.DismissCallbackRegistry;
 import com.android.systemui.keyguard.KeyguardService;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -187,7 +176,6 @@
 import com.android.systemui.statusbar.BackDropView;
 import com.android.systemui.statusbar.CircleReveal;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.GestureRecorder;
 import com.android.systemui.statusbar.KeyboardShortcuts;
 import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -202,19 +190,17 @@
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.NotificationShelfController;
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
+import com.android.systemui.statusbar.OperatorNameViewController;
 import com.android.systemui.statusbar.PowerButtonReveal;
 import com.android.systemui.statusbar.PulseExpansionHandler;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
 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.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.init.NotificationsController;
 import com.android.systemui.statusbar.notification.interruption.BypassHeadsUpNotifier;
@@ -237,14 +223,15 @@
 import com.android.systemui.statusbar.policy.ExtensionController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
-import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
 import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.tuner.TunerService;
 import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
+import com.android.systemui.unfold.config.UnfoldTransitionConfig;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.concurrency.MessageRouter;
 import com.android.systemui.volume.VolumeComponent;
 import com.android.systemui.wmshell.BubblesManager;
-import com.android.unfold.config.UnfoldTransitionConfig;
 import com.android.wm.shell.bubbles.Bubbles;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
 import com.android.wm.shell.startingsurface.SplashscreenContentDrawer;
@@ -260,28 +247,18 @@
 import java.util.concurrent.Executor;
 
 import javax.inject.Named;
-import javax.inject.Provider;
 
 import dagger.Lazy;
 
 /** */
 public class StatusBar extends SystemUI implements
-        ActivityStarter, KeyguardStateController.Callback,
-        OnHeadsUpChangedListener, CommandQueue.Callbacks,
-        ColorExtractor.OnColorsChangedListener, ConfigurationListener,
-        StatusBarStateController.StateListener,
-        LifecycleOwner, BatteryController.BatteryStateChangeCallback,
-        ActivityLaunchAnimator.Callback {
+        ActivityStarter,
+        LifecycleOwner {
     public static final boolean MULTIUSER_DEBUG = false;
 
-    protected static final int MSG_HIDE_RECENT_APPS = 1020;
-    protected static final int MSG_PRELOAD_RECENT_APPS = 1022;
-    protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
-    protected static final int MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU = 1026;
     protected static final int MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU = 1027;
 
     // Should match the values in PhoneWindowManager
-    public static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
     public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
     static public final String SYSTEM_DIALOG_REASON_SCREENSHOT = "screenshot";
 
@@ -306,14 +283,12 @@
 
     public static final String ACTION_FAKE_ARTWORK = "fake_artwork";
 
-    private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
-    private static final int MSG_CLOSE_PANELS = 1001;
     private static final int MSG_OPEN_SETTINGS_PANEL = 1002;
     private static final int MSG_LAUNCH_TRANSITION_TIMEOUT = 1003;
     // 1020-1040 reserved for BaseStatusBar
 
     // Time after we abort the launch transition.
-    private static final long LAUNCH_TRANSITION_TIMEOUT_MS = 5000;
+    static final long LAUNCH_TRANSITION_TIMEOUT_MS = 5000;
 
     protected static final boolean CLOSE_PANEL_WHEN_EMPTIED = true;
 
@@ -322,11 +297,6 @@
      */
     private static final int HINT_RESET_DELAY_MS = 1200;
 
-    private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
-            .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
-            .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
-            .build();
-
     public static final int FADE_KEYGUARD_START_DELAY = 100;
     public static final int FADE_KEYGUARD_DURATION = 300;
     public static final int FADE_KEYGUARD_DURATION_PULSING = 96;
@@ -352,14 +322,114 @@
         try {
             IPackageManager packageManager =
                     IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
-            onlyCoreApps = packageManager.isOnlyCoreApps();
+            onlyCoreApps = packageManager != null && packageManager.isOnlyCoreApps();
         } catch (RemoteException e) {
             onlyCoreApps = false;
         }
         ONLY_CORE_APPS = onlyCoreApps;
     }
 
-    private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+    private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+    private StatusBarCommandQueueCallbacks mCommandQueueCallbacks;
+
+    void setWindowState(int state) {
+        mStatusBarWindowState =  state;
+        mStatusBarWindowHidden = state == WINDOW_STATE_HIDDEN;
+    }
+
+    void acquireGestureWakeLock(long time) {
+        mGestureWakeLock.acquire(time);
+    }
+
+    boolean setAppearance(int appearance) {
+        if (mAppearance != appearance) {
+            mAppearance = appearance;
+            return updateBarMode(barMode(isTransientShown(), appearance));
+        }
+
+        return false;
+    }
+
+    int getBarMode() {
+        return mStatusBarMode;
+    }
+
+    boolean getWereIconsJustHidden() {
+        return mWereIconsJustHidden;
+    }
+
+    void setWereIconsJustHidden(boolean justHidden) {
+        mWereIconsJustHidden = justHidden;
+    }
+
+    void resendMessage(int msg) {
+        mMessageRouter.cancelMessages(msg);
+        mMessageRouter.sendMessage(msg);
+    }
+
+    void resendMessage(Object msg) {
+        mMessageRouter.cancelMessages(msg.getClass());
+        mMessageRouter.sendMessage(msg);
+    }
+
+    int getDisabled1() {
+        return mDisabled1;
+    }
+
+    void setDisabled1(int disabled) {
+        mDisabled1 = disabled;
+    }
+
+    int getDisabled2() {
+        return mDisabled2;
+    }
+
+    void setDisabled2(int disabled) {
+        mDisabled2 = disabled;
+    }
+
+    void setLastCameraLaunchSource(int source) {
+        mLastCameraLaunchSource = source;
+    }
+
+    void setLaunchCameraOnFinishedGoingToSleep(boolean launch) {
+        mLaunchCameraOnFinishedGoingToSleep = launch;
+    }
+
+    void setLaunchCameraOnFinishedWaking(boolean launch) {
+        mLaunchCameraWhenFinishedWaking = launch;
+    }
+
+    void setLaunchEmergencyActionOnFinishedGoingToSleep(boolean launch) {
+        mLaunchEmergencyActionOnFinishedGoingToSleep = launch;
+    }
+
+    void setLaunchEmergencyActionOnFinishedWaking(boolean launch) {
+        mLaunchEmergencyActionWhenFinishedWaking = launch;
+    }
+
+    void setTopHidesStatusBar(boolean hides) {
+        mTopHidesStatusBar = hides;
+    }
+
+    QSPanelController getQSPanelController() {
+        return mQSPanelController;
+    }
+
+    /** */
+    public void animateExpandNotificationsPanel() {
+        mCommandQueueCallbacks.animateExpandNotificationsPanel();
+    }
+
+    /** */
+    public void animateExpandSettingsPanel(@Nullable String subpanel) {
+        mCommandQueueCallbacks.animateExpandSettingsPanel(subpanel);
+    }
+
+    /** */
+    public void animateCollapsePanels(int flags, boolean force) {
+        mCommandQueueCallbacks.animateCollapsePanels(flags, force);
+    }
 
     public interface ExpansionChangedListener {
         void onExpansionChanged(float expansion, boolean expanded);
@@ -371,8 +441,7 @@
     protected int mState; // TODO: remove this. Just use StatusBarStateController
     protected boolean mBouncerShowing;
 
-    private PhoneStatusBarPolicy mIconPolicy;
-    private StatusBarSignalPolicy mSignalPolicy;
+    private final PhoneStatusBarPolicy mIconPolicy;
 
     private final VolumeComponent mVolumeComponent;
     private BrightnessMirrorController mBrightnessMirrorController;
@@ -380,17 +449,16 @@
     private BiometricUnlockController mBiometricUnlockController;
     private final LightBarController mLightBarController;
     private final Lazy<LockscreenWallpaper> mLockscreenWallpaperLazy;
+    private final LockscreenGestureLogger mLockscreenGestureLogger;
     @Nullable
     protected LockscreenWallpaper mLockscreenWallpaper;
     private final AutoHideController mAutoHideController;
-    @Nullable
-    private final KeyguardLiftController mKeyguardLiftController;
 
     private final Point mCurrentDisplaySize = new Point();
 
     protected NotificationShadeWindowView mNotificationShadeWindowView;
-    protected StatusBarWindowView mPhoneStatusBarWindow;
     protected PhoneStatusBarView mStatusBarView;
+    private PhoneStatusBarViewController mPhoneStatusBarViewController;
     private AuthRippleController mAuthRippleController;
     private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
     protected NotificationShadeWindowController mNotificationShadeWindowController;
@@ -401,7 +469,6 @@
     private boolean mWakeUpComingFromTouch;
     private PointF mWakeUpTouchLocation;
     private LightRevealScrim mLightRevealScrim;
-    private WiredChargingRippleController mChargingRippleAnimationController;
     private PowerButtonReveal mPowerButtonReveal;
 
     private final Object mQueueLock = new Object();
@@ -421,13 +488,13 @@
     protected NotificationShadeWindowViewController mNotificationShadeWindowViewController;
     private final DozeParameters mDozeParameters;
     private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
-    private final Provider<StatusBarComponent.Builder> mStatusBarComponentBuilder;
+    private final StatusBarComponent.Factory mStatusBarComponentFactory;
     private final PluginManager mPluginManager;
     private final Optional<LegacySplitScreen> mSplitScreenOptional;
     private final StatusBarNotificationActivityStarter.Builder
             mStatusBarNotificationActivityStarterBuilder;
     private final ShadeController mShadeController;
-    private final SuperStatusBarViewFactory mSuperStatusBarViewFactory;
+    private final StatusBarWindowView mStatusBarWindowView;
     private final LightsOutNotifController mLightsOutNotifController;
     private final InitController mInitController;
 
@@ -435,9 +502,8 @@
     private final KeyguardDismissUtil mKeyguardDismissUtil;
     private final ExtensionController mExtensionController;
     private final UserInfoControllerImpl mUserInfoControllerImpl;
-    private final DismissCallbackRegistry mDismissCallbackRegistry;
     private final DemoModeController mDemoModeController;
-    private NotificationsController mNotificationsController;
+    private final NotificationsController mNotificationsController;
     private final OngoingCallController mOngoingCallController;
     private final SystemStatusAnimationScheduler mAnimationScheduler;
     private final StatusBarLocationPublisher mStatusBarLocationPublisher;
@@ -450,16 +516,16 @@
     // settings
     private QSPanelController mQSPanelController;
 
+    private final OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
     KeyguardIndicationController mKeyguardIndicationController;
 
-    private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
-
     private View mReportRejectedTouch;
 
     private boolean mExpandedVisible;
 
     private final int[] mAbsPos = new int[2];
 
+    protected final NotificationEntryManager mEntryManager;
     private final NotificationGutsManager mGutsManager;
     private final NotificationLogger mNotificationLogger;
     private final NotificationViewHierarchyManager mViewHierarchyManager;
@@ -470,15 +536,19 @@
     private final UnfoldTransitionConfig mUnfoldTransitionConfig;
     private final Lazy<UnfoldLightRevealOverlayAnimation> mUnfoldLightRevealOverlayAnimation;
     private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
+    private final MessageRouter mMessageRouter;
+    private final WallpaperManager mWallpaperManager;
     private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
+    private final TunerService mTunerService;
 
     private final List<ExpansionChangedListener> mExpansionChangedListeners;
 
-    // for disabling the status bar
+    // Flags for disabling the status bar
+    // Two variables becaseu the first one evidently ran out of room for new flags.
     private int mDisabled1 = 0;
     private int mDisabled2 = 0;
 
-    /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int) */
+    /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */
     private @Appearance int mAppearance;
 
     private boolean mTransientShown;
@@ -497,29 +567,6 @@
     // ensure quick settings is disabled until the current user makes it through the setup wizard
     @VisibleForTesting
     protected boolean mUserSetup = false;
-    private final DeviceProvisionedListener mUserSetupObserver = new DeviceProvisionedListener() {
-        @Override
-        public void onUserSetupChanged() {
-            final boolean userSetup = mDeviceProvisionedController.isUserSetup(
-                    mDeviceProvisionedController.getCurrentUser());
-            Log.d(TAG, "mUserSetupObserver - DeviceProvisionedListener called for user "
-                    + mDeviceProvisionedController.getCurrentUser());
-            if (MULTIUSER_DEBUG) {
-                Log.d(TAG, String.format("User setup changed: userSetup=%s mUserSetup=%s",
-                        userSetup, mUserSetup));
-            }
-
-            if (userSetup != mUserSetup) {
-                mUserSetup = userSetup;
-                if (!mUserSetup && mStatusBarView != null)
-                    animateCollapseQuickSettings();
-                if (mNotificationPanelViewController != null) {
-                    mNotificationPanelViewController.setUserSetupComplete(mUserSetup);
-                }
-                updateQsExpansionEnabled();
-            }
-        }
-    };
 
     @VisibleForTesting
     public enum StatusBarUiEvent implements UiEventLogger.UiEventEnum {
@@ -559,12 +606,13 @@
         }
     }
 
-    protected final H mHandler = createHandler();
+    private Handler mMainHandler;
+    private final DelayableExecutor mMainExecutor;
 
     private int mInteractingWindows;
     private @TransitionMode int mStatusBarMode;
 
-    private ViewMediatorCallback mKeyguardViewMediatorCallback;
+    private final ViewMediatorCallback mKeyguardViewMediatorCallback;
     private final ScrimController mScrimController;
     protected DozeScrimController mDozeScrimController;
     private final Executor mUiBgExecutor;
@@ -576,46 +624,13 @@
     private final NotificationRemoteInputManager mRemoteInputManager;
     private boolean mWallpaperSupported;
 
-    private final BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (!mWallpaperSupported) {
-                // Receiver should not have been registered at all...
-                Log.wtf(TAG, "WallpaperManager not supported");
-                return;
-            }
-            WallpaperManager wallpaperManager = context.getSystemService(WallpaperManager.class);
-            WallpaperInfo info = wallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT);
-            final boolean deviceSupportsAodWallpaper = mContext.getResources().getBoolean(
-                    com.android.internal.R.bool.config_dozeSupportsAodWallpaper);
-            // If WallpaperInfo is null, it must be ImageWallpaper.
-            final boolean supportsAmbientMode = deviceSupportsAodWallpaper
-                    && (info != null && info.supportsAmbientMode());
-
-            mNotificationShadeWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
-            mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
-        }
-    };
-
-    BroadcastReceiver mTaskbarChangeReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (mBubblesOptional.isPresent()) {
-                mBubblesOptional.get().onTaskbarChanged(intent.getExtras());
-            }
-        }
-    };
-
     private Runnable mLaunchTransitionEndRunnable;
-    private NotificationEntry mDraggedDownEntry;
     private boolean mLaunchCameraWhenFinishedWaking;
     private boolean mLaunchCameraOnFinishedGoingToSleep;
     private boolean mLaunchEmergencyActionWhenFinishedWaking;
     private boolean mLaunchEmergencyActionOnFinishedGoingToSleep;
     private int mLastCameraLaunchSource;
     protected PowerManager.WakeLock mGestureWakeLock;
-    private Vibrator mVibrator;
-    private VibrationEffect mCameraLaunchGestureVibrationEffect;
 
     private final int[] mTmpInt2 = new int[2];
 
@@ -627,29 +642,7 @@
     private boolean mIsOccluded;
     private boolean mWereIconsJustHidden;
     private boolean mBouncerWasShowingWhenHidden;
-
-    // Notifies StatusBarKeyguardViewManager every time the keyguard transition is over,
-    // this animation is tied to the scrim for historic reasons.
-    // TODO: notify when keyguard has faded away instead of the scrim.
-    private final ScrimController.Callback mUnlockScrimCallback = new ScrimController
-            .Callback() {
-        @Override
-        public void onFinished() {
-            if (mStatusBarKeyguardViewManager == null) {
-                Log.w(TAG, "Tried to notify keyguard visibility when "
-                        + "mStatusBarKeyguardViewManager was null");
-                return;
-            }
-            if (mKeyguardStateController.isKeyguardFadingAway()) {
-                mStatusBarKeyguardViewManager.onKeyguardFadedAway();
-            }
-        }
-
-        @Override
-        public void onCancelled() {
-            onFinished();
-        }
-    };
+    private boolean mIsLaunchingActivityOverLockscreen;
 
     private final UserSwitcherController mUserSwitcherController;
     private final NetworkController mNetworkController;
@@ -668,51 +661,24 @@
     private boolean mNoAnimationOnNextBarModeChange;
     private final SysuiStatusBarStateController mStatusBarStateController;
 
-    private final KeyguardUpdateMonitorCallback mUpdateCallback =
-            new KeyguardUpdateMonitorCallback() {
-                @Override
-                public void onDreamingStateChanged(boolean dreaming) {
-                    if (dreaming) {
-                        maybeEscalateHeadsUp();
-                    }
-                }
-
-                // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by
-                //  KeyguardCoordinator
-                @Override
-                public void onStrongAuthStateChanged(int userId) {
-                    super.onStrongAuthStateChanged(userId);
-                    mNotificationsController.requestNotificationUpdate("onStrongAuthStateChanged");
-                }
-            };
-
-
-    private final FalsingManager.FalsingBeliefListener mFalsingBeliefListener =
-            new FalsingManager.FalsingBeliefListener() {
-                @Override
-                public void onFalse() {
-                    // Hides quick settings, bouncer, and quick-quick settings.
-                    mStatusBarKeyguardViewManager.reset(true);
-                }
-            };
-
-    private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
-
     private HeadsUpAppearanceController mHeadsUpAppearanceController;
-    private boolean mVibrateOnOpening;
-    private final VibratorHelper mVibratorHelper;
     private ActivityLaunchAnimator mActivityLaunchAnimator;
     private NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
     protected StatusBarNotificationPresenter mPresenter;
     private NotificationActivityStarter mNotificationActivityStarter;
-    private Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy;
+    private final Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy;
     private final Optional<BubblesManager> mBubblesManagerOptional;
     private final Optional<Bubbles> mBubblesOptional;
     private final Bubbles.BubbleExpandListener mBubbleExpandListener;
     private final Optional<StartingSurface> mStartingSurfaceOptional;
 
-    private ActivityIntentHelper mActivityIntentHelper;
+    private final ActivityIntentHelper mActivityIntentHelper;
     private NotificationStackScrollLayoutController mStackScrollerController;
+    private BatteryMeterViewController mBatteryMeterViewController;
+
+    private final ColorExtractor.OnColorsChangedListener mOnColorsChangedListener =
+            (extractor, which) -> updateTheme();
+
 
     /**
      * Public constructor for StatusBar.
@@ -727,7 +693,6 @@
             LightBarController lightBarController,
             AutoHideController autoHideController,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
-            StatusBarSignalPolicy signalPolicy,
             PulseExpansionHandler pulseExpansionHandler,
             NotificationWakeUpCoordinator notificationWakeUpCoordinator,
             KeyguardBypassController keyguardBypassController,
@@ -738,7 +703,7 @@
             FalsingManager falsingManager,
             FalsingCollector falsingCollector,
             BroadcastDispatcher broadcastDispatcher,
-            RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
+            NotificationEntryManager notificationEntryManager,
             NotificationGutsManager notificationGutsManager,
             NotificationLogger notificationLogger,
             NotificationInterruptStateProvider notificationInterruptStateProvider,
@@ -757,20 +722,18 @@
             ScreenLifecycle screenLifecycle,
             WakefulnessLifecycle wakefulnessLifecycle,
             SysuiStatusBarStateController statusBarStateController,
-            VibratorHelper vibratorHelper,
             Optional<BubblesManager> bubblesManagerOptional,
             Optional<Bubbles> bubblesOptional,
             VisualStabilityManager visualStabilityManager,
             DeviceProvisionedController deviceProvisionedController,
             NavigationBarController navigationBarController,
-            AccessibilityFloatingMenuController accessibilityFloatingMenuController,
             Lazy<AssistManager> assistManagerLazy,
             ConfigurationController configurationController,
             NotificationShadeWindowController notificationShadeWindowController,
             DozeParameters dozeParameters,
             ScrimController scrimController,
-            @Nullable KeyguardLiftController keyguardLiftController,
             Lazy<LockscreenWallpaper> lockscreenWallpaperLazy,
+            LockscreenGestureLogger lockscreenGestureLogger,
             Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
             DozeServiceHost dozeServiceHost,
             PowerManager powerManager,
@@ -778,14 +741,14 @@
             DozeScrimController dozeScrimController,
             VolumeComponent volumeComponent,
             CommandQueue commandQueue,
-            Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
+            StatusBarComponent.Factory statusBarComponentFactory,
             PluginManager pluginManager,
             Optional<LegacySplitScreen> splitScreenOptional,
             LightsOutNotifController lightsOutNotifController,
             StatusBarNotificationActivityStarter.Builder
                     statusBarNotificationActivityStarterBuilder,
             ShadeController shadeController,
-            SuperStatusBarViewFactory superStatusBarViewFactory,
+            StatusBarWindowView statusBarWindowView,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             ViewMediatorCallback viewMediatorCallback,
             InitController initController,
@@ -794,15 +757,14 @@
             KeyguardDismissUtil keyguardDismissUtil,
             ExtensionController extensionController,
             UserInfoControllerImpl userInfoControllerImpl,
+            OperatorNameViewController.Factory operatorNameViewControllerFactory,
             PhoneStatusBarPolicy phoneStatusBarPolicy,
             KeyguardIndicationController keyguardIndicationController,
-            DismissCallbackRegistry dismissCallbackRegistry,
             DemoModeController demoModeController,
             Lazy<NotificationShadeDepthController> notificationShadeDepthControllerLazy,
             StatusBarTouchableRegionManager statusBarTouchableRegionManager,
             NotificationIconAreaController notificationIconAreaController,
             BrightnessSlider.Factory brightnessSliderFactory,
-            WiredChargingRippleController chargingRippleAnimationController,
             UnfoldTransitionConfig unfoldTransitionConfig,
             Lazy<UnfoldLightRevealOverlayAnimation> unfoldLightRevealOverlayAnimation,
             OngoingCallController ongoingCallController,
@@ -812,19 +774,24 @@
             LockscreenShadeTransitionController lockscreenShadeTransitionController,
             FeatureFlags featureFlags,
             KeyguardUnlockAnimationController keyguardUnlockAnimationController,
+            @Main Handler mainHandler,
+            @Main DelayableExecutor delayableExecutor,
+            @Main MessageRouter messageRouter,
+            WallpaperManager wallpaperManager,
             UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
-            Optional<StartingSurface> startingSurfaceOptional) {
+            Optional<StartingSurface> startingSurfaceOptional,
+            TunerService tunerService) {
         super(context);
         mNotificationsController = notificationsController;
         mLightBarController = lightBarController;
         mAutoHideController = autoHideController;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
-        mSignalPolicy = signalPolicy;
         mPulseExpansionHandler = pulseExpansionHandler;
         mWakeUpCoordinator = notificationWakeUpCoordinator;
         mKeyguardBypassController = keyguardBypassController;
         mKeyguardStateController = keyguardStateController;
         mHeadsUpManager = headsUpManagerPhone;
+        mOperatorNameViewControllerFactory = operatorNameViewControllerFactory;
         mKeyguardIndicationController = keyguardIndicationController;
         mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
         mDynamicPrivacyController = dynamicPrivacyController;
@@ -832,7 +799,7 @@
         mFalsingCollector = falsingCollector;
         mFalsingManager = falsingManager;
         mBroadcastDispatcher = broadcastDispatcher;
-        mRemoteInputQuickSettingsDisabler = remoteInputQuickSettingsDisabler;
+        mEntryManager = notificationEntryManager;
         mGutsManager = notificationGutsManager;
         mNotificationLogger = notificationLogger;
         mNotificationInterruptStateProvider = notificationInterruptStateProvider;
@@ -851,7 +818,6 @@
         mScreenLifecycle = screenLifecycle;
         mWakefulnessLifecycle = wakefulnessLifecycle;
         mStatusBarStateController = statusBarStateController;
-        mVibratorHelper = vibratorHelper;
         mBubblesManagerOptional = bubblesManagerOptional;
         mBubblesOptional = bubblesOptional;
         mVisualStabilityManager = visualStabilityManager;
@@ -864,20 +830,20 @@
         mPowerManager = powerManager;
         mDozeParameters = dozeParameters;
         mScrimController = scrimController;
-        mKeyguardLiftController = keyguardLiftController;
         mLockscreenWallpaperLazy = lockscreenWallpaperLazy;
+        mLockscreenGestureLogger = lockscreenGestureLogger;
         mScreenPinningRequest = screenPinningRequest;
         mDozeScrimController = dozeScrimController;
         mBiometricUnlockControllerLazy = biometricUnlockControllerLazy;
         mNotificationShadeDepthControllerLazy = notificationShadeDepthControllerLazy;
         mVolumeComponent = volumeComponent;
         mCommandQueue = commandQueue;
-        mStatusBarComponentBuilder = statusBarComponentBuilder;
+        mStatusBarComponentFactory = statusBarComponentFactory;
         mPluginManager = pluginManager;
         mSplitScreenOptional = splitScreenOptional;
         mStatusBarNotificationActivityStarterBuilder = statusBarNotificationActivityStarterBuilder;
         mShadeController = shadeController;
-        mSuperStatusBarViewFactory = superStatusBarViewFactory;
+        mStatusBarWindowView = statusBarWindowView;
         mLightsOutNotifController =  lightsOutNotifController;
         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
         mKeyguardViewMediatorCallback = viewMediatorCallback;
@@ -887,11 +853,9 @@
         mExtensionController = extensionController;
         mUserInfoControllerImpl = userInfoControllerImpl;
         mIconPolicy = phoneStatusBarPolicy;
-        mDismissCallbackRegistry = dismissCallbackRegistry;
         mDemoModeController = demoModeController;
         mNotificationIconAreaController = notificationIconAreaController;
         mBrightnessSliderFactory = brightnessSliderFactory;
-        mChargingRippleAnimationController = chargingRippleAnimationController;
         mUnfoldTransitionConfig = unfoldTransitionConfig;
         mUnfoldLightRevealOverlayAnimation = unfoldLightRevealOverlayAnimation;
         mOngoingCallController = ongoingCallController;
@@ -900,7 +864,12 @@
         mStatusBarIconController = statusBarIconController;
         mFeatureFlags = featureFlags;
         mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
+        mMainHandler = mainHandler;
+        mMainExecutor = delayableExecutor;
+        mMessageRouter = messageRouter;
+        mWallpaperManager = wallpaperManager;
         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
+        mTunerService = tunerService;
 
         mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
         mStartingSurfaceOptional = startingSurfaceOptional;
@@ -909,15 +878,24 @@
         mExpansionChangedListeners = new ArrayList<>();
 
         mBubbleExpandListener =
-                (isExpanding, key) -> {
-                    mContext.getMainExecutor().execute(() -> {
-                        mNotificationsController.requestNotificationUpdate("onBubbleExpandChanged");
-                        updateScrimController();
-                    });
-                };
+                (isExpanding, key) -> mContext.getMainExecutor().execute(() -> {
+                    mNotificationsController.requestNotificationUpdate("onBubbleExpandChanged");
+                    updateScrimController();
+                });
 
         mActivityIntentHelper = new ActivityIntentHelper(mContext);
+
+        // TODO(b/190746471): Find a better home for this.
         DateTimeView.setReceiverHandler(timeTickHandler);
+
+        mMessageRouter.subscribeTo(KeyboardShortcutsMessage.class,
+                data -> toggleKeyboardShortcuts(data.mDeviceId));
+        mMessageRouter.subscribeTo(MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU,
+                id -> dismissKeyboardShortcuts());
+        mMessageRouter.subscribeTo(AnimateExpandSettingsPanelMessage.class,
+                data -> mCommandQueueCallbacks.animateExpandSettingsPanel(data.mSubpanel));
+        mMessageRouter.subscribeTo(MSG_LAUNCH_TRANSITION_TIMEOUT,
+                id -> onLaunchTransitionTimeout());
     }
 
     @Override
@@ -934,21 +912,18 @@
 
         mKeyguardIndicationController.init();
 
-        mColorExtractor.addOnColorsChangedListener(this);
-        mStatusBarStateController.addCallback(this,
+        mColorExtractor.addOnColorsChangedListener(mOnColorsChangedListener);
+        mStatusBarStateController.addCallback(mStateListener,
                 SysuiStatusBarStateController.RANK_STATUS_BAR);
 
         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
         mDreamManager = IDreamManager.Stub.asInterface(
                 ServiceManager.checkService(DreamService.DREAM_SERVICE));
 
-        mDisplay = mWindowManager.getDefaultDisplay();
+        mDisplay = mContext.getDisplay();
         mDisplayId = mDisplay.getDisplayId();
         updateDisplaySize();
 
-        mVibrateOnOpening = mContext.getResources().getBoolean(
-                R.bool.config_vibrateOnIconAnimation);
-
         // start old BaseStatusBar.start().
         mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
         mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
@@ -962,12 +937,7 @@
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
 
         mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
-        mWallpaperSupported =
-                mContext.getSystemService(WallpaperManager.class).isWallpaperSupported();
-
-        // Connect in to the status bar manager service
-        mCommandQueue.addCallback(this);
-
+        mWallpaperSupported = mWallpaperManager.isWallpaperSupported();
 
         RegisterStatusBarResult result = null;
         try {
@@ -994,13 +964,13 @@
         if (containsType(result.mTransientBarTypes, ITYPE_STATUS_BAR)) {
             showTransientUnchecked();
         }
-        onSystemBarAttributesChanged(mDisplayId, result.mAppearance, result.mAppearanceRegions,
-                result.mNavbarColorManagedByIme, result.mBehavior, result.mRequestedVisibilities,
-                result.mPackageName);
+        mCommandQueueCallbacks.onSystemBarAttributesChanged(mDisplayId, result.mAppearance,
+                result.mAppearanceRegions, result.mNavbarColorManagedByIme, result.mBehavior,
+                result.mRequestedVisibilities, result.mPackageName);
 
         // StatusBarManagerService has a back up of IME token and it's restored here.
-        setImeWindowStatus(mDisplayId, result.mImeToken, result.mImeWindowVis,
-                result.mImeBackDisposition, result.mShowImeSwitcher);
+        mCommandQueueCallbacks.setImeWindowStatus(mDisplayId, result.mImeToken,
+                result.mImeWindowVis, result.mImeBackDisposition, result.mShowImeSwitcher);
 
         // Set up the initial icon state
         int numIcons = result.mIcons.size();
@@ -1039,7 +1009,13 @@
         // Lastly, call to the icon policy to install/update all the icons.
         mIconPolicy.init();
 
-        mKeyguardStateController.addCallback(this);
+        mKeyguardStateController.addCallback(new KeyguardStateController.Callback() {
+            @Override
+            public void onUnlockedChanged() {
+                updateKeyguardState();
+                logStateToEventlog();
+            }
+        });
         startKeyguard();
 
         mKeyguardUpdateMonitor.registerCallback(mUpdateCallback);
@@ -1049,11 +1025,11 @@
                 mNotificationShadeWindowViewController,
                 mNotificationPanelViewController,
                 mAmbientIndicationContainer);
-        mDozeParameters.addCallback(this::updateLightRevealScrimVisibility);
+        updateLightRevealScrimVisibility();
 
-        mConfigurationController.addCallback(this);
+        mConfigurationController.addCallback(mConfigurationListener);
 
-        mBatteryController.observe(mLifecycle, this);
+        mBatteryController.observe(mLifecycle, mBatteryStateChangeCallback);
         mLifecycle.setCurrentState(RESUMED);
 
         // set the initial view visibility
@@ -1070,11 +1046,11 @@
 
         mPluginManager.addPluginListener(
                 new PluginListener<OverlayPlugin>() {
-                    private ArraySet<OverlayPlugin> mOverlays = new ArraySet<>();
+                    private final ArraySet<OverlayPlugin> mOverlays = new ArraySet<>();
 
                     @Override
                     public void onPluginConnected(OverlayPlugin plugin, Context pluginContext) {
-                        mMainThreadHandler.post(
+                        mMainExecutor.execute(
                                 () -> plugin.setup(getNotificationShadeWindowView(),
                                         getNavigationBarView(),
                                         new Callback(plugin), mDozeParameters));
@@ -1082,7 +1058,7 @@
 
                     @Override
                     public void onPluginDisconnected(OverlayPlugin plugin) {
-                        mMainThreadHandler.post(() -> {
+                        mMainExecutor.execute(() -> {
                             mOverlays.remove(plugin);
                             mNotificationShadeWindowController
                                     .setForcePluginOpen(mOverlays.size() != 0, this);
@@ -1103,7 +1079,7 @@
                             } else {
                                 mOverlays.remove(mPlugin);
                             }
-                            mMainThreadHandler.post(() -> {
+                            mMainExecutor.execute(() -> {
                                 mNotificationShadeWindowController
                                         .setStateListener(b -> mOverlays.forEach(
                                                 o -> o.setCollapseDesired(b)));
@@ -1119,7 +1095,6 @@
     // Constructing the view
     // ================================================================================
     protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
-        final Context context = mContext;
         updateDisplaySize(); // populates mDisplayMetrics
         updateResources();
         updateTheme();
@@ -1130,14 +1105,10 @@
 
         // TODO: Deal with the ugliness that comes from having some of the statusbar broken out
         // into fragments, but the rest here, it leaves some awkward lifecycle and whatnot.
-        mStackScrollerController =
-                mNotificationPanelViewController.getNotificationStackScrollLayoutController();
-        mStackScroller = mStackScrollerController.getView();
         NotificationListContainer notifListContainer =
                 mStackScrollerController.getNotificationListContainer();
         mNotificationLogger.setUpWithContainer(notifListContainer);
 
-        inflateShelf();
         mNotificationIconAreaController.setupShelf(mNotificationShelfController);
         mNotificationPanelViewController.addExpansionListener(mWakeUpCoordinator);
         mNotificationPanelViewController.addExpansionListener(
@@ -1146,7 +1117,7 @@
         // Allow plugins to reference DarkIconDispatcher and StatusBarStateController
         mPluginDependencyProvider.allowPluginDependency(DarkIconDispatcher.class);
         mPluginDependencyProvider.allowPluginDependency(StatusBarStateController.class);
-        FragmentHostManager.get(mPhoneStatusBarWindow)
+        FragmentHostManager.get(mStatusBarWindowView)
                 .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
                     CollapsedStatusBarFragment statusBarFragment =
                             (CollapsedStatusBarFragment) fragment;
@@ -1157,6 +1128,24 @@
                     mStatusBarView.setPanel(mNotificationPanelViewController);
                     mStatusBarView.setScrimController(mScrimController);
                     mStatusBarView.setExpansionChangedListeners(mExpansionChangedListeners);
+                    for (ExpansionChangedListener listener : mExpansionChangedListeners) {
+                        sendInitialExpansionAmount(listener);
+                    }
+
+                    mPhoneStatusBarViewController =
+                            new PhoneStatusBarViewController(mStatusBarView, mCommandQueue);
+                    mPhoneStatusBarViewController.init();
+
+                    mBatteryMeterViewController = new BatteryMeterViewController(
+                            mStatusBarView.findViewById(R.id.battery),
+                            mConfigurationController,
+                            mTunerService,
+                            mBroadcastDispatcher,
+                            mMainHandler,
+                            mContext.getContentResolver(),
+                            mBatteryController
+                    );
+                    mBatteryMeterViewController.init();
 
                     // CollapsedStatusBarFragment re-inflated PhoneStatusBarView and both of
                     // mStatusBarView.mExpanded and mStatusBarView.mBouncerShowing are false.
@@ -1208,14 +1197,14 @@
                                 mKeyguardStateController,
                                 mNetworkController,
                                 mStatusBarStateController,
-                                mCommandQueue
+                                mCommandQueue,
+                                mOperatorNameViewControllerFactory
                         ),
                         CollapsedStatusBarFragment.TAG)
                 .commit();
 
         mHeadsUpManager.setup(mVisualStabilityManager);
         mStatusBarTouchableRegionManager.setup(this, mNotificationShadeWindowView);
-        mHeadsUpManager.addListener(this);
         mHeadsUpManager.addListener(mNotificationPanelViewController.getOnHeadsUpChangedListener());
         mHeadsUpManager.addListener(mVisualStabilityManager);
         mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
@@ -1258,17 +1247,26 @@
         ScrimView notificationsScrim = mNotificationShadeWindowView
                 .findViewById(R.id.scrim_notifications);
         ScrimView scrimInFront = mNotificationShadeWindowView.findViewById(R.id.scrim_in_front);
-        ScrimView scrimForBubble = mBubblesManagerOptional.isPresent()
-                ? mBubblesManagerOptional.get().getScrimForBubble() : null;
 
         mScrimController.setScrimVisibleListener(scrimsVisible -> {
             mNotificationShadeWindowController.setScrimsVisibility(scrimsVisible);
         });
-        mScrimController.attachViews(scrimBehind, notificationsScrim, scrimInFront, scrimForBubble);
+        mScrimController.attachViews(scrimBehind, notificationsScrim, scrimInFront);
 
         mLightRevealScrim = mNotificationShadeWindowView.findViewById(R.id.light_reveal_scrim);
-        mLightRevealScrim.setRevealAmountListener(
-                mNotificationShadeWindowController::setLightRevealScrimAmount);
+        mLightRevealScrim.setScrimOpaqueChangedListener((opaque) -> {
+            Runnable updateOpaqueness = () -> {
+                mNotificationShadeWindowController.setLightRevealScrimOpaque(
+                        mLightRevealScrim.isScrimOpaque());
+            };
+            if (opaque) {
+                // Delay making the view opaque for a frame, because it needs some time to render
+                // otherwise this can lead to a flicker where the scrim doesn't cover the screen
+                mLightRevealScrim.post(updateOpaqueness);
+            } else {
+                updateOpaqueness.run();
+            }
+        });
         mUnlockedScreenOffAnimationController.initialize(this, mLightRevealScrim);
         updateLightRevealScrimVisibility();
 
@@ -1314,7 +1312,7 @@
                 QS qs = (QS) f;
                 if (qs instanceof QSFragment) {
                     mQSPanelController = ((QSFragment) qs).getQSPanelController();
-                    mQSPanelController.setBrightnessMirror(mBrightnessMirrorController);
+                    ((QSFragment) qs).setBrightnessMirrorController(mBrightnessMirrorController);
                 }
             });
         }
@@ -1345,14 +1343,11 @@
             });
         }
 
-        if (!mPowerManager.isScreenOn()) {
+        if (!mPowerManager.isInteractive()) {
             mBroadcastReceiver.onReceive(mContext, new Intent(Intent.ACTION_SCREEN_OFF));
         }
         mGestureWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
-                "GestureWakeLock");
-        mVibrator = mContext.getSystemService(Vibrator.class);
-        mCameraLaunchGestureVibrationEffect = getCameraGestureVibrationEffect(
-                mVibrator, context.getResources());
+                "sysui:GestureWakeLock");
 
         // receive broadcasts
         registerBroadcastReceiver();
@@ -1361,7 +1356,7 @@
         if (DEBUG_MEDIA_FAKE_ARTWORK) {
             demoFilter.addAction(ACTION_FAKE_ARTWORK);
         }
-        context.registerReceiverAsUser(mDemoReceiver, UserHandle.ALL, demoFilter,
+        mContext.registerReceiverAsUser(mDemoReceiver, UserHandle.ALL, demoFilter,
                 android.Manifest.permission.DUMP, null);
 
         // listen for USER_SETUP_COMPLETE setting (per-user)
@@ -1414,19 +1409,6 @@
         return mLifecycle;
     }
 
-    @Override
-    public void onPowerSaveChanged(boolean isPowerSave) {
-        mHandler.post(mCheckBarModes);
-        if (mDozeServiceHost != null) {
-            mDozeServiceHost.firePowerSaveChanged(isPowerSave);
-        }
-    }
-
-    @Override
-    public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
-        // noop
-    }
-
     @VisibleForTesting
     protected void registerBroadcastReceiver() {
         IntentFilter filter = new IntentFilter();
@@ -1442,7 +1424,7 @@
 
     private void setUpPresenter() {
         // Set up the initial notification state.
-        mActivityLaunchAnimator = new ActivityLaunchAnimator(this, mContext);
+        mActivityLaunchAnimator = new ActivityLaunchAnimator(mKeyguardHandler, mContext);
         mNotificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider(
                 mNotificationShadeWindowViewController,
                 mStackScrollerController.getNotificationListContainer(),
@@ -1450,14 +1432,34 @@
         );
 
         // TODO: inject this.
-        mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanelViewController,
-                mHeadsUpManager, mNotificationShadeWindowView, mStackScrollerController,
-                mDozeScrimController, mScrimController, mNotificationShadeWindowController,
-                mDynamicPrivacyController, mKeyguardStateController,
+        mPresenter = new StatusBarNotificationPresenter(
+                mContext,
+                mNotificationPanelViewController,
+                mHeadsUpManager,
+                mNotificationShadeWindowView,
+                mStackScrollerController,
+                mDozeScrimController,
+                mScrimController,
+                mNotificationShadeWindowController,
+                mDynamicPrivacyController,
+                mKeyguardStateController,
                 mKeyguardIndicationController,
-                this /* statusBar */, mShadeController,
-                mLockscreenShadeTransitionController, mCommandQueue, mInitController,
-                mNotificationInterruptStateProvider);
+                this /* statusBar */,
+                mShadeController,
+                mLockscreenShadeTransitionController,
+                mCommandQueue,
+                mViewHierarchyManager,
+                mLockscreenUserManager,
+                mStatusBarStateController,
+                mEntryManager,
+                mMediaManager,
+                mGutsManager,
+                mKeyguardUpdateMonitor,
+                mLockscreenGestureLogger,
+                mInitController,
+                mNotificationInterruptStateProvider,
+                mRemoteInputManager,
+                mConfigurationController);
 
         mNotificationShelfController.setOnActivatedListener(mPresenter);
         mRemoteInputManager.addControllerCallback(mNotificationShadeWindowController);
@@ -1533,71 +1535,36 @@
         };
     }
 
-    private void inflateShelf() {
-        mNotificationShelfController = mSuperStatusBarViewFactory
-                .getNotificationShelfController(mStackScroller);
-    }
-
-    @Override
-    public void onDensityOrFontScaleChanged() {
-        // TODO: Remove this.
-        if (mBrightnessMirrorController != null) {
-            mBrightnessMirrorController.onDensityOrFontScaleChanged();
-        }
-        // TODO: Bring these out of StatusBar.
-        mUserInfoControllerImpl.onDensityOrFontScaleChanged();
-        mUserSwitcherController.onDensityOrFontScaleChanged();
-        mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext);
-        mHeadsUpManager.onDensityOrFontScaleChanged();
-    }
-
-    @Override
-    public void onThemeChanged() {
-        if (mStatusBarKeyguardViewManager != null) {
-            mStatusBarKeyguardViewManager.onThemeChanged();
-        }
-        if (mAmbientIndicationContainer instanceof AutoReinflateContainer) {
-            ((AutoReinflateContainer) mAmbientIndicationContainer).inflateLayout();
-        }
-        mNotificationIconAreaController.onThemeChanged();
-    }
-
-    @Override
-    public void onOverlayChanged() {
-        if (mBrightnessMirrorController != null) {
-            mBrightnessMirrorController.onOverlayChanged();
-        }
-        // We need the new R.id.keyguard_indication_area before recreating
-        // mKeyguardIndicationController
-        mNotificationPanelViewController.onThemeChanged();
-        onThemeChanged();
-    }
-
-    @Override
-    public void onUiModeChanged() {
-        if (mBrightnessMirrorController != null) {
-            mBrightnessMirrorController.onUiModeChanged();
-        }
-    }
-
     private void inflateStatusBarWindow() {
-        mNotificationShadeWindowView = mSuperStatusBarViewFactory.getNotificationShadeWindowView();
-        StatusBarComponent statusBarComponent = mStatusBarComponentBuilder.get()
-                .statusBarWindowView(mNotificationShadeWindowView).build();
+        StatusBarComponent statusBarComponent = mStatusBarComponentFactory.create();
+        mNotificationShadeWindowView = statusBarComponent.getNotificationShadeWindowView();
         mNotificationShadeWindowViewController = statusBarComponent
                 .getNotificationShadeWindowViewController();
         mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
         mNotificationShadeWindowViewController.setupExpandedStatusBar();
         mStatusBarWindowController = statusBarComponent.getStatusBarWindowController();
-        mPhoneStatusBarWindow = mSuperStatusBarViewFactory.getStatusBarWindowView();
         mNotificationPanelViewController = statusBarComponent.getNotificationPanelViewController();
         statusBarComponent.getLockIconViewController().init();
+        mStackScrollerController = statusBarComponent.getNotificationStackScrollLayoutController();
+        mStackScroller = mStackScrollerController.getView();
 
+        mNotificationShelfController = statusBarComponent.getNotificationShelfController();
         mAuthRippleController = statusBarComponent.getAuthRippleController();
         mAuthRippleController.init();
 
+        mHeadsUpManager.addListener(statusBarComponent.getStatusBarHeadsUpChangeListener());
+
+        mHeadsUpManager.addListener(statusBarComponent.getStatusBarHeadsUpChangeListener());
+
         // Listen for demo mode changes
         mDemoModeController.addCallback(statusBarComponent.getStatusBarDemoMode());
+
+        if (mCommandQueueCallbacks != null) {
+            mCommandQueue.removeCallback(mCommandQueueCallbacks);
+        }
+        mCommandQueueCallbacks = statusBarComponent.getStatusBarCommandQueueCallbacks();
+        // Connect in to the status bar manager service
+        mCommandQueue.addCallback(mCommandQueueCallbacks);
     }
 
     protected void startKeyguard() {
@@ -1647,7 +1614,7 @@
         Trace.endSection();
     }
 
-    protected View getStatusBarView() {
+    protected PhoneStatusBarView getStatusBarView() {
         return mStatusBarView;
     }
 
@@ -1656,7 +1623,7 @@
     }
 
     public StatusBarWindowView getStatusBarWindow() {
-        return mPhoneStatusBarWindow;
+        return mStatusBarWindowView;
     }
 
     public NotificationShadeWindowViewController getNotificationShadeWindowViewController() {
@@ -1668,7 +1635,7 @@
     }
 
     protected ViewGroup getBouncerContainer() {
-        return mNotificationShadeWindowView;
+        return mNotificationShadeWindowView.findViewById(R.id.keyboard_bouncer_container);
     }
 
     public int getStatusBarHeight() {
@@ -1709,7 +1676,7 @@
      * If the user switcher is simple then disable QS during setup because
      * the user intends to use the lock screen user switcher, QS in not needed.
      */
-    private void updateQsExpansionEnabled() {
+    void updateQsExpansionEnabled() {
         final boolean expandEnabled = mDeviceProvisionedController.isDeviceProvisioned()
                 && (mUserSetup || mUserSwitcherController == null
                         || !mUserSwitcherController.isSimpleUserSwitcher())
@@ -1725,22 +1692,6 @@
         return (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0;
     }
 
-    public void addQsTile(ComponentName tile) {
-        if (mQSPanelController != null && mQSPanelController.getHost() != null) {
-            mQSPanelController.getHost().addTile(tile);
-        }
-    }
-
-    public void remQsTile(ComponentName tile) {
-        if (mQSPanelController != null && mQSPanelController.getHost() != null) {
-            mQSPanelController.getHost().removeTile(tile);
-        }
-    }
-
-    public void clickTile(ComponentName tile) {
-        mQSPanelController.clickTile(tile);
-    }
-
     /**
      * Request a notification update
      * @param reason why we're requesting a notification update
@@ -1766,102 +1717,10 @@
                 && mFalsingCollector.isReportingEnabled() ? View.VISIBLE : View.INVISIBLE);
     }
 
-    /**
-     * State is one or more of the DISABLE constants from StatusBarManager.
-     */
-    @Override
-    public void disable(int displayId, int state1, int state2, boolean animate) {
-        if (displayId != mDisplayId) {
-            return;
-        }
-        state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2);
-
-        animate &= mStatusBarWindowState != WINDOW_STATE_HIDDEN;
-        final int old1 = mDisabled1;
-        final int diff1 = state1 ^ old1;
-        mDisabled1 = state1;
-
-        final int old2 = mDisabled2;
-        final int diff2 = state2 ^ old2;
-        mDisabled2 = state2;
-
-        if (DEBUG) {
-            Log.d(TAG, String.format("disable1: 0x%08x -> 0x%08x (diff1: 0x%08x)",
-                old1, state1, diff1));
-            Log.d(TAG, String.format("disable2: 0x%08x -> 0x%08x (diff2: 0x%08x)",
-                old2, state2, diff2));
-        }
-
-        StringBuilder flagdbg = new StringBuilder();
-        flagdbg.append("disable<");
-        flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_EXPAND))                ? 'E' : 'e');
-        flagdbg.append(0 != ((diff1  & StatusBarManager.DISABLE_EXPAND))                ? '!' : ' ');
-        flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS))    ? 'I' : 'i');
-        flagdbg.append(0 != ((diff1  & StatusBarManager.DISABLE_NOTIFICATION_ICONS))    ? '!' : ' ');
-        flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS))   ? 'A' : 'a');
-        flagdbg.append(0 != ((diff1  & StatusBarManager.DISABLE_NOTIFICATION_ALERTS))   ? '!' : ' ');
-        flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_SYSTEM_INFO))           ? 'S' : 's');
-        flagdbg.append(0 != ((diff1  & StatusBarManager.DISABLE_SYSTEM_INFO))           ? '!' : ' ');
-        flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_BACK))                  ? 'B' : 'b');
-        flagdbg.append(0 != ((diff1  & StatusBarManager.DISABLE_BACK))                  ? '!' : ' ');
-        flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_HOME))                  ? 'H' : 'h');
-        flagdbg.append(0 != ((diff1  & StatusBarManager.DISABLE_HOME))                  ? '!' : ' ');
-        flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_RECENT))                ? 'R' : 'r');
-        flagdbg.append(0 != ((diff1  & StatusBarManager.DISABLE_RECENT))                ? '!' : ' ');
-        flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_CLOCK))                 ? 'C' : 'c');
-        flagdbg.append(0 != ((diff1  & StatusBarManager.DISABLE_CLOCK))                 ? '!' : ' ');
-        flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_SEARCH))                ? 'S' : 's');
-        flagdbg.append(0 != ((diff1  & StatusBarManager.DISABLE_SEARCH))                ? '!' : ' ');
-        flagdbg.append("> disable2<");
-        flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_QUICK_SETTINGS))       ? 'Q' : 'q');
-        flagdbg.append(0 != ((diff2  & StatusBarManager.DISABLE2_QUICK_SETTINGS))       ? '!' : ' ');
-        flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_SYSTEM_ICONS))         ? 'I' : 'i');
-        flagdbg.append(0 != ((diff2  & StatusBarManager.DISABLE2_SYSTEM_ICONS))         ? '!' : ' ');
-        flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE))   ? 'N' : 'n');
-        flagdbg.append(0 != ((diff2  & StatusBarManager.DISABLE2_NOTIFICATION_SHADE))   ? '!' : ' ');
-        flagdbg.append('>');
-        Log.d(TAG, flagdbg.toString());
-
-        if ((diff1 & StatusBarManager.DISABLE_EXPAND) != 0) {
-            if ((state1 & StatusBarManager.DISABLE_EXPAND) != 0) {
-                mShadeController.animateCollapsePanels();
-            }
-        }
-
-        if ((diff1 & StatusBarManager.DISABLE_RECENT) != 0) {
-            if ((state1 & StatusBarManager.DISABLE_RECENT) != 0) {
-                // close recents if it's visible
-                mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
-                mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS);
-            }
-        }
-
-        if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
-            if (areNotificationAlertsDisabled()) {
-                mHeadsUpManager.releaseAllImmediately();
-            }
-        }
-
-        if ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) {
-            updateQsExpansionEnabled();
-        }
-
-        if ((diff2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
-            updateQsExpansionEnabled();
-            if ((state2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
-                mShadeController.animateCollapsePanels();
-            }
-        }
-    }
-
     boolean areNotificationAlertsDisabled() {
         return (mDisabled1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
     }
 
-    protected H createHandler() {
-        return new StatusBar.H();
-    }
-
     @Override
     public void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade,
             int flags) {
@@ -1875,10 +1734,77 @@
 
     @Override
     public void startActivity(Intent intent, boolean dismissShade,
-            ActivityLaunchAnimator.Controller animationController) {
-        startActivityDismissingKeyguard(intent, false, dismissShade,
+            @Nullable ActivityLaunchAnimator.Controller animationController,
+            boolean showOverLockscreenWhenLocked) {
+        // Make sure that we dismiss the keyguard if it is directly dismissable or when we don't
+        // want to show the activity above it.
+        if (mKeyguardStateController.isUnlocked() || !showOverLockscreenWhenLocked) {
+            startActivityDismissingKeyguard(intent, false, dismissShade,
                 false /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */,
                 0 /* flags */, animationController);
+            return;
+        }
+
+        boolean animate =
+                animationController != null && shouldAnimateLaunch(true /* isActivityIntent */,
+                        showOverLockscreenWhenLocked);
+
+        ActivityLaunchAnimator.Controller controller = null;
+        if (animate) {
+            // Wrap the animation controller to dismiss the shade and set
+            // mIsLaunchingActivityOverLockscreen during the animation.
+            ActivityLaunchAnimator.Controller delegate = wrapAnimationController(
+                    animationController, dismissShade);
+            controller = new DelegateLaunchAnimatorController(delegate) {
+                @Override
+                public void onIntentStarted(boolean willAnimate) {
+                    getDelegate().onIntentStarted(willAnimate);
+
+                    if (willAnimate) {
+                        StatusBar.this.mIsLaunchingActivityOverLockscreen = true;
+                    }
+                }
+
+                @Override
+                public void onLaunchAnimationEnd(boolean isExpandingFullyAbove) {
+                    // Set mIsLaunchingActivityOverLockscreen to false before actually finishing the
+                    // animation so that we can assume that mIsLaunchingActivityOverLockscreen
+                    // being true means that we will collapse the shade (or at least run the
+                    // post collapse runnables) later on.
+                    StatusBar.this.mIsLaunchingActivityOverLockscreen = false;
+                    getDelegate().onLaunchAnimationEnd(isExpandingFullyAbove);
+                }
+
+                @Override
+                public void onLaunchAnimationCancelled() {
+                    // Set mIsLaunchingActivityOverLockscreen to false before actually finishing the
+                    // animation so that we can assume that mIsLaunchingActivityOverLockscreen
+                    // being true means that we will collapse the shade (or at least run the
+                    // post collapse runnables) later on.
+                    StatusBar.this.mIsLaunchingActivityOverLockscreen = false;
+                    getDelegate().onLaunchAnimationCancelled();
+                }
+            };
+        } else if (dismissShade) {
+            // The animation will take care of dismissing the shade at the end of the animation. If
+            // we don't animate, collapse it directly.
+            collapseShade();
+        }
+
+        mActivityLaunchAnimator.startIntentWithAnimation(controller, animate,
+                intent.getPackage(), showOverLockscreenWhenLocked, (adapter) -> TaskStackBuilder
+                        .create(mContext)
+                        .addNextIntent(intent)
+                        .startActivities(getActivityOptions(getDisplayId(), adapter),
+                                UserHandle.CURRENT));
+    }
+
+    /**
+     * Whether we are currently animating an activity launch above the lockscreen (occluding
+     * activity).
+     */
+    public boolean isLaunchingActivityOverLockscreen() {
+        return mIsLaunchingActivityOverLockscreen;
     }
 
     @Override
@@ -1918,70 +1844,6 @@
         logStateToEventlog();
     }
 
-    @Override
-    public void onUnlockedChanged() {
-        updateKeyguardState();
-        logStateToEventlog();
-    }
-
-    @Override
-    public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
-        if (inPinnedMode) {
-            mNotificationShadeWindowController.setHeadsUpShowing(true);
-            mStatusBarWindowController.setForceStatusBarVisible(true);
-            if (mNotificationPanelViewController.isFullyCollapsed()) {
-                // We need to ensure that the touchable region is updated before the window will be
-                // resized, in order to not catch any touches. A layout will ensure that
-                // onComputeInternalInsets will be called and after that we can resize the layout. Let's
-                // make sure that the window stays small for one frame until the touchableRegion is set.
-                mNotificationPanelViewController.getView().requestLayout();
-                mNotificationShadeWindowController.setForceWindowCollapsed(true);
-                mNotificationPanelViewController.getView().post(() -> {
-                    mNotificationShadeWindowController.setForceWindowCollapsed(false);
-                });
-            }
-        } else {
-            boolean bypassKeyguard = mKeyguardBypassController.getBypassEnabled()
-                    && mState == StatusBarState.KEYGUARD;
-            if (!mNotificationPanelViewController.isFullyCollapsed()
-                    || mNotificationPanelViewController.isTracking() || bypassKeyguard) {
-                // We are currently tracking or is open and the shade doesn't need to be kept
-                // open artificially.
-                mNotificationShadeWindowController.setHeadsUpShowing(false);
-                if (bypassKeyguard) {
-                    mStatusBarWindowController.setForceStatusBarVisible(false);
-                }
-            } else {
-                // we need to keep the panel open artificially, let's wait until the animation
-                // is finished.
-                mHeadsUpManager.setHeadsUpGoingAway(true);
-                mNotificationPanelViewController.runAfterAnimationFinished(() -> {
-                    if (!mHeadsUpManager.hasPinnedHeadsUp()) {
-                        mNotificationShadeWindowController.setHeadsUpShowing(false);
-                        mHeadsUpManager.setHeadsUpGoingAway(false);
-                    }
-                    mRemoteInputManager.onPanelCollapsed();
-                });
-            }
-        }
-    }
-
-    @Override
-    public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
-        mNotificationsController.requestNotificationUpdate("onHeadsUpStateChanged");
-        if (mStatusBarStateController.isDozing() && isHeadsUp) {
-            entry.setPulseSuppressed(false);
-            mDozeServiceHost.fireNotificationPulse(entry);
-            if (mDozeServiceHost.isPulsing()) {
-                mDozeScrimController.cancelPendingPulseTimeout();
-            }
-        }
-        if (!isHeadsUp && !mHeadsUpManager.hasNotifications()) {
-            // There are no longer any notifications to show.  We should end the pulse now.
-            mDozeScrimController.pulseOutNow();
-        }
-    }
-
     public void setPanelExpanded(boolean isExpanded) {
         if (mPanelExpanded != isExpanded) {
             mNotificationLogger.onPanelExpandedChanged(isExpanded);
@@ -2014,11 +1876,6 @@
         return mNotificationPanelViewController.hideStatusBarIconsWhenExpanded();
     }
 
-    @Override
-    public void onColorsChanged(ColorExtractor extractor, int which) {
-        updateTheme();
-    }
-
     @Nullable
     public View getAmbientIndicationContainer() {
         return mAmbientIndicationContainer;
@@ -2054,7 +1911,7 @@
      *
      * @param animate should the change of the icons be animated.
      */
-    private void updateHideIconsForBouncer(boolean animate) {
+    void updateHideIconsForBouncer(boolean animate) {
         boolean hideBecauseApp = mTopHidesStatusBar && mIsOccluded
                 && (mStatusBarWindowHidden || mBouncerShowing);
         boolean hideBecauseKeyguard = !mPanelExpanded && !mIsOccluded && mBouncerShowing;
@@ -2065,7 +1922,7 @@
                 // We're delaying the showing, since most of the time the fullscreen app will
                 // hide the icons again and we don't want them to fade in and out immediately again.
                 mWereIconsJustHidden = true;
-                mHandler.postDelayed(() -> {
+                mMainExecutor.executeDelayed(() -> {
                     mWereIconsJustHidden = false;
                     mCommandQueue.recomputeDisableFlags(mDisplayId, true);
                 }, 500);
@@ -2108,51 +1965,28 @@
      *
      * Note: This method must be called *before* dismissing the keyguard.
      */
-    public boolean shouldAnimateLaunch(boolean isActivityIntent) {
+    public boolean shouldAnimateLaunch(boolean isActivityIntent, boolean showOverLockscreen) {
         // TODO(b/184121838): Support launch animations when occluded.
         if (isOccluded()) {
             return false;
         }
 
-        // Always animate if we are unlocked.
-        if (!mKeyguardStateController.isShowing()) {
+        // Always animate if we are not showing the keyguard or if we animate over the lockscreen
+        // (without unlocking it).
+        if (showOverLockscreen || !mKeyguardStateController.isShowing()) {
             return true;
         }
 
-        // If we are locked, only animate if remote unlock animations are enabled. We also don't
-        // animate non-activity launches as they can break the animation.
+        // If we are locked and have to dismiss the keyguard, only animate if remote unlock
+        // animations are enabled. We also don't animate non-activity launches as they can break the
+        // animation.
         // TODO(b/184121838): Support non activity launches on the lockscreen.
         return isActivityIntent && KeyguardService.sEnableRemoteKeyguardGoingAwayAnimation;
     }
 
-    @Override
-    public boolean isOnKeyguard() {
-        return mKeyguardStateController.isShowing();
-    }
-
-    @Override
-    public void hideKeyguardWithAnimation(IRemoteAnimationRunner runner) {
-        // We post to the main thread for 2 reasons:
-        //   1. KeyguardViewMediator is not thread-safe.
-        //   2. To ensure that ViewMediatorCallback#keyguardDonePending is called before
-        //      ViewMediatorCallback#readyForKeyguardDone. The wrong order could occur when doing
-        //      dismissKeyguardThenExecute { hideKeyguardWithAnimation(runner) }.
-        mMainThreadHandler.post(() -> mKeyguardViewMediator.hideWithAnimation(runner));
-    }
-
-    @Override
-    public void setBlursDisabledForAppLaunch(boolean disabled) {
-        mKeyguardViewMediator.setBlursDisabledForAppLaunch(disabled);
-    }
-
-    @Override
-    public int getBackgroundColor(TaskInfo task) {
-        if (!mStartingSurfaceOptional.isPresent()) {
-            Log.w(TAG, "No starting surface, defaulting to SystemBGColor");
-            return SplashscreenContentDrawer.getSystemBGColor();
-        }
-
-        return mStartingSurfaceOptional.get().getBackgroundColor(task);
+    /** Whether we should animate an activity launch. */
+    public boolean shouldAnimateLaunch(boolean isActivityIntent) {
+        return shouldAnimateLaunch(isActivityIntent, false /* showOverLockscreen */);
     }
 
     public boolean isDeviceInVrMode() {
@@ -2168,38 +2002,19 @@
         mState = state;
     }
 
-    @VisibleForTesting
-    void setUserSetupForTest(boolean userSetup) {
-        mUserSetup = userSetup;
+    static class KeyboardShortcutsMessage {
+        final int mDeviceId;
+
+        KeyboardShortcutsMessage(int deviceId) {
+            mDeviceId = deviceId;
+        }
     }
 
-    /**
-     * All changes to the status bar and notifications funnel through here and are batched.
-     */
-    protected class H extends Handler {
-        @Override
-        public void handleMessage(Message m) {
-            switch (m.what) {
-                case MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU:
-                    toggleKeyboardShortcuts(m.arg1);
-                    break;
-                case MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU:
-                    dismissKeyboardShortcuts();
-                    break;
-                // End old BaseStatusBar.H handling.
-                case MSG_OPEN_NOTIFICATION_PANEL:
-                    animateExpandNotificationsPanel();
-                    break;
-                case MSG_OPEN_SETTINGS_PANEL:
-                    animateExpandSettingsPanel((String) m.obj);
-                    break;
-                case MSG_CLOSE_PANELS:
-                    mShadeController.animateCollapsePanels();
-                    break;
-                case MSG_LAUNCH_TRANSITION_TIMEOUT:
-                    onLaunchTransitionTimeout();
-                    break;
-            }
+    static class AnimateExpandSettingsPanelMessage {
+        final String mSubpanel;
+
+        AnimateExpandSettingsPanelMessage(String subpanel) {
+            mSubpanel = subpanel;
         }
     }
 
@@ -2223,59 +2038,6 @@
         mHeadsUpManager.releaseAllImmediately();
     }
 
-    /**
-     * Called for system navigation gestures. First action opens the panel, second opens
-     * settings. Down action closes the entire panel.
-     */
-    @Override
-    public void handleSystemKey(int key) {
-        if (SPEW) Log.d(TAG, "handleNavigationKey: " + key);
-        if (!mCommandQueue.panelsEnabled() || !mKeyguardUpdateMonitor.isDeviceInteractive()
-                || mKeyguardStateController.isShowing() && !mKeyguardStateController.isOccluded()) {
-            return;
-        }
-
-        // Panels are not available in setup
-        if (!mUserSetup) return;
-
-        if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP == key) {
-            mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_UP);
-            mNotificationPanelViewController.collapse(
-                    false /* delayed */, 1.0f /* speedUpFactor */);
-        } else if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN == key) {
-            mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_DOWN);
-            if (mNotificationPanelViewController.isFullyCollapsed()) {
-                if (mVibrateOnOpening) {
-                    mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
-                }
-                mNotificationPanelViewController.expand(true /* animate */);
-                mStackScroller.setWillExpand(true);
-                mHeadsUpManager.unpinAll(true /* userUnpinned */);
-                mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN, 1);
-            } else if (!mNotificationPanelViewController.isInSettings()
-                    && !mNotificationPanelViewController.isExpanding()) {
-                mNotificationPanelViewController.flingSettings(0 /* velocity */,
-                        NotificationPanelView.FLING_EXPAND);
-                mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN_QS, 1);
-            }
-        }
-
-    }
-
-    @Override
-    public void showPinningEnterExitToast(boolean entering) {
-        if (getNavigationBarView() != null) {
-            getNavigationBarView().showPinningEnterExitToast(entering);
-        }
-    }
-
-    @Override
-    public void showPinningEscapeToast() {
-        if (getNavigationBarView() != null) {
-            getNavigationBarView().showPinningEscapeToast();
-        }
-    }
-
     void makeExpandedVisible(boolean force) {
         if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
         if (!force && (mExpandedVisible || !mCommandQueue.panelsEnabled())) {
@@ -2294,42 +2056,17 @@
     }
 
     public void postAnimateCollapsePanels() {
-        mHandler.post(mShadeController::animateCollapsePanels);
+        mMainExecutor.execute(mShadeController::animateCollapsePanels);
     }
 
     public void postAnimateForceCollapsePanels() {
-        mHandler.post(() -> mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE,
+        mMainExecutor.execute(
+                () -> mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE,
                 true /* force */));
     }
 
     public void postAnimateOpenPanels() {
-        mHandler.sendEmptyMessage(MSG_OPEN_SETTINGS_PANEL);
-    }
-
-    @Override
-    public void togglePanel() {
-        if (mPanelExpanded) {
-            mShadeController.animateCollapsePanels();
-        } else {
-            animateExpandNotificationsPanel();
-        }
-    }
-
-    @Override
-    public void animateCollapsePanels(int flags, boolean force) {
-        mShadeController.animateCollapsePanels(flags, force, false /* delayed */,
-                1.0f /* speedUpFactor */);
-    }
-
-    /**
-     * Called by {@link ShadeController} when it calls
-     * {@link ShadeController#animateCollapsePanels(int, boolean, boolean, float)}.
-     */
-    void postHideRecentApps() {
-        if (!mHandler.hasMessages(MSG_HIDE_RECENT_APPS)) {
-            mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
-            mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS);
-        }
+        mMessageRouter.sendMessage(MSG_OPEN_SETTINGS_PANEL);
     }
 
     public boolean isExpandedVisible() {
@@ -2355,36 +2092,6 @@
         }
     }
 
-    @Override
-    public void animateExpandNotificationsPanel() {
-        if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
-        if (!mCommandQueue.panelsEnabled()) {
-            return ;
-        }
-
-        mNotificationPanelViewController.expandWithoutQs();
-
-        if (false) postStartTracing();
-    }
-
-    @Override
-    public void animateExpandSettingsPanel(@Nullable String subPanel) {
-        if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
-        if (!mCommandQueue.panelsEnabled()) {
-            return;
-        }
-
-        // Settings are not available in setup
-        if (!mUserSetup) return;
-
-        if (subPanel != null) {
-            mQSPanelController.openDetails(subPanel);
-        }
-        mNotificationPanelViewController.expandWithQs();
-
-        if (false) postStartTracing();
-    }
-
     public void animateCollapseQuickSettings() {
         if (mState == StatusBarState.SHADE) {
             mStatusBarView.collapsePanel(true, false /* delayed */, 1.0f /* speedUpFactor */);
@@ -2464,11 +2171,7 @@
             final boolean upOrCancel =
                     event.getAction() == MotionEvent.ACTION_UP ||
                     event.getAction() == MotionEvent.ACTION_CANCEL;
-            if (upOrCancel && !mExpandedVisible) {
-                setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
-            } else {
-                setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
-            }
+            setInteracting(StatusBarManager.WINDOW_STATUS_BAR, !upOrCancel || mExpandedVisible);
         }
         return false;
     }
@@ -2485,63 +2188,7 @@
         return mBiometricUnlockController;
     }
 
-    @Override // CommandQueue
-    public void setWindowState(
-            int displayId, @WindowType int window, @WindowVisibleState int state) {
-        if (displayId != mDisplayId) {
-            return;
-        }
-        boolean showing = state == WINDOW_STATE_SHOWING;
-        if (mNotificationShadeWindowView != null
-                && window == StatusBarManager.WINDOW_STATUS_BAR
-                && mStatusBarWindowState != state) {
-            mStatusBarWindowState = state;
-            if (DEBUG_WINDOW_STATE) Log.d(TAG, "Status bar " + windowStateToString(state));
-            if (mStatusBarView != null) {
-                if (!showing && mState == StatusBarState.SHADE) {
-                    mStatusBarView.collapsePanel(false /* animate */, false /* delayed */,
-                            1.0f /* speedUpFactor */);
-                }
-                mStatusBarWindowHidden = state == WINDOW_STATE_HIDDEN;
-                updateHideIconsForBouncer(false /* animate */);
-            }
-        }
-
-        updateBubblesVisibility();
-    }
-
-    @Override
-    public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
-            AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
-            @Behavior int behavior, InsetsVisibilities requestedVisibilities, String packageName) {
-        if (displayId != mDisplayId) {
-            return;
-        }
-        boolean barModeChanged = false;
-        if (mAppearance != appearance) {
-            mAppearance = appearance;
-            barModeChanged = updateBarMode(barMode(mTransientShown, appearance));
-        }
-        mLightBarController.onStatusBarAppearanceChanged(appearanceRegions, barModeChanged,
-                mStatusBarMode, navbarColorManagedByIme);
-
-        updateBubblesVisibility();
-        mStatusBarStateController.setSystemBarAttributes(
-                appearance, behavior, requestedVisibilities, packageName);
-    }
-
-    @Override
-    public void showTransient(int displayId, @InternalInsetsType int[] types) {
-        if (displayId != mDisplayId) {
-            return;
-        }
-        if (!containsType(types, ITYPE_STATUS_BAR)) {
-            return;
-        }
-        showTransientUnchecked();
-    }
-
-    private void showTransientUnchecked() {
+    void showTransientUnchecked() {
         if (!mTransientShown) {
             mTransientShown = true;
             mNoAnimationOnNextBarModeChange = true;
@@ -2549,18 +2196,8 @@
         }
     }
 
-    @Override
-    public void abortTransient(int displayId, @InternalInsetsType int[] types) {
-        if (displayId != mDisplayId) {
-            return;
-        }
-        if (!containsType(types, ITYPE_STATUS_BAR)) {
-            return;
-        }
-        clearTransient();
-    }
 
-    private void clearTransient() {
+    void clearTransient() {
         if (mTransientShown) {
             mTransientShown = false;
             handleTransientChanged();
@@ -2602,8 +2239,7 @@
         }
     }
 
-    @Override
-    public void showWirelessChargingAnimation(int batteryLevel) {
+    protected void showWirelessChargingAnimation(int batteryLevel) {
         showChargingAnimation(batteryLevel, UNKNOWN_BATTERY_LEVEL, 0);
     }
 
@@ -2624,11 +2260,6 @@
                 }, false, sUiEventLogger).show(animationDelay);
     }
 
-    @Override
-    public void onRecentsAnimationStateChanged(boolean running) {
-        setInteracting(StatusBarManager.WINDOW_NAVIGATION_BAR, running);
-    }
-
     protected BarTransitions getStatusBarTransitions() {
         return mNotificationShadeWindowViewController.getBarTransitions();
     }
@@ -2648,13 +2279,11 @@
     }
 
     /** Temporarily hides Bubbles if the status bar is hidden. */
-    private void updateBubblesVisibility() {
-        if (mBubblesOptional.isPresent()) {
-            mBubblesOptional.get().onStatusBarVisibilityChanged(
-                    mStatusBarMode != MODE_LIGHTS_OUT
-                            && mStatusBarMode != MODE_LIGHTS_OUT_TRANSPARENT
-                            && !mStatusBarWindowHidden);
-        }
+    void updateBubblesVisibility() {
+        mBubblesOptional.ifPresent(bubbles -> bubbles.onStatusBarVisibilityChanged(
+                mStatusBarMode != MODE_LIGHTS_OUT
+                        && mStatusBarMode != MODE_LIGHTS_OUT_TRANSPARENT
+                        && !mStatusBarWindowHidden));
     }
 
     void checkBarMode(@TransitionMode int mode, @WindowVisibleState int windowState,
@@ -2738,7 +2367,7 @@
             mNotificationPanelViewController.dump(fd, pw, args);
         }
         pw.println("  mStackScroller: ");
-        if (mStackScroller instanceof Dumpable) {
+        if (mStackScroller != null) {
             pw.print  ("      ");
             ((Dumpable) mStackScroller).dump(fd, pw, args);
         }
@@ -2771,19 +2400,6 @@
 
         mNotificationsController.dump(fd, pw, args, DUMPTRUCK);
 
-        if (DUMPTRUCK) {
-            if (false) {
-                pw.println("see the logcat for a dump of the views we have created.");
-                // must happen on ui thread
-                mHandler.post(() -> {
-                    mStatusBarView.getLocationOnScreen(mAbsPos);
-                    Log.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1] +
-                            ") " + mStatusBarView.getWidth() + "x" + getStatusBarHeight());
-                    mStatusBarView.debug();
-                });
-            }
-        }
-
         if (DEBUG_GESTURES) {
             pw.print("  status bar gestures: ");
             mGestureRec.dump(fd, pw, args);
@@ -2814,7 +2430,7 @@
         pw.println("   Insecure camera: " + CameraIntents.getInsecureCameraIntent(mContext));
         pw.println("   Secure camera: " + CameraIntents.getSecureCameraIntent(mContext));
         pw.println("   Override package: "
-                + String.valueOf(CameraIntents.getOverrideCameraPackage(mContext)));
+                + CameraIntents.getOverrideCameraPackage(mContext));
     }
 
     public static void dumpBarTransitions(
@@ -2875,7 +2491,7 @@
         startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade, 0);
     }
 
-    private void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
+    void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
             final boolean dismissShade, final boolean disallowEnterPictureInPictureWhileLaunching,
             final Callback callback, int flags,
             @Nullable ActivityLaunchAnimator.Controller animationController) {
@@ -2920,7 +2536,7 @@
                             options.setRotationAnimationHint(
                                     WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS);
                         }
-                        if (intent.getAction() == Settings.Panel.ACTION_VOLUME) {
+                        if (Settings.Panel.ACTION_VOLUME.equals(intent.getAction())) {
                             // Settings Panel is implemented as activity(not a dialog), so
                             // underlying app is paused and may enter picture-in-picture mode
                             // as a result.
@@ -2960,7 +2576,7 @@
     private ActivityLaunchAnimator.Controller wrapAnimationController(
             ActivityLaunchAnimator.Controller animationController, boolean dismissShade) {
         View rootView = animationController.getLaunchContainer().getRootView();
-        if (rootView == mSuperStatusBarViewFactory.getStatusBarWindowView()) {
+        if (rootView == mStatusBarWindowView) {
             // We are animating a view in the status bar. We have to make sure that the status bar
             // window matches the full screen during the animation and that we are expanding the
             // view below the other status bar text.
@@ -3019,7 +2635,7 @@
                             && mStatusBarKeyguardViewManager.isOccluded()) {
                         mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
                     } else {
-                        AsyncTask.execute(runnable);
+                        mMainExecutor.execute(runnable);
                     }
                 }
                 if (dismissShade) {
@@ -3031,7 +2647,7 @@
 
                         // Do it after DismissAction has been processed to conserve the needed
                         // ordering.
-                        mHandler.post(mShadeController::runPostCollapseRunnables);
+                        mMainExecutor.execute(mShadeController::runPostCollapseRunnables);
                     }
                 } else if (StatusBar.this.isInLaunchTransition()
                         && mNotificationPanelViewController.isLaunchTransitionFinished()) {
@@ -3040,7 +2656,7 @@
                     // finished,
                     // so nobody will call readyForKeyguardDone anymore. Post it such that
                     // keyguardDonePending gets called first.
-                    mHandler.post(mStatusBarKeyguardViewManager::readyForKeyguardDone);
+                    mMainExecutor.execute(mStatusBarKeyguardViewManager::readyForKeyguardDone);
                 }
                 return deferred;
             }
@@ -3079,8 +2695,8 @@
                     mNotificationShadeWindowController.setNotTouchable(false);
                 }
                 if (mBubblesOptional.isPresent() && mBubblesOptional.get().isStackExpanded()) {
-                    // Post to main thread handler, since updating the UI.
-                    mMainThreadHandler.post(() -> mBubblesOptional.get().collapseStack());
+                    // Post to main thread, since updating the UI.
+                    mMainExecutor.execute(() -> mBubblesOptional.get().collapseStack());
                 }
                 finishBarAnimations();
                 resetUserExpandedStates();
@@ -3141,20 +2757,6 @@
             action.onDismiss();
         }
     }
-
-    @Override
-    public void onConfigChanged(Configuration newConfig) {
-        updateResources();
-        updateDisplaySize(); // populates mDisplayMetrics
-
-        if (DEBUG) {
-            Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
-        }
-
-        mViewHierarchyManager.updateRowStates();
-        mScreenPinningRequest.onConfigurationChanged();
-    }
-
     /**
      * Notify the shade controller that the current user changed
      *
@@ -3302,43 +2904,12 @@
                 | ((currentlyInsecure ? 1 : 0) << 12);
     }
 
-    //
-    // tracing
-    //
-
-    void postStartTracing() {
-        mHandler.postDelayed(mStartTracing, 3000);
-    }
-
-    void vibrate() {
-        android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
-                Context.VIBRATOR_SERVICE);
-        vib.vibrate(250, VIBRATION_ATTRIBUTES);
-    }
-
-    final Runnable mStartTracing = new Runnable() {
-        @Override
-        public void run() {
-            vibrate();
-            SystemClock.sleep(250);
-            Log.d(TAG, "startTracing");
-            android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
-            mHandler.postDelayed(mStopTracing, 10000);
-        }
-    };
-
-    final Runnable mStopTracing = () -> {
-        android.os.Debug.stopMethodTracing();
-        Log.d(TAG, "stopTracing");
-        vibrate();
-    };
-
     @Override
     public void postQSRunnableDismissingKeyguard(final Runnable runnable) {
-        mHandler.post(() -> {
+        mMainExecutor.execute(() -> {
             mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
-            executeRunnableDismissingKeyguard(() -> mHandler.post(runnable), null, false, false,
-                    false);
+            executeRunnableDismissingKeyguard(
+                    () -> mMainExecutor.execute(runnable), null, false, false, false);
         });
     }
 
@@ -3350,7 +2921,7 @@
     @Override
     public void postStartActivityDismissingKeyguard(final PendingIntent intent,
             @Nullable ActivityLaunchAnimator.Controller animationController) {
-        mHandler.post(() -> startPendingIntentDismissingKeyguard(intent,
+        mMainExecutor.execute(() -> startPendingIntentDismissingKeyguard(intent,
                 null /* intentSentUiThreadCallback */, animationController));
     }
 
@@ -3362,7 +2933,7 @@
     @Override
     public void postStartActivityDismissingKeyguard(Intent intent, int delay,
             @Nullable ActivityLaunchAnimator.Controller animationController) {
-        mHandler.postDelayed(
+        mMainExecutor.executeDelayed(
                 () ->
                         startActivityDismissingKeyguard(intent, true /* onlyProvisioned */,
                                 true /* dismissShade */,
@@ -3432,7 +3003,7 @@
             mNotificationPanelViewController.cancelAnimation();
             onLaunchTransitionFadingEnded();
         }
-        mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
+        mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
         if (mUserSwitcherController != null && mUserSwitcherController.useFullscreenUserSwitcher()) {
             mStatusBarStateController.setState(StatusBarState.FULLSCREEN_USER_SWITCHER);
         } else if (!mPulseExpansionHandler.isWakingToShadeLocked()) {
@@ -3473,7 +3044,7 @@
      */
     public void fadeKeyguardAfterLaunchTransition(final Runnable beforeFading,
             Runnable endRunnable) {
-        mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
+        mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
         mLaunchTransitionEndRunnable = endRunnable;
         Runnable hideRunnable = () -> {
             mKeyguardStateController.setLaunchTransitionFadingAway(true);
@@ -3513,7 +3084,8 @@
      */
     public void animateKeyguardUnoccluding() {
         mNotificationPanelViewController.setExpandedFraction(0f);
-        animateExpandNotificationsPanel();
+        mCommandQueueCallbacks.animateExpandNotificationsPanel();
+        mScrimController.setUnocclusionAnimationRunning(true);
     }
 
     /**
@@ -3522,8 +3094,8 @@
      * because the launched app crashed or something else went wrong.
      */
     public void startLaunchTransitionTimeout() {
-        mHandler.sendEmptyMessageDelayed(MSG_LAUNCH_TRANSITION_TIMEOUT,
-                LAUNCH_TRANSITION_TIMEOUT_MS);
+        mMessageRouter.sendMessageDelayed(
+                MSG_LAUNCH_TRANSITION_TIMEOUT, LAUNCH_TRANSITION_TIMEOUT_MS);
     }
 
     private void onLaunchTransitionTimeout() {
@@ -3578,7 +3150,7 @@
         if (mQSPanelController != null) {
             mQSPanelController.refreshAllTiles();
         }
-        mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
+        mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
         releaseGestureWakeLock();
         mNotificationPanelViewController.onAffordanceLaunchEnded();
         mNotificationPanelViewController.cancelAnimation();
@@ -3684,7 +3256,7 @@
 
     /**
      * While IME is active and a BACK event is detected, check with
-     * {@link StatusBarKeyguardViewManager#dispatchBackKeyEventPreIme(KeyEvent)} to see if the event
+     * {@link StatusBarKeyguardViewManager#dispatchBackKeyEventPreIme()} to see if the event
      * should be handled before routing to IME, in order to prevent the user having to hit back
      * twice to exit bouncer.
      */
@@ -3806,69 +3378,7 @@
         mNotificationPanelViewController.collapseWithDuration(duration);
     }
 
-    @Override
-    public void onStatePreChange(int oldState, int newState) {
-        // If we're visible and switched to SHADE_LOCKED (the user dragged
-        // down on the lockscreen), clear notification LED, vibration,
-        // ringing.
-        // Other transitions are covered in handleVisibleToUserChanged().
-        if (mVisible && (newState == StatusBarState.SHADE_LOCKED
-                || mStatusBarStateController.goingToFullShade())) {
-            clearNotificationEffects();
-        }
-        if (newState == StatusBarState.KEYGUARD) {
-            mRemoteInputManager.onPanelCollapsed();
-            maybeEscalateHeadsUp();
-        }
-    }
 
-    @Override
-    public void onStateChanged(int newState) {
-        mState = newState;
-        updateReportRejectedTouchVisibility();
-        mDozeServiceHost.updateDozing();
-        updateTheme();
-        mNavigationBarController.touchAutoDim(mDisplayId);
-        Trace.beginSection("StatusBar#updateKeyguardState");
-        if (mState == StatusBarState.KEYGUARD && mStatusBarView != null) {
-            mStatusBarView.removePendingHideExpandedRunnables();
-        }
-        updateDozingState();
-        checkBarModes();
-        updateScrimController();
-        mPresenter.updateMediaMetaData(false, mState != StatusBarState.KEYGUARD);
-        updateKeyguardState();
-        Trace.endSection();
-    }
-
-    @Override
-    public void onDozeAmountChanged(float linear, float eased) {
-        if (mFeatureFlags.useNewLockscreenAnimations()
-                && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
-            mLightRevealScrim.setRevealAmount(1f - linear);
-        }
-    }
-
-    @Override
-    public void onDozingChanged(boolean isDozing) {
-        Trace.beginSection("StatusBar#updateDozing");
-        mDozing = isDozing;
-
-        // Collapse the notification panel if open
-        boolean dozingAnimated = mDozeServiceHost.getDozingRequested()
-                && mDozeParameters.shouldControlScreenOff();
-        mNotificationPanelViewController.resetViews(dozingAnimated);
-
-        updateQsExpansionEnabled();
-        mKeyguardViewMediator.setDozing(mDozing);
-
-        mNotificationsController.requestNotificationUpdate("onDozingChanged");
-        updateDozingState();
-        mDozeServiceHost.updateDozing();
-        updateScrimController();
-        updateReportRejectedTouchVisibility();
-        Trace.endSection();
-    }
 
     /**
      * Updates the light reveal effect to reflect the reason we're waking or sleeping (for example,
@@ -4010,7 +3520,8 @@
 
                 // This gets executed before we will show Keyguard, so post it in order that the state
                 // is correct.
-                mHandler.post(() -> onCameraLaunchGestureDetected(mLastCameraLaunchSource));
+                mMainExecutor.execute(() -> mCommandQueueCallbacks.onCameraLaunchGestureDetected(
+                        mLastCameraLaunchSource));
             }
 
             if (mLaunchEmergencyActionOnFinishedGoingToSleep) {
@@ -4018,7 +3529,8 @@
 
                 // This gets executed before we will show Keyguard, so post it in order that the
                 // state is correct.
-                mHandler.post(() -> onEmergencyActionLaunchGestureDetected());
+                mMainExecutor.execute(
+                        () -> mCommandQueueCallbacks.onEmergencyActionLaunchGestureDetected());
             }
             updateIsKeyguard();
         }
@@ -4131,36 +3643,6 @@
         return mWakefulnessLifecycle.getWakefulness();
     }
 
-    private void vibrateForCameraGesture() {
-        // Make sure to pass -1 for repeat so VibratorService doesn't stop us when going to sleep.
-        mVibrator.vibrate(mCameraLaunchGestureVibrationEffect, VIBRATION_ATTRIBUTES);
-    }
-
-    private static VibrationEffect getCameraGestureVibrationEffect(Vibrator vibrator,
-            Resources resources) {
-        if (vibrator.areAllPrimitivesSupported(
-                VibrationEffect.Composition.PRIMITIVE_QUICK_RISE,
-                VibrationEffect.Composition.PRIMITIVE_CLICK)) {
-            return VibrationEffect.startComposition()
-                    .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE)
-                    .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 50)
-                    .compose();
-        }
-        if (vibrator.hasAmplitudeControl()) {
-            return VibrationEffect.createWaveform(
-                    CAMERA_LAUNCH_GESTURE_VIBRATION_TIMINGS,
-                    CAMERA_LAUNCH_GESTURE_VIBRATION_AMPLITUDES,
-                    /* repeat= */ -1);
-        }
-
-        int[] pattern = resources.getIntArray(R.array.config_cameraLaunchGestureVibePattern);
-        long[] timings = new long[pattern.length];
-        for (int i = 0; i < pattern.length; i++) {
-            timings[i] = pattern[i];
-        }
-        return VibrationEffect.createWaveform(timings, /* repeat= */ -1);
-    }
-
     /**
      * @return true if the screen is currently fully off, i.e. has finished turning off and has
      * since not started turning on.
@@ -4169,139 +3651,11 @@
         return mScreenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_OFF;
     }
 
-    @Override
-    public void showScreenPinningRequest(int taskId) {
-        if (mKeyguardStateController.isShowing()) {
-            // Don't allow apps to trigger this from keyguard.
-            return;
-        }
-        // Show screen pinning request, since this comes from an app, show 'no thanks', button.
-        showScreenPinningRequest(taskId, true);
-    }
-
     public void showScreenPinningRequest(int taskId, boolean allowCancel) {
         mScreenPinningRequest.showPrompt(taskId, allowCancel);
     }
 
-    @Override
-    public void appTransitionCancelled(int displayId) {
-        if (displayId == mDisplayId) {
-            mSplitScreenOptional.ifPresent(splitScreen -> splitScreen.onAppTransitionFinished());
-        }
-    }
-
-    @Override
-    public void appTransitionFinished(int displayId) {
-        if (displayId == mDisplayId) {
-            mSplitScreenOptional.ifPresent(splitScreen -> splitScreen.onAppTransitionFinished());
-        }
-    }
-
-    @Override
-    public void onCameraLaunchGestureDetected(int source) {
-        mLastCameraLaunchSource = source;
-        if (isGoingToSleep()) {
-            if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Finish going to sleep before launching camera");
-            mLaunchCameraOnFinishedGoingToSleep = true;
-            return;
-        }
-        if (!mNotificationPanelViewController.canCameraGestureBeLaunched()) {
-            if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Can't launch camera right now");
-            return;
-        }
-        if (!mDeviceInteractive) {
-            mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_CAMERA_LAUNCH,
-                    "com.android.systemui:CAMERA_GESTURE");
-        }
-        vibrateForCameraGesture();
-
-        if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) {
-            Log.v(TAG, "Camera launch");
-            mKeyguardUpdateMonitor.onCameraLaunched();
-        }
-
-        if (!mStatusBarKeyguardViewManager.isShowing()) {
-            final Intent cameraIntent = CameraIntents.getInsecureCameraIntent(mContext);
-            startActivityDismissingKeyguard(cameraIntent,
-                    false /* onlyProvisioned */, true /* dismissShade */,
-                    true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0,
-                    null /* animationController */);
-        } else {
-            if (!mDeviceInteractive) {
-                // Avoid flickering of the scrim when we instant launch the camera and the bouncer
-                // comes on.
-                mGestureWakeLock.acquire(LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
-            }
-            if (isWakingUpOrAwake()) {
-                if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Launching camera");
-                if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
-                    mStatusBarKeyguardViewManager.reset(true /* hide */);
-                }
-                mNotificationPanelViewController.launchCamera(
-                        mDeviceInteractive /* animate */, source);
-                updateScrimController();
-            } else {
-                // We need to defer the camera launch until the screen comes on, since otherwise
-                // we will dismiss us too early since we are waiting on an activity to be drawn and
-                // incorrectly get notified because of the screen on event (which resumes and pauses
-                // some activities)
-                if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Deferring until screen turns on");
-                mLaunchCameraWhenFinishedWaking = true;
-            }
-        }
-    }
-
-    @Override
-    public void onEmergencyActionLaunchGestureDetected() {
-        Intent emergencyIntent = getEmergencyActionIntent();
-
-        if (emergencyIntent == null) {
-            Log.wtf(TAG, "Couldn't find an app to process the emergency intent.");
-            return;
-        }
-
-        if (isGoingToSleep()) {
-            mLaunchEmergencyActionOnFinishedGoingToSleep = true;
-            return;
-        }
-
-        if (!mDeviceInteractive) {
-            mPowerManager.wakeUp(SystemClock.uptimeMillis(),
-                    PowerManager.WAKE_REASON_GESTURE,
-                    "com.android.systemui:EMERGENCY_GESTURE");
-        }
-        // TODO(b/169087248) Possibly add haptics here for emergency action. Currently disabled for
-        // app-side haptic experimentation.
-
-        if (!mStatusBarKeyguardViewManager.isShowing()) {
-            startActivityDismissingKeyguard(emergencyIntent,
-                    false /* onlyProvisioned */, true /* dismissShade */,
-                    true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0,
-                    null /* animationController */);
-            return;
-        }
-
-        if (!mDeviceInteractive) {
-            // Avoid flickering of the scrim when we instant launch the camera and the bouncer
-            // comes on.
-            mGestureWakeLock.acquire(LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
-        }
-
-        if (isWakingUpOrAwake()) {
-            if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
-                mStatusBarKeyguardViewManager.reset(true /* hide */);
-            }
-            mContext.startActivityAsUser(emergencyIntent, UserHandle.CURRENT);
-            return;
-        }
-        // We need to defer the emergency action launch until the screen comes on, since otherwise
-        // we will dismiss us too early since we are waiting on an activity to be drawn and
-        // incorrectly get notified because of the screen on event (which resumes and pauses
-        // some activities)
-        mLaunchEmergencyActionWhenFinishedWaking = true;
-    }
-
-    private @Nullable Intent getEmergencyActionIntent() {
+    @Nullable Intent getEmergencyActionIntent() {
         Intent emergencyIntent = new Intent(EmergencyGesture.ACTION_LAUNCH_EMERGENCY);
         PackageManager pm = mContext.getPackageManager();
         List<ResolveInfo> emergencyActivities = pm.queryIntentActivities(emergencyIntent,
@@ -4359,16 +3713,11 @@
         return true;
     }
 
-    private boolean isGoingToSleep() {
+    boolean isGoingToSleep() {
         return mWakefulnessLifecycle.getWakefulness()
                 == WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP;
     }
 
-    private boolean isWakingUpOrAwake() {
-        return mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_AWAKE
-                || mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_WAKING;
-    }
-
     public void notifyBiometricAuthModeChanged() {
         mDozeServiceHost.updateDozing();
         updateScrimController();
@@ -4400,10 +3749,8 @@
             ScrimState state = mStatusBarKeyguardViewManager.bouncerNeedsScrimming()
                     ? ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER;
             mScrimController.transitionTo(state);
-        } else if (isInLaunchTransition()
-                || mLaunchCameraWhenFinishedWaking
-                || launchingAffordanceWithPreview) {
-            // TODO(b/170133395) Investigate whether Emergency Gesture flag should be included here.
+        } else if (launchingAffordanceWithPreview) {
+            // We want to avoid animating when launching with a preview.
             mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
         } else if (mBrightnessMirrorVisible) {
             mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR);
@@ -4423,8 +3770,6 @@
             mScrimController.transitionTo(ScrimState.AOD);
         } else if (mIsKeyguard && !unlocking) {
             mScrimController.transitionTo(ScrimState.KEYGUARD);
-        } else if (mBubblesOptional.isPresent() && mBubblesOptional.get().isStackExpanded()) {
-            mScrimController.transitionTo(ScrimState.BUBBLE_EXPANDED, mUnlockScrimCallback);
         } else {
             mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
         }
@@ -4521,10 +3866,6 @@
         mNotificationsController.setNotificationSnoozed(sbn, snoozeOption);
     }
 
-    @Override
-    public void toggleSplitScreen() {
-        toggleSplitScreenMode(-1 /* metricsDockAction */, -1 /* metricsUndockAction */);
-    }
 
     public void awakenDreams() {
         mUiBgExecutor.execute(() -> {
@@ -4536,46 +3877,6 @@
         });
     }
 
-    @Override
-    public void preloadRecentApps() {
-        int msg = MSG_PRELOAD_RECENT_APPS;
-        mHandler.removeMessages(msg);
-        mHandler.sendEmptyMessage(msg);
-    }
-
-    @Override
-    public void cancelPreloadRecentApps() {
-        int msg = MSG_CANCEL_PRELOAD_RECENT_APPS;
-        mHandler.removeMessages(msg);
-        mHandler.sendEmptyMessage(msg);
-    }
-
-    @Override
-    public void dismissKeyboardShortcutsMenu() {
-        int msg = MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU;
-        mHandler.removeMessages(msg);
-        mHandler.sendEmptyMessage(msg);
-    }
-
-    @Override
-    public void toggleKeyboardShortcutsMenu(int deviceId) {
-        int msg = MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU;
-        mHandler.removeMessages(msg);
-        mHandler.obtainMessage(msg, deviceId, 0).sendToTarget();
-    }
-
-    @Override
-    public void setTopAppHidesStatusBar(boolean topAppHidesStatusBar) {
-        mTopHidesStatusBar = topAppHidesStatusBar;
-        if (!topAppHidesStatusBar && mWereIconsJustHidden) {
-            // Immediately update the icon hidden state, since that should only apply if we're
-            // staying fullscreen.
-            mWereIconsJustHidden = false;
-            mCommandQueue.recomputeDisableFlags(mDisplayId, true);
-        }
-        updateHideIconsForBouncer(true /* animate */);
-    }
-
     protected void toggleKeyboardShortcuts(int deviceId) {
         KeyboardShortcuts.toggle(mContext, deviceId);
     }
@@ -4695,7 +3996,7 @@
     }
 
     private void postOnUiThread(Runnable runnable) {
-        mMainThreadHandler.post(runnable);
+        mMainExecutor.execute(runnable);
     }
 
     /**
@@ -4837,37 +4138,30 @@
         }
         return mStatusBarKeyguardViewManager.isSecure();
     }
-
-    @Override
-    public void showAssistDisclosure() {
-        mAssistManagerLazy.get().showDisclosure();
-    }
-
     public NotificationPanelViewController getPanelController() {
         return mNotificationPanelViewController;
     }
-
-    @Override
-    public void startAssist(Bundle args) {
-        mAssistManagerLazy.get().startAssist(args);
-    }
     // End Extra BaseStatusBarMethods.
 
     public NotificationGutsManager getGutsManager() {
         return mGutsManager;
     }
 
-    private boolean isTransientShown() {
+    boolean isTransientShown() {
         return mTransientShown;
     }
 
-    @Override
-    public void suppressAmbientDisplay(boolean suppressed) {
-        mDozeServiceHost.setDozeSuppressed(suppressed);
-    }
 
     public void addExpansionChangedListener(@NonNull ExpansionChangedListener listener) {
         mExpansionChangedListeners.add(listener);
+        sendInitialExpansionAmount(listener);
+    }
+
+    private void sendInitialExpansionAmount(ExpansionChangedListener expansionChangedListener) {
+        if (mStatusBarView != null) {
+            expansionChangedListener.onExpansionChanged(mStatusBarView.getExpansionFraction(),
+                    mStatusBarView.isExpanded());
+        }
     }
 
     public void removeExpansionChangedListener(@NonNull ExpansionChangedListener listener) {
@@ -4881,11 +4175,278 @@
         }
 
         mLightRevealScrim.setAlpha(mScrimController.getState().getMaxLightRevealScrimAlpha());
-        if (mFeatureFlags.useNewLockscreenAnimations()
-                && (mDozeParameters.getAlwaysOn() || mDozeParameters.isQuickPickupEnabled())) {
-            mLightRevealScrim.setVisibility(View.VISIBLE);
-        } else {
-            mLightRevealScrim.setVisibility(View.GONE);
-        }
     }
+
+    private final KeyguardUpdateMonitorCallback mUpdateCallback =
+            new KeyguardUpdateMonitorCallback() {
+                @Override
+                public void onDreamingStateChanged(boolean dreaming) {
+                    if (dreaming) {
+                        maybeEscalateHeadsUp();
+                    }
+                }
+
+                // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by
+                //  KeyguardCoordinator
+                @Override
+                public void onStrongAuthStateChanged(int userId) {
+                    super.onStrongAuthStateChanged(userId);
+                    mNotificationsController.requestNotificationUpdate("onStrongAuthStateChanged");
+                }
+            };
+
+
+    private final FalsingManager.FalsingBeliefListener mFalsingBeliefListener =
+            new FalsingManager.FalsingBeliefListener() {
+                @Override
+                public void onFalse() {
+                    // Hides quick settings, bouncer, and quick-quick settings.
+                    mStatusBarKeyguardViewManager.reset(true);
+                }
+            };
+
+    // Notifies StatusBarKeyguardViewManager every time the keyguard transition is over,
+    // this animation is tied to the scrim for historic reasons.
+    // TODO: notify when keyguard has faded away instead of the scrim.
+    private final ScrimController.Callback mUnlockScrimCallback = new ScrimController
+            .Callback() {
+        @Override
+        public void onFinished() {
+            if (mStatusBarKeyguardViewManager == null) {
+                Log.w(TAG, "Tried to notify keyguard visibility when "
+                        + "mStatusBarKeyguardViewManager was null");
+                return;
+            }
+            if (mKeyguardStateController.isKeyguardFadingAway()) {
+                mStatusBarKeyguardViewManager.onKeyguardFadedAway();
+            }
+        }
+
+        @Override
+        public void onCancelled() {
+            onFinished();
+        }
+    };
+
+    private final DeviceProvisionedListener mUserSetupObserver = new DeviceProvisionedListener() {
+        @Override
+        public void onUserSetupChanged() {
+            final boolean userSetup = mDeviceProvisionedController.isUserSetup(
+                    mDeviceProvisionedController.getCurrentUser());
+            Log.d(TAG, "mUserSetupObserver - DeviceProvisionedListener called for user "
+                    + mDeviceProvisionedController.getCurrentUser());
+            if (MULTIUSER_DEBUG) {
+                Log.d(TAG, String.format("User setup changed: userSetup=%s mUserSetup=%s",
+                        userSetup, mUserSetup));
+            }
+
+            if (userSetup != mUserSetup) {
+                mUserSetup = userSetup;
+                if (!mUserSetup && mStatusBarView != null) {
+                    animateCollapseQuickSettings();
+                }
+                if (mNotificationPanelViewController != null) {
+                    mNotificationPanelViewController.setUserSetupComplete(mUserSetup);
+                }
+                updateQsExpansionEnabled();
+            }
+        }
+    };
+
+    private final BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (!mWallpaperSupported) {
+                // Receiver should not have been registered at all...
+                Log.wtf(TAG, "WallpaperManager not supported");
+                return;
+            }
+            WallpaperInfo info = mWallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT);
+            final boolean deviceSupportsAodWallpaper = mContext.getResources().getBoolean(
+                    com.android.internal.R.bool.config_dozeSupportsAodWallpaper);
+            // If WallpaperInfo is null, it must be ImageWallpaper.
+            final boolean supportsAmbientMode = deviceSupportsAodWallpaper
+                    && (info != null && info.supportsAmbientMode());
+
+            mNotificationShadeWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
+            mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
+        }
+    };
+
+    BroadcastReceiver mTaskbarChangeReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mBubblesOptional.ifPresent(bubbles -> bubbles.onTaskbarChanged(intent.getExtras()));
+        }
+    };
+
+    private final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
+        @Override
+        public void onConfigChanged(Configuration newConfig) {
+            updateResources();
+            updateDisplaySize(); // populates mDisplayMetrics
+
+            if (DEBUG) {
+                Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
+            }
+
+            mViewHierarchyManager.updateRowStates();
+            mScreenPinningRequest.onConfigurationChanged();
+        }
+
+        @Override
+        public void onDensityOrFontScaleChanged() {
+            // TODO: Remove this.
+            if (mBrightnessMirrorController != null) {
+                mBrightnessMirrorController.onDensityOrFontScaleChanged();
+            }
+            // TODO: Bring these out of StatusBar.
+            mUserInfoControllerImpl.onDensityOrFontScaleChanged();
+            mUserSwitcherController.onDensityOrFontScaleChanged();
+            mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext);
+            mHeadsUpManager.onDensityOrFontScaleChanged();
+        }
+
+        @Override
+        public void onThemeChanged() {
+            if (mStatusBarKeyguardViewManager != null) {
+                mStatusBarKeyguardViewManager.onThemeChanged();
+            }
+            if (mAmbientIndicationContainer instanceof AutoReinflateContainer) {
+                ((AutoReinflateContainer) mAmbientIndicationContainer).inflateLayout();
+            }
+            mNotificationIconAreaController.onThemeChanged();
+        }
+
+        @Override
+        public void onOverlayChanged() {
+            if (mBrightnessMirrorController != null) {
+                mBrightnessMirrorController.onOverlayChanged();
+            }
+            // We need the new R.id.keyguard_indication_area before recreating
+            // mKeyguardIndicationController
+            mNotificationPanelViewController.onThemeChanged();
+            onThemeChanged();
+        }
+
+        @Override
+        public void onUiModeChanged() {
+            if (mBrightnessMirrorController != null) {
+                mBrightnessMirrorController.onUiModeChanged();
+            }
+        }
+    };
+
+    private StatusBarStateController.StateListener mStateListener =
+            new StatusBarStateController.StateListener() {
+                @Override
+                public void onStatePreChange(int oldState, int newState) {
+                    // If we're visible and switched to SHADE_LOCKED (the user dragged
+                    // down on the lockscreen), clear notification LED, vibration,
+                    // ringing.
+                    // Other transitions are covered in handleVisibleToUserChanged().
+                    if (mVisible && (newState == StatusBarState.SHADE_LOCKED
+                            || mStatusBarStateController.goingToFullShade())) {
+                        clearNotificationEffects();
+                    }
+                    if (newState == StatusBarState.KEYGUARD) {
+                        mRemoteInputManager.onPanelCollapsed();
+                        maybeEscalateHeadsUp();
+                    }
+                }
+
+                @Override
+                public void onStateChanged(int newState) {
+                    mState = newState;
+                    updateReportRejectedTouchVisibility();
+                    mDozeServiceHost.updateDozing();
+                    updateTheme();
+                    mNavigationBarController.touchAutoDim(mDisplayId);
+                    Trace.beginSection("StatusBar#updateKeyguardState");
+                    if (mState == StatusBarState.KEYGUARD && mStatusBarView != null) {
+                        mStatusBarView.removePendingHideExpandedRunnables();
+                    }
+                    updateDozingState();
+                    checkBarModes();
+                    updateScrimController();
+                    mPresenter.updateMediaMetaData(false, mState != StatusBarState.KEYGUARD);
+                    updateKeyguardState();
+                    Trace.endSection();
+                }
+
+                @Override
+                public void onDozeAmountChanged(float linear, float eased) {
+                    if (mFeatureFlags.useNewLockscreenAnimations()
+                            && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
+                        mLightRevealScrim.setRevealAmount(1f - linear);
+                    }
+                }
+
+                @Override
+                public void onDozingChanged(boolean isDozing) {
+                    Trace.beginSection("StatusBar#updateDozing");
+                    mDozing = isDozing;
+
+                    // Collapse the notification panel if open
+                    boolean dozingAnimated = mDozeServiceHost.getDozingRequested()
+                            && mDozeParameters.shouldControlScreenOff();
+                    mNotificationPanelViewController.resetViews(dozingAnimated);
+
+                    updateQsExpansionEnabled();
+                    mKeyguardViewMediator.setDozing(mDozing);
+
+                    mNotificationsController.requestNotificationUpdate("onDozingChanged");
+                    updateDozingState();
+                    mDozeServiceHost.updateDozing();
+                    updateScrimController();
+                    updateReportRejectedTouchVisibility();
+                    Trace.endSection();
+                }
+            };
+
+    private final BatteryController.BatteryStateChangeCallback mBatteryStateChangeCallback =
+            new BatteryController.BatteryStateChangeCallback() {
+                @Override
+                public void onPowerSaveChanged(boolean isPowerSave) {
+                    mMainExecutor.execute(mCheckBarModes);
+                    if (mDozeServiceHost != null) {
+                        mDozeServiceHost.firePowerSaveChanged(isPowerSave);
+                    }
+                }
+            };
+
+    private final ActivityLaunchAnimator.Callback mKeyguardHandler =
+            new ActivityLaunchAnimator.Callback() {
+                @Override
+                public boolean isOnKeyguard() {
+                    return mKeyguardStateController.isShowing();
+                }
+
+                @Override
+                public void hideKeyguardWithAnimation(IRemoteAnimationRunner runner) {
+                    // We post to the main thread for 2 reasons:
+                    //   1. KeyguardViewMediator is not thread-safe.
+                    //   2. To ensure that ViewMediatorCallback#keyguardDonePending is called before
+                    //      ViewMediatorCallback#readyForKeyguardDone. The wrong order could occur
+                    //      when doing
+                    //      dismissKeyguardThenExecute { hideKeyguardWithAnimation(runner) }.
+                    mMainExecutor.execute(() -> mKeyguardViewMediator.hideWithAnimation(runner));
+                }
+
+                @Override
+                public void setBlursDisabledForAppLaunch(boolean disabled) {
+                    mKeyguardViewMediator.setBlursDisabledForAppLaunch(disabled);
+                }
+
+                @Override
+                public int getBackgroundColor(TaskInfo task) {
+                    if (!mStartingSurfaceOptional.isPresent()) {
+                        Log.w(TAG, "No starting surface, defaulting to SystemBGColor");
+                        return SplashscreenContentDrawer.getSystemBGColor();
+                    }
+
+                    return mStartingSurfaceOptional.get().getBackgroundColor(task);
+                }
+            };
+
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
new file mode 100644
index 0000000..6a510c9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
@@ -0,0 +1,671 @@
+/*
+ * 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.phone;
+
+import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
+import static android.app.StatusBarManager.windowStateToString;
+import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.InsetsState.containsType;
+
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
+import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
+
+import android.annotation.Nullable;
+import android.app.StatusBarManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.media.AudioAttributes;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.util.Log;
+import android.util.Slog;
+import android.view.InsetsState.InternalInsetsType;
+import android.view.InsetsVisibilities;
+import android.view.KeyEvent;
+import android.view.WindowInsetsController.Appearance;
+import android.view.WindowInsetsController.Behavior;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.view.AppearanceRegion;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.R;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.camera.CameraIntents;
+import com.android.systemui.dagger.qualifiers.DisplayId;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.qs.QSPanelController;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
+import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
+
+import java.util.Optional;
+
+import javax.inject.Inject;
+
+/** */
+@StatusBarComponent.StatusBarScope
+public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
+    private final StatusBar mStatusBar;
+    private final Context mContext;
+    private final ShadeController mShadeController;
+    private final CommandQueue mCommandQueue;
+    private final NotificationPanelViewController mNotificationPanelViewController;
+    private final Optional<LegacySplitScreen> mSplitScreenOptional;
+    private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
+    private final MetricsLogger mMetricsLogger;
+    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private final KeyguardStateController mKeyguardStateController;
+    private final HeadsUpManager mHeadsUpManager;
+    private final WakefulnessLifecycle mWakefulnessLifecycle;
+    private final DeviceProvisionedController mDeviceProvisionedController;
+    private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+    private final AssistManager mAssistManager;
+    private final DozeServiceHost mDozeServiceHost;
+    private final SysuiStatusBarStateController mStatusBarStateController;
+    private final NotificationShadeWindowView mNotificationShadeWindowView;
+    private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
+    private final PowerManager mPowerManager;
+    private final VibratorHelper mVibratorHelper;
+    private final Optional<Vibrator> mVibratorOptional;
+    private final LightBarController mLightBarController;
+    private final int mDisplayId;
+    private final boolean mVibrateOnOpening;
+    private final VibrationEffect mCameraLaunchGestureVibrationEffect;
+
+
+    private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
+            .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+            .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+            .build();
+
+    @Inject
+    StatusBarCommandQueueCallbacks(
+            StatusBar statusBar,
+            Context context,
+            @Main Resources resources,
+            ShadeController shadeController,
+            CommandQueue commandQueue,
+            NotificationPanelViewController notificationPanelViewController,
+            Optional<LegacySplitScreen> splitScreenOptional,
+            RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
+            MetricsLogger metricsLogger,
+            KeyguardUpdateMonitor keyguardUpdateMonitor,
+            KeyguardStateController keyguardStateController,
+            HeadsUpManager headsUpManager,
+            WakefulnessLifecycle wakefulnessLifecycle,
+            DeviceProvisionedController deviceProvisionedController,
+            StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+            AssistManager assistManager,
+            DozeServiceHost dozeServiceHost,
+            SysuiStatusBarStateController statusBarStateController,
+            NotificationShadeWindowView notificationShadeWindowView,
+            NotificationStackScrollLayoutController notificationStackScrollLayoutController,
+            PowerManager powerManager,
+            VibratorHelper vibratorHelper,
+            Optional<Vibrator> vibratorOptional,
+            LightBarController lightBarController,
+            @DisplayId int displayId) {
+
+        mStatusBar = statusBar;
+        mContext = context;
+        mShadeController = shadeController;
+        mCommandQueue = commandQueue;
+        mNotificationPanelViewController = notificationPanelViewController;
+        mSplitScreenOptional = splitScreenOptional;
+        mRemoteInputQuickSettingsDisabler = remoteInputQuickSettingsDisabler;
+        mMetricsLogger = metricsLogger;
+        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        mKeyguardStateController = keyguardStateController;
+        mHeadsUpManager = headsUpManager;
+        mWakefulnessLifecycle = wakefulnessLifecycle;
+        mDeviceProvisionedController = deviceProvisionedController;
+        mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+        mAssistManager = assistManager;
+        mDozeServiceHost = dozeServiceHost;
+        mStatusBarStateController = statusBarStateController;
+        mNotificationShadeWindowView = notificationShadeWindowView;
+        mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
+        mPowerManager = powerManager;
+        mVibratorHelper = vibratorHelper;
+        mVibratorOptional = vibratorOptional;
+        mLightBarController = lightBarController;
+        mDisplayId = displayId;
+
+        mVibrateOnOpening = resources.getBoolean(R.bool.config_vibrateOnIconAnimation);
+        mCameraLaunchGestureVibrationEffect = getCameraGestureVibrationEffect(
+                mVibratorOptional, resources);
+    }
+
+    @Override
+    public void abortTransient(int displayId, @InternalInsetsType int[] types) {
+        if (displayId != mDisplayId) {
+            return;
+        }
+        if (!containsType(types, ITYPE_STATUS_BAR)) {
+            return;
+        }
+        mStatusBar.clearTransient();
+    }
+
+    @Override
+    public void addQsTile(ComponentName tile) {
+        QSPanelController qsPanelController = mStatusBar.getQSPanelController();
+        if (qsPanelController != null && qsPanelController.getHost() != null) {
+            qsPanelController.getHost().addTile(tile);
+        }
+    }
+
+    @Override
+    public void remQsTile(ComponentName tile) {
+        QSPanelController qsPanelController = mStatusBar.getQSPanelController();
+        if (qsPanelController != null && qsPanelController.getHost() != null) {
+            qsPanelController.getHost().removeTile(tile);
+        }
+    }
+
+    @Override
+    public void clickTile(ComponentName tile) {
+        QSPanelController qsPanelController = mStatusBar.getQSPanelController();
+        if (qsPanelController != null) {
+            qsPanelController.clickTile(tile);
+        }
+    }
+
+    @Override
+    public void animateCollapsePanels(int flags, boolean force) {
+        mShadeController.animateCollapsePanels(flags, force, false /* delayed */,
+                1.0f /* speedUpFactor */);
+    }
+
+    @Override
+    public void animateExpandNotificationsPanel() {
+        if (StatusBar.SPEW) {
+            Log.d(StatusBar.TAG,
+                    "animateExpand: mExpandedVisible=" + mStatusBar.isExpandedVisible());
+        }
+        if (!mCommandQueue.panelsEnabled()) {
+            return;
+        }
+
+        mNotificationPanelViewController.expandWithoutQs();
+    }
+
+    @Override
+    public void animateExpandSettingsPanel(@Nullable String subPanel) {
+        if (StatusBar.SPEW) {
+            Log.d(StatusBar.TAG,
+                    "animateExpand: mExpandedVisible=" + mStatusBar.isExpandedVisible());
+        }
+        if (!mCommandQueue.panelsEnabled()) {
+            return;
+        }
+
+        // Settings are not available in setup
+        if (!mDeviceProvisionedController.isCurrentUserSetup()) return;
+
+
+        QSPanelController qsPanelController = mStatusBar.getQSPanelController();
+        if (subPanel != null && qsPanelController != null) {
+            qsPanelController.openDetails(subPanel);
+        }
+        mNotificationPanelViewController.expandWithQs();
+    }
+
+    @Override
+    public void appTransitionCancelled(int displayId) {
+        if (displayId == mDisplayId) {
+            mSplitScreenOptional.ifPresent(LegacySplitScreen::onAppTransitionFinished);
+        }
+    }
+
+    @Override
+    public void appTransitionFinished(int displayId) {
+        if (displayId == mDisplayId) {
+            mSplitScreenOptional.ifPresent(LegacySplitScreen::onAppTransitionFinished);
+        }
+    }
+
+    @Override
+    public void dismissKeyboardShortcutsMenu() {
+        mStatusBar.resendMessage(StatusBar.MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU);
+    }
+    /**
+     * State is one or more of the DISABLE constants from StatusBarManager.
+     */
+    @Override
+    public void disable(int displayId, int state1, int state2, boolean animate) {
+        if (displayId != mDisplayId) {
+            return;
+        }
+        state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2);
+
+        final int old1 = mStatusBar.getDisabled1();
+        final int diff1 = state1 ^ old1;
+        mStatusBar.setDisabled1(state1);
+
+        final int old2 = mStatusBar.getDisabled2();
+        final int diff2 = state2 ^ old2;
+        mStatusBar.setDisabled2(state2);
+
+        if (StatusBar.DEBUG) {
+            Log.d(StatusBar.TAG, String.format("disable1: 0x%08x -> 0x%08x (diff1: 0x%08x)",
+                    old1, state1, diff1));
+            Log.d(StatusBar.TAG, String.format("disable2: 0x%08x -> 0x%08x (diff2: 0x%08x)",
+                    old2, state2, diff2));
+        }
+
+        StringBuilder flagdbg = new StringBuilder();
+        flagdbg.append("disable<");
+        flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_EXPAND))               ? 'E' : 'e');
+        flagdbg.append(0 != ((diff1  & StatusBarManager.DISABLE_EXPAND))               ? '!' : ' ');
+        flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS))   ? 'I' : 'i');
+        flagdbg.append(0 != ((diff1  & StatusBarManager.DISABLE_NOTIFICATION_ICONS))   ? '!' : ' ');
+        flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS))  ? 'A' : 'a');
+        flagdbg.append(0 != ((diff1  & StatusBarManager.DISABLE_NOTIFICATION_ALERTS))  ? '!' : ' ');
+        flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_SYSTEM_INFO))          ? 'S' : 's');
+        flagdbg.append(0 != ((diff1  & StatusBarManager.DISABLE_SYSTEM_INFO))          ? '!' : ' ');
+        flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_BACK))                 ? 'B' : 'b');
+        flagdbg.append(0 != ((diff1  & StatusBarManager.DISABLE_BACK))                 ? '!' : ' ');
+        flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_HOME))                 ? 'H' : 'h');
+        flagdbg.append(0 != ((diff1  & StatusBarManager.DISABLE_HOME))                 ? '!' : ' ');
+        flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_RECENT))               ? 'R' : 'r');
+        flagdbg.append(0 != ((diff1  & StatusBarManager.DISABLE_RECENT))               ? '!' : ' ');
+        flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_CLOCK))                ? 'C' : 'c');
+        flagdbg.append(0 != ((diff1  & StatusBarManager.DISABLE_CLOCK))                ? '!' : ' ');
+        flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_SEARCH))               ? 'S' : 's');
+        flagdbg.append(0 != ((diff1  & StatusBarManager.DISABLE_SEARCH))               ? '!' : ' ');
+        flagdbg.append("> disable2<");
+        flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_QUICK_SETTINGS))      ? 'Q' : 'q');
+        flagdbg.append(0 != ((diff2  & StatusBarManager.DISABLE2_QUICK_SETTINGS))      ? '!' : ' ');
+        flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_SYSTEM_ICONS))        ? 'I' : 'i');
+        flagdbg.append(0 != ((diff2  & StatusBarManager.DISABLE2_SYSTEM_ICONS))        ? '!' : ' ');
+        flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE))  ? 'N' : 'n');
+        flagdbg.append(0 != ((diff2  & StatusBarManager.DISABLE2_NOTIFICATION_SHADE))  ? '!' : ' ');
+        flagdbg.append('>');
+        Log.d(StatusBar.TAG, flagdbg.toString());
+
+        if ((diff1 & StatusBarManager.DISABLE_EXPAND) != 0) {
+            if ((state1 & StatusBarManager.DISABLE_EXPAND) != 0) {
+                mShadeController.animateCollapsePanels();
+            }
+        }
+
+        if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
+            if (mStatusBar.areNotificationAlertsDisabled()) {
+                mHeadsUpManager.releaseAllImmediately();
+            }
+        }
+
+        if ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) {
+            mStatusBar.updateQsExpansionEnabled();
+        }
+
+        if ((diff2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
+            mStatusBar.updateQsExpansionEnabled();
+            if ((state2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
+                mShadeController.animateCollapsePanels();
+            }
+        }
+    }
+
+    /**
+     * Called for system navigation gestures. First action opens the panel, second opens
+     * settings. Down action closes the entire panel.
+     */
+    @Override
+    public void handleSystemKey(int key) {
+        if (StatusBar.SPEW) {
+            Log.d(StatusBar.TAG, "handleNavigationKey: " + key);
+        }
+        if (!mCommandQueue.panelsEnabled() || !mKeyguardUpdateMonitor.isDeviceInteractive()
+                || mKeyguardStateController.isShowing() && !mKeyguardStateController.isOccluded()) {
+            return;
+        }
+
+        // Panels are not available in setup
+        if (!mDeviceProvisionedController.isCurrentUserSetup()) return;
+
+        if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP == key) {
+            mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_UP);
+            mNotificationPanelViewController.collapse(
+                    false /* delayed */, 1.0f /* speedUpFactor */);
+        } else if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN == key) {
+            mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_DOWN);
+            if (mNotificationPanelViewController.isFullyCollapsed()) {
+                if (mVibrateOnOpening) {
+                    mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
+                }
+                mNotificationPanelViewController.expand(true /* animate */);
+                mNotificationStackScrollLayoutController.setWillExpand(true);
+                mHeadsUpManager.unpinAll(true /* userUnpinned */);
+                mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN, 1);
+            } else if (!mNotificationPanelViewController.isInSettings()
+                    && !mNotificationPanelViewController.isExpanding()) {
+                mNotificationPanelViewController.flingSettings(0 /* velocity */,
+                        NotificationPanelView.FLING_EXPAND);
+                mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN_QS, 1);
+            }
+        }
+
+    }
+
+    @Override
+    public void onCameraLaunchGestureDetected(int source) {
+        mStatusBar.setLastCameraLaunchSource(source);
+        if (mStatusBar.isGoingToSleep()) {
+            if (StatusBar.DEBUG_CAMERA_LIFT) {
+                Slog.d(StatusBar.TAG, "Finish going to sleep before launching camera");
+            }
+            mStatusBar.setLaunchCameraOnFinishedGoingToSleep(true);
+            return;
+        }
+        if (!mNotificationPanelViewController.canCameraGestureBeLaunched()) {
+            if (StatusBar.DEBUG_CAMERA_LIFT) {
+                Slog.d(StatusBar.TAG, "Can't launch camera right now");
+            }
+            return;
+        }
+        if (!mStatusBar.isDeviceInteractive()) {
+            mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_CAMERA_LAUNCH,
+                    "com.android.systemui:CAMERA_GESTURE");
+        }
+        vibrateForCameraGesture();
+
+        if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) {
+            Log.v(StatusBar.TAG, "Camera launch");
+            mKeyguardUpdateMonitor.onCameraLaunched();
+        }
+
+        if (!mStatusBarKeyguardViewManager.isShowing()) {
+            final Intent cameraIntent = CameraIntents.getInsecureCameraIntent(mContext);
+            mStatusBar.startActivityDismissingKeyguard(cameraIntent,
+                    false /* onlyProvisioned */, true /* dismissShade */,
+                    true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0,
+                    null /* animationController */);
+        } else {
+            if (!mStatusBar.isDeviceInteractive()) {
+                // Avoid flickering of the scrim when we instant launch the camera and the bouncer
+                // comes on.
+                mStatusBar.acquireGestureWakeLock(StatusBar.LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
+            }
+            if (isWakingUpOrAwake()) {
+                if (StatusBar.DEBUG_CAMERA_LIFT) {
+                    Slog.d(StatusBar.TAG, "Launching camera");
+                }
+                if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
+                    mStatusBarKeyguardViewManager.reset(true /* hide */);
+                }
+                mNotificationPanelViewController.launchCamera(
+                        mStatusBar.isDeviceInteractive() /* animate */, source);
+                mStatusBar.updateScrimController();
+            } else {
+                // We need to defer the camera launch until the screen comes on, since otherwise
+                // we will dismiss us too early since we are waiting on an activity to be drawn and
+                // incorrectly get notified because of the screen on event (which resumes and pauses
+                // some activities)
+                if (StatusBar.DEBUG_CAMERA_LIFT) {
+                    Slog.d(StatusBar.TAG, "Deferring until screen turns on");
+                }
+                mStatusBar.setLaunchCameraOnFinishedWaking(true);
+            }
+        }
+    }
+
+    @Override
+    public void onEmergencyActionLaunchGestureDetected() {
+        Intent emergencyIntent = mStatusBar.getEmergencyActionIntent();
+
+        if (emergencyIntent == null) {
+            Log.wtf(StatusBar.TAG, "Couldn't find an app to process the emergency intent.");
+            return;
+        }
+
+        if (isGoingToSleep()) {
+            mStatusBar.setLaunchEmergencyActionOnFinishedGoingToSleep(true);
+            return;
+        }
+
+        if (!mStatusBar.isDeviceInteractive()) {
+            mPowerManager.wakeUp(SystemClock.uptimeMillis(),
+                    PowerManager.WAKE_REASON_GESTURE,
+                    "com.android.systemui:EMERGENCY_GESTURE");
+        }
+        // TODO(b/169087248) Possibly add haptics here for emergency action. Currently disabled for
+        // app-side haptic experimentation.
+
+        if (!mStatusBarKeyguardViewManager.isShowing()) {
+            mStatusBar.startActivityDismissingKeyguard(emergencyIntent,
+                    false /* onlyProvisioned */, true /* dismissShade */,
+                    true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0,
+                    null /* animationController */);
+            return;
+        }
+
+        if (!mStatusBar.isDeviceInteractive()) {
+            // Avoid flickering of the scrim when we instant launch the camera and the bouncer
+            // comes on.
+            mStatusBar.acquireGestureWakeLock(StatusBar.LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
+        }
+
+        if (isWakingUpOrAwake()) {
+            if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
+                mStatusBarKeyguardViewManager.reset(true /* hide */);
+            }
+            mContext.startActivityAsUser(emergencyIntent, UserHandle.CURRENT);
+            return;
+        }
+        // We need to defer the emergency action launch until the screen comes on, since otherwise
+        // we will dismiss us too early since we are waiting on an activity to be drawn and
+        // incorrectly get notified because of the screen on event (which resumes and pauses
+        // some activities)
+        mStatusBar.setLaunchEmergencyActionOnFinishedWaking(true);
+    }
+
+    @Override
+    public void onRecentsAnimationStateChanged(boolean running) {
+        mStatusBar.setInteracting(StatusBarManager.WINDOW_NAVIGATION_BAR, running);
+    }
+
+
+    @Override
+    public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
+            AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
+            @Behavior int behavior, InsetsVisibilities requestedVisibilities, String packageName) {
+        if (displayId != mDisplayId) {
+            return;
+        }
+        boolean barModeChanged = mStatusBar.setAppearance(appearance);
+
+        mLightBarController.onStatusBarAppearanceChanged(appearanceRegions, barModeChanged,
+                mStatusBar.getBarMode(), navbarColorManagedByIme);
+
+        mStatusBar.updateBubblesVisibility();
+        mStatusBarStateController.setSystemBarAttributes(
+                appearance, behavior, requestedVisibilities, packageName);
+    }
+
+    @Override
+    public void showTransient(int displayId, @InternalInsetsType int[] types) {
+        if (displayId != mDisplayId) {
+            return;
+        }
+        if (!containsType(types, ITYPE_STATUS_BAR)) {
+            return;
+        }
+        mStatusBar.showTransientUnchecked();
+    }
+
+    @Override
+    public void toggleKeyboardShortcutsMenu(int deviceId) {
+        mStatusBar.resendMessage(new StatusBar.KeyboardShortcutsMessage(deviceId));
+    }
+
+    @Override
+    public void setTopAppHidesStatusBar(boolean topAppHidesStatusBar) {
+        mStatusBar.setTopHidesStatusBar(topAppHidesStatusBar);
+        if (!topAppHidesStatusBar && mStatusBar.getWereIconsJustHidden()) {
+            // Immediately update the icon hidden state, since that should only apply if we're
+            // staying fullscreen.
+            mStatusBar.setWereIconsJustHidden(false);
+            mCommandQueue.recomputeDisableFlags(mDisplayId, true);
+        }
+        mStatusBar.updateHideIconsForBouncer(true /* animate */);
+    }
+
+    @Override
+    public void setWindowState(
+            int displayId, @StatusBarManager.WindowType int window,
+            @StatusBarManager.WindowVisibleState int state) {
+        if (displayId != mDisplayId) {
+            return;
+        }
+        boolean showing = state == WINDOW_STATE_SHOWING;
+        if (mNotificationShadeWindowView != null
+                && window == StatusBarManager.WINDOW_STATUS_BAR
+                && !mStatusBar.isSameStatusBarState(state)) {
+            mStatusBar.setWindowState(state);
+            if (StatusBar.DEBUG_WINDOW_STATE) {
+                Log.d(StatusBar.TAG, "Status bar " + windowStateToString(state));
+            }
+            if (mStatusBar.getStatusBarView() != null) {
+                if (!showing && mStatusBarStateController.getState() == StatusBarState.SHADE) {
+                    mStatusBar.getStatusBarView().collapsePanel(
+                            false /* animate */, false /* delayed */, 1.0f /* speedUpFactor */);
+                }
+
+                mStatusBar.updateHideIconsForBouncer(false /* animate */);
+            }
+        }
+
+        mStatusBar.updateBubblesVisibility();
+    }
+
+    @Override
+    public void showAssistDisclosure() {
+        mAssistManager.showDisclosure();
+    }
+
+    @Override
+    public void showPinningEnterExitToast(boolean entering) {
+        if (mStatusBar.getNavigationBarView() != null) {
+            mStatusBar.getNavigationBarView().showPinningEnterExitToast(entering);
+        }
+    }
+
+    @Override
+    public void showPinningEscapeToast() {
+        if (mStatusBar.getNavigationBarView() != null) {
+            mStatusBar.getNavigationBarView().showPinningEscapeToast();
+        }
+    }
+
+    @Override
+    public void showScreenPinningRequest(int taskId) {
+        if (mKeyguardStateController.isShowing()) {
+            // Don't allow apps to trigger this from keyguard.
+            return;
+        }
+        // Show screen pinning request, since this comes from an app, show 'no thanks', button.
+        mStatusBar.showScreenPinningRequest(taskId, true);
+    }
+
+    @Override
+    public void showWirelessChargingAnimation(int batteryLevel) {
+        mStatusBar.showWirelessChargingAnimation(batteryLevel);
+    }
+
+    @Override
+    public void startAssist(Bundle args) {
+        mAssistManager.startAssist(args);
+    }
+
+    @Override
+    public void suppressAmbientDisplay(boolean suppressed) {
+        mDozeServiceHost.setDozeSuppressed(suppressed);
+    }
+
+    @Override
+    public void togglePanel() {
+        if (mStatusBar.isPanelExpanded()) {
+            mShadeController.animateCollapsePanels();
+        } else {
+            animateExpandNotificationsPanel();
+        }
+    }
+
+    @Override
+    public void toggleSplitScreen() {
+        mStatusBar.toggleSplitScreenMode(-1 /* metricsDockAction */, -1 /* metricsUndockAction */);
+    }
+
+    private boolean isGoingToSleep() {
+        return mWakefulnessLifecycle.getWakefulness()
+                == WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP;
+    }
+
+    private boolean isWakingUpOrAwake() {
+        return mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_AWAKE
+                || mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_WAKING;
+    }
+
+    private void vibrateForCameraGesture() {
+        // Make sure to pass -1 for repeat so VibratorService doesn't stop us when going to sleep.
+        mVibratorOptional.ifPresent(
+                v -> v.vibrate(mCameraLaunchGestureVibrationEffect, VIBRATION_ATTRIBUTES));
+    }
+
+    private static VibrationEffect getCameraGestureVibrationEffect(
+            Optional<Vibrator> vibratorOptional, Resources resources) {
+        if (vibratorOptional.isPresent() && vibratorOptional.get().areAllPrimitivesSupported(
+                VibrationEffect.Composition.PRIMITIVE_QUICK_RISE,
+                VibrationEffect.Composition.PRIMITIVE_CLICK)) {
+            return VibrationEffect.startComposition()
+                    .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE)
+                    .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 50)
+                    .compose();
+        }
+        if (vibratorOptional.isPresent() && vibratorOptional.get().hasAmplitudeControl()) {
+            return VibrationEffect.createWaveform(
+                    StatusBar.CAMERA_LAUNCH_GESTURE_VIBRATION_TIMINGS,
+                    StatusBar.CAMERA_LAUNCH_GESTURE_VIBRATION_AMPLITUDES,
+                    /* repeat= */ -1);
+        }
+
+        int[] pattern = resources.getIntArray(R.array.config_cameraLaunchGestureVibePattern);
+        long[] timings = new long[pattern.length];
+        for (int i = 0; i < pattern.length; i++) {
+            timings[i] = pattern[i];
+        }
+        return VibrationEffect.createWaveform(timings, /* repeat= */ -1);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
index fe1f63a..515094b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
@@ -19,11 +19,10 @@
 import android.content.Context
 import android.content.res.Resources
 import android.graphics.Rect
-import android.util.Log
+import android.util.LruCache
 import android.util.Pair
 import android.view.DisplayCutout
 import android.view.View.LAYOUT_DIRECTION_RTL
-import android.view.WindowManager
 import android.view.WindowMetrics
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.Dumpable
@@ -38,6 +37,7 @@
 import com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE
 import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN
 import com.android.systemui.util.leak.RotationUtils.Rotation
+import com.android.systemui.util.leak.RotationUtils.getResourcesForRotation
 import java.io.FileDescriptor
 import java.io.PrintWriter
 import java.lang.Math.max
@@ -61,13 +61,14 @@
 class StatusBarContentInsetsProvider @Inject constructor(
     val context: Context,
     val configurationController: ConfigurationController,
-    val windowManager: WindowManager,
     val dumpManager: DumpManager
 ) : CallbackController<StatusBarContentInsetsChangedListener>,
         ConfigurationController.ConfigurationListener,
         Dumpable {
-    // Indexed by @Rotation
-    private val insetsByCorner = arrayOfNulls<Rect>(4)
+
+    // Limit cache size as potentially we may connect large number of displays
+    // (e.g. network displays)
+    private val insetsCache = LruCache<CacheKey, Rect>(MAX_CACHE_SIZE)
     private val listeners = mutableSetOf<StatusBarContentInsetsChangedListener>()
 
     init {
@@ -91,12 +92,12 @@
         clearCachedInsets()
     }
 
-    private fun clearCachedInsets() {
-        insetsByCorner[0] = null
-        insetsByCorner[1] = null
-        insetsByCorner[2] = null
-        insetsByCorner[3] = null
+    override fun onMaxBoundsChanged() {
+        notifyInsetsChanged()
+    }
 
+    private fun clearCachedInsets() {
+        insetsCache.evictAll()
         notifyInsetsChanged()
     }
 
@@ -111,10 +112,10 @@
      * dot in the coordinates relative to the given rotation.
      */
     fun getBoundingRectForPrivacyChipForRotation(@Rotation rotation: Int): Rect {
-        var insets = insetsByCorner[rotation]
-        val rotatedResources = RotationUtils.getResourcesForRotation(rotation, context)
+        var insets = insetsCache[getCacheKey(rotation = rotation)]
+        val rotatedResources = getResourcesForRotation(rotation, context)
         if (insets == null) {
-            insets = getAndSetInsetsForRotation(rotation, rotatedResources)
+            insets = getStatusBarContentInsetsForRotation(rotation, rotatedResources)
         }
 
         val dotWidth = rotatedResources.getDimensionPixelSize(R.dimen.ongoing_appops_dot_diameter)
@@ -129,24 +130,16 @@
      * Calculates the necessary left and right locations for the status bar contents invariant of
      * the current device rotation, in the target rotation's coordinates
      */
-    fun getStatusBarContentInsetsForRotation(@Rotation rotation: Int): Rect {
-        var insets = insetsByCorner[rotation]
-        if (insets == null) {
-            val rotatedResources = RotationUtils.getResourcesForRotation(rotation, context)
-            insets = getAndSetInsetsForRotation(rotation, rotatedResources)
-        }
-
-        return insets
-    }
-
-    private fun getAndSetInsetsForRotation(
-        @Rotation rot: Int,
-        rotatedResources: Resources
+    @JvmOverloads
+    fun getStatusBarContentInsetsForRotation(
+        @Rotation rotation: Int,
+        rotatedResources: Resources = getResourcesForRotation(rotation, context)
     ): Rect {
-        val insets = getCalculatedInsetsForRotation(rot, rotatedResources)
-        insetsByCorner[rot] = insets
-
-        return insets
+        val key = getCacheKey(rotation = rotation)
+        return insetsCache[key] ?: getCalculatedInsetsForRotation(rotation, rotatedResources)
+            .also {
+                insetsCache.put(key, it)
+            }
     }
 
     private fun getCalculatedInsetsForRotation(
@@ -176,17 +169,29 @@
                 currentRotation,
                 targetRotation,
                 dc,
-                windowManager.maximumWindowMetrics,
+                context.resources.configuration.windowConfiguration.maxBounds,
                 rotatedResources.getDimensionPixelSize(R.dimen.status_bar_height),
                 minLeft,
                 minRight)
     }
 
     override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
-        insetsByCorner.forEachIndexed { index, rect ->
-            pw.println("${RotationUtils.toString(index)} -> $rect")
+        insetsCache.snapshot().forEach { (key, rect) ->
+            pw.println("$key -> $rect")
         }
+        pw.println(insetsCache)
     }
+
+    private fun getCacheKey(@Rotation rotation: Int): CacheKey =
+        CacheKey(
+            uniqueDisplayId = context.display.uniqueId,
+            rotation = rotation
+        )
+
+    private data class CacheKey(
+        val uniqueDisplayId: String,
+        @Rotation val rotation: Int
+    )
 }
 
 interface StatusBarContentInsetsChangedListener {
@@ -194,10 +199,9 @@
 }
 
 private const val TAG = "StatusBarInsetsProvider"
+private const val MAX_CACHE_SIZE = 16
 
-private fun getRotationZeroDisplayBounds(wm: WindowMetrics, @Rotation exactRotation: Int): Rect {
-    val bounds = wm.bounds
-
+private fun getRotationZeroDisplayBounds(bounds: Rect, @Rotation exactRotation: Int): Rect {
     if (exactRotation == ROTATION_NONE || exactRotation == ROTATION_UPSIDE_DOWN) {
         return bounds
     }
@@ -243,7 +247,7 @@
     @Rotation currentRotation: Int,
     @Rotation targetRotation: Int,
     displayCutout: DisplayCutout?,
-    windowMetrics: WindowMetrics,
+    maxBounds: Rect,
     statusBarHeight: Int,
     minLeft: Int,
     minRight: Int
@@ -254,16 +258,15 @@
     val right = if (isRtl) paddingStart else paddingEnd
      */
 
-    val rotZeroBounds = getRotationZeroDisplayBounds(windowMetrics, currentRotation)
-    val currentBounds = windowMetrics.bounds
+    val rotZeroBounds = getRotationZeroDisplayBounds(maxBounds, currentRotation)
 
     val sbLeftRight = getStatusBarLeftRight(
             displayCutout,
             statusBarHeight,
             rotZeroBounds.right,
             rotZeroBounds.bottom,
-            currentBounds.width(),
-            currentBounds.height(),
+            maxBounds.width(),
+            maxBounds.height(),
             minLeft,
             minRight,
             targetRotation,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarDemoMode.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarDemoMode.java
index 8938f96..e642b2e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarDemoMode.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarDemoMode.java
@@ -113,7 +113,6 @@
         }
     }
 
-
     private void dispatchDemoModeStartedToView(int id) {
         View statusBarView = mStatusBar.getStatusBarView();
         if (statusBarView == null) return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
new file mode 100644
index 0000000..ca877af
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
@@ -0,0 +1,133 @@
+/*
+ * 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.phone;
+
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.init.NotificationsController;
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+
+import javax.inject.Inject;
+
+/** Ties the {@link StatusBar} to {@link com.android.systemui.statusbar.policy.HeadsUpManager}. */
+@StatusBarComponent.StatusBarScope
+public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener {
+    private final NotificationShadeWindowController mNotificationShadeWindowController;
+    private final StatusBarWindowController mStatusBarWindowController;
+    private final NotificationPanelViewController mNotificationPanelViewController;
+    private final KeyguardBypassController mKeyguardBypassController;
+    private final HeadsUpManagerPhone mHeadsUpManager;
+    private final StatusBarStateController mStatusBarStateController;
+    private final NotificationRemoteInputManager mNotificationRemoteInputManager;
+    private final NotificationsController mNotificationsController;
+    private final DozeServiceHost mDozeServiceHost;
+    private final DozeScrimController mDozeScrimController;
+
+    @Inject
+    StatusBarHeadsUpChangeListener(
+            NotificationShadeWindowController notificationShadeWindowController,
+            StatusBarWindowController statusBarWindowController,
+            NotificationPanelViewController notificationPanelViewController,
+            KeyguardBypassController keyguardBypassController,
+            HeadsUpManagerPhone headsUpManager,
+            StatusBarStateController statusBarStateController,
+            NotificationRemoteInputManager notificationRemoteInputManager,
+            NotificationsController notificationsController,
+            DozeServiceHost dozeServiceHost,
+            DozeScrimController dozeScrimController) {
+
+        mNotificationShadeWindowController = notificationShadeWindowController;
+        mStatusBarWindowController = statusBarWindowController;
+        mNotificationPanelViewController = notificationPanelViewController;
+        mKeyguardBypassController = keyguardBypassController;
+        mHeadsUpManager = headsUpManager;
+        mStatusBarStateController = statusBarStateController;
+        mNotificationRemoteInputManager = notificationRemoteInputManager;
+        mNotificationsController = notificationsController;
+        mDozeServiceHost = dozeServiceHost;
+        mDozeScrimController = dozeScrimController;
+    }
+
+    @Override
+    public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
+        if (inPinnedMode) {
+            mNotificationShadeWindowController.setHeadsUpShowing(true);
+            mStatusBarWindowController.setForceStatusBarVisible(true);
+            if (mNotificationPanelViewController.isFullyCollapsed()) {
+                // We need to ensure that the touchable region is updated before the
+                //window will be
+                // resized, in order to not catch any touches. A layout will ensure that
+                // onComputeInternalInsets will be called and after that we can
+                //resize the layout. Let's
+                // make sure that the window stays small for one frame until the
+                //touchableRegion is set.
+                mNotificationPanelViewController.getView().requestLayout();
+                mNotificationShadeWindowController.setForceWindowCollapsed(true);
+                mNotificationPanelViewController.getView().post(() -> {
+                    mNotificationShadeWindowController.setForceWindowCollapsed(false);
+                });
+            }
+        } else {
+            boolean bypassKeyguard = mKeyguardBypassController.getBypassEnabled()
+                    && mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
+            if (!mNotificationPanelViewController.isFullyCollapsed()
+                    || mNotificationPanelViewController.isTracking()
+                    || bypassKeyguard) {
+                // We are currently tracking or is open and the shade doesn't need to
+                //be kept
+                // open artificially.
+                mNotificationShadeWindowController.setHeadsUpShowing(false);
+                if (bypassKeyguard) {
+                    mStatusBarWindowController.setForceStatusBarVisible(false);
+                }
+            } else {
+                // we need to keep the panel open artificially, let's wait until the
+                //animation
+                // is finished.
+                mHeadsUpManager.setHeadsUpGoingAway(true);
+                mNotificationPanelViewController.runAfterAnimationFinished(() -> {
+                    if (!mHeadsUpManager.hasPinnedHeadsUp()) {
+                        mNotificationShadeWindowController.setHeadsUpShowing(false);
+                        mHeadsUpManager.setHeadsUpGoingAway(false);
+                    }
+                    mNotificationRemoteInputManager.onPanelCollapsed();
+                });
+            }
+        }
+    }
+
+    @Override
+    public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
+        mNotificationsController.requestNotificationUpdate("onHeadsUpStateChanged");
+        if (mStatusBarStateController.isDozing() && isHeadsUp) {
+            entry.setPulseSuppressed(false);
+            mDozeServiceHost.fireNotificationPulse(entry);
+            if (mDozeServiceHost.isPulsing()) {
+                mDozeScrimController.cancelPendingPulseTimeout();
+            }
+        }
+        if (!isHeadsUp && !mHeadsUpManager.hasNotifications()) {
+            // There are no longer any notifications to show.  We should end the
+            //pulse now.
+            mDozeScrimController.pulseOutNow();
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index dbe4c1e..48fe774 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -37,9 +37,9 @@
 import com.android.systemui.R;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.demomode.DemoModeCommandReceiver;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.StatusBarMobileView;
 import com.android.systemui.statusbar.StatusBarWifiView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 95fd886d..4316ccf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -73,6 +73,8 @@
 
 import javax.inject.Inject;
 
+import dagger.Lazy;
+
 /**
  * Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back
  * via {@link ViewMediatorCallback} to poke the wake lock and report that the keyguard is done,
@@ -111,6 +113,7 @@
     private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
     private final KeyguardMessageAreaController.Factory mKeyguardMessageAreaFactory;
     private KeyguardMessageAreaController mKeyguardMessageAreaController;
+    private final Lazy<ShadeController> mShadeController;
     private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() {
         @Override
         public void onFullyShown() {
@@ -243,7 +246,8 @@
             KeyguardBouncer.Factory keyguardBouncerFactory,
             WakefulnessLifecycle wakefulnessLifecycle,
             UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
-            KeyguardMessageAreaController.Factory keyguardMessageAreaFactory) {
+            KeyguardMessageAreaController.Factory keyguardMessageAreaFactory,
+            Lazy<ShadeController> shadeController) {
         mContext = context;
         mViewMediatorCallback = callback;
         mLockPatternUtils = lockPatternUtils;
@@ -260,6 +264,7 @@
         mWakefulnessLifecycle = wakefulnessLifecycle;
         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
         mKeyguardMessageAreaFactory = keyguardMessageAreaFactory;
+        mShadeController = shadeController;
     }
 
     @Override
@@ -303,8 +308,10 @@
      * Sets a new alt auth interceptor.
      */
     public void setAlternateAuthInterceptor(@NonNull AlternateAuthInterceptor authInterceptor) {
-        mAlternateAuthInterceptor = authInterceptor;
-        resetAlternateAuth(false);
+        if (!Objects.equals(mAlternateAuthInterceptor, authInterceptor)) {
+            mAlternateAuthInterceptor = authInterceptor;
+            resetAlternateAuth(false);
+        }
     }
 
     private void registerListeners() {
@@ -634,6 +641,18 @@
                         });
                 return;
             }
+
+            if (mStatusBar.isLaunchingActivityOverLockscreen()) {
+                mOccluded = true;
+
+                // When isLaunchingActivityOverLockscreen() is true, we know for sure that the post
+                // collapse runnables will be run.
+                mShadeController.get().addPostCollapseAction(() -> {
+                    mNotificationShadeWindowController.setKeyguardOccluded(mOccluded);
+                    reset(true /* hideBouncerWhenShowing */);
+                });
+                return;
+            }
         } else if (!occluded && mOccluded && mShowing) {
             SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED,
                     SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN);
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 1717b82..dba3b24 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -52,10 +52,10 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dagger.qualifiers.UiBackground;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationClickNotifier;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationPresenter;
@@ -71,6 +71,7 @@
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowDragController;
 import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
 import com.android.systemui.statusbar.policy.HeadsUpUtil;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -402,6 +403,53 @@
         mIsCollapsingToShowActivityOverLockscreen = false;
     }
 
+    /**
+     * Called when a notification is dropped on proper target window.
+     * Intent that is included in this entry notification,
+     * will be sent by {@link ExpandableNotificationRowDragController}
+     *
+     * @param entry notification entry that is dropped.
+     */
+    @Override
+    public void onDragSuccess(NotificationEntry entry) {
+        // this method is not responsible for intent sending.
+        // will focus follow operation only after drag-and-drop that notification.
+        NotificationVisibility.NotificationLocation location =
+                NotificationLogger.getNotificationLocation(entry);
+        final NotificationVisibility nv = NotificationVisibility.obtain(entry.getKey(),
+                entry.getRanking().getRank(), getVisibleNotificationsCount(), true, location);
+
+        // retrieve the group summary to remove with this entry before we tell NMS the
+        // notification was clicked to avoid a race condition
+        final boolean shouldAutoCancel = shouldAutoCancel(entry.getSbn());
+        final NotificationEntry summaryToRemove = shouldAutoCancel
+                ? mOnUserInteractionCallback.getGroupSummaryToDismiss(entry) : null;
+
+        String notificationKey = entry.getKey();
+        // inform NMS that the notification was clicked
+        mClickNotifier.onNotificationClick(notificationKey, nv);
+
+        if (shouldAutoCancel || mRemoteInputManager.isNotificationKeptForRemoteInputHistory(
+                notificationKey)) {
+            // Immediately remove notification from visually showing.
+            // We have to post the removal to the UI thread for synchronization.
+            mMainThreadHandler.post(() -> {
+                final Runnable removeNotification = () ->
+                        mOnUserInteractionCallback.onDismiss(
+                                entry, REASON_CLICK, summaryToRemove);
+                if (mPresenter.isCollapsing()) {
+                    // To avoid lags we're only performing the remove
+                    // after the shade is collapsed
+                    mShadeController.addPostCollapseAction(removeNotification);
+                } else {
+                    removeNotification.run();
+                }
+            });
+        }
+
+        mIsCollapsingToShowActivityOverLockscreen = false;
+    }
+
     private void expandBubbleStackOnMainThread(NotificationEntry entry) {
         if (!mBubblesManagerOptional.isPresent()) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index cb844d8..832f317 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -47,7 +47,6 @@
 import com.android.systemui.R;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
@@ -65,7 +64,6 @@
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
-import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -84,28 +82,18 @@
         ConfigurationController.ConfigurationListener,
         NotificationRowBinderImpl.BindRowCallback,
         CommandQueue.Callbacks {
-
-    private final LockscreenGestureLogger mLockscreenGestureLogger =
-            Dependency.get(LockscreenGestureLogger.class);
-
     private static final String TAG = "StatusBarNotificationPresenter";
 
     private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
     private final KeyguardStateController mKeyguardStateController;
-    private final NotificationViewHierarchyManager mViewHierarchyManager =
-            Dependency.get(NotificationViewHierarchyManager.class);
-    private final NotificationLockscreenUserManager mLockscreenUserManager =
-            Dependency.get(NotificationLockscreenUserManager.class);
-    private final SysuiStatusBarStateController mStatusBarStateController =
-            (SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class);
-    private final NotificationEntryManager mEntryManager =
-            Dependency.get(NotificationEntryManager.class);
-    private final NotificationMediaManager mMediaManager =
-            Dependency.get(NotificationMediaManager.class);
-    private final VisualStabilityManager mVisualStabilityManager =
-            Dependency.get(VisualStabilityManager.class);
-    private final NotificationGutsManager mGutsManager =
-            Dependency.get(NotificationGutsManager.class);
+    private final NotificationViewHierarchyManager mViewHierarchyManager;
+    private final NotificationLockscreenUserManager mLockscreenUserManager;
+    private final SysuiStatusBarStateController mStatusBarStateController;
+    private final NotificationEntryManager mEntryManager;
+    private final NotificationMediaManager mMediaManager;
+    private final NotificationGutsManager mGutsManager;
+    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private final LockscreenGestureLogger mLockscreenGestureLogger;
 
     private final NotificationPanelViewController mNotificationPanel;
     private final HeadsUpManagerPhone mHeadsUpManager;
@@ -144,8 +132,18 @@
             ShadeController shadeController,
             LockscreenShadeTransitionController shadeTransitionController,
             CommandQueue commandQueue,
+            NotificationViewHierarchyManager notificationViewHierarchyManager,
+            NotificationLockscreenUserManager lockscreenUserManager,
+            SysuiStatusBarStateController sysuiStatusBarStateController,
+            NotificationEntryManager notificationEntryManager,
+            NotificationMediaManager notificationMediaManager,
+            NotificationGutsManager notificationGutsManager,
+            KeyguardUpdateMonitor keyguardUpdateMonitor,
+            LockscreenGestureLogger lockscreenGestureLogger,
             InitController initController,
-            NotificationInterruptStateProvider notificationInterruptStateProvider) {
+            NotificationInterruptStateProvider notificationInterruptStateProvider,
+            NotificationRemoteInputManager remoteInputManager,
+            ConfigurationController configurationController) {
         mKeyguardStateController = keyguardStateController;
         mNotificationPanel = panel;
         mHeadsUpManager = headsUp;
@@ -156,6 +154,14 @@
         mShadeController = shadeController;
         mShadeTransitionController = shadeTransitionController;
         mCommandQueue = commandQueue;
+        mViewHierarchyManager = notificationViewHierarchyManager;
+        mLockscreenUserManager = lockscreenUserManager;
+        mStatusBarStateController = sysuiStatusBarStateController;
+        mEntryManager = notificationEntryManager;
+        mMediaManager = notificationMediaManager;
+        mGutsManager = notificationGutsManager;
+        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        mLockscreenGestureLogger = lockscreenGestureLogger;
         mAboveShelfObserver = new AboveShelfObserver(stackScrollerController.getView());
         mNotificationShadeWindowController = notificationShadeWindowController;
         mAboveShelfObserver.setListener(statusBarWindow.findViewById(
@@ -176,8 +182,6 @@
                 Slog.e(TAG, "Failed to register VR mode state listener: " + e);
             }
         }
-        NotificationRemoteInputManager remoteInputManager =
-                Dependency.get(NotificationRemoteInputManager.class);
         remoteInputManager.setUpWithCallback(
                 Dependency.get(NotificationRemoteInputManager.Callback.class),
                 mNotificationPanel.createRemoteInputDelegate());
@@ -220,14 +224,14 @@
 
             onUserSwitched(mLockscreenUserManager.getCurrentUserId());
         });
-        Dependency.get(ConfigurationController.class).addCallback(this);
+        configurationController.addCallback(this);
     }
 
     @Override
     public void onDensityOrFontScaleChanged() {
         MessagingMessage.dropCache();
         MessagingGroup.dropCache();
-        if (!Dependency.get(KeyguardUpdateMonitor.class).isSwitchingUser()) {
+        if (!mKeyguardUpdateMonitor.isSwitchingUser()) {
             updateNotificationsOnDensityOrFontScaleChanged();
         } else {
             mReinflateNotificationsOnUserSwitched = true;
@@ -236,7 +240,7 @@
 
     @Override
     public void onUiModeChanged() {
-        if (!Dependency.get(KeyguardUpdateMonitor.class).isSwitchingUser()) {
+        if (!mKeyguardUpdateMonitor.isSwitchingUser()) {
             updateNotificationOnUiModeChanged();
         } else {
             mDispatchUiModeChangeOnUserSwitched = true;
@@ -308,17 +312,11 @@
     private void onNotificationRemoved(String key, StatusBarNotification old, int reason) {
         if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
 
-        if (old != null) {
-            if (CLOSE_PANEL_WHEN_EMPTIED && !hasActiveNotifications()
-                    && !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded()) {
-                if (mStatusBarStateController.getState() == StatusBarState.SHADE
-                        && reason != NotificationListenerService.REASON_CLICK) {
-                    mCommandQueue.animateCollapsePanels();
-                } else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED
+        if (old != null && CLOSE_PANEL_WHEN_EMPTIED && !hasActiveNotifications()
+                && !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded()
+                && mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED
                         && !isCollapsing()) {
-                    mStatusBarStateController.setState(StatusBarState.KEYGUARD);
-                }
-            }
+                mStatusBarStateController.setState(StatusBarState.KEYGUARD);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index fe52281..7136432 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -25,7 +25,7 @@
 import com.android.settingslib.mobile.TelephonyIcons;
 import com.android.systemui.R;
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
 import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index 3d3b58a..aec27d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -43,7 +43,6 @@
 import com.android.systemui.R;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
 
 import javax.inject.Inject;
 
@@ -58,14 +57,13 @@
     private final Context mContext;
     private final WindowManager mWindowManager;
     private final IWindowManager mIWindowManager;
-    private final SuperStatusBarViewFactory mSuperStatusBarViewFactory;
     private final StatusBarContentInsetsProvider mContentInsetsProvider;
     private final Resources mResources;
     private int mBarHeight = -1;
     private final State mCurrentState = new State();
 
-    private ViewGroup mStatusBarView;
-    private ViewGroup mLaunchAnimationContainer;
+    private final ViewGroup mStatusBarView;
+    private final ViewGroup mLaunchAnimationContainer;
     private WindowManager.LayoutParams mLp;
     private final WindowManager.LayoutParams mLpChanged;
 
@@ -74,15 +72,14 @@
             Context context,
             WindowManager windowManager,
             IWindowManager iWindowManager,
-            SuperStatusBarViewFactory superStatusBarViewFactory,
+            StatusBarWindowView statusBarWindowView,
             StatusBarContentInsetsProvider contentInsetsProvider,
             @Main Resources resources) {
         mContext = context;
         mWindowManager = windowManager;
         mIWindowManager = iWindowManager;
         mContentInsetsProvider = contentInsetsProvider;
-        mSuperStatusBarViewFactory = superStatusBarViewFactory;
-        mStatusBarView = mSuperStatusBarViewFactory.getStatusBarWindowView();
+        mStatusBarView = statusBarWindowView;
         mLaunchAnimationContainer = mStatusBarView.findViewById(
                 R.id.status_bar_launch_animation_container);
         mLpChanged = new WindowManager.LayoutParams();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index a5b868b..6b52dca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -149,14 +149,18 @@
         lightRevealAnimationPlaying = false
         aodUiAnimationPlaying = false
 
-        // Make sure the status bar is in the correct keyguard state, forcing it if necessary. This
-        // is required if the screen off animation is cancelled, since it might be incorrectly left
-        // in the KEYGUARD or SHADE states depending on when it was cancelled and whether 'lock
-        // instantly' is enabled. We need to force it so that the state is set even if we're going
-        // from SHADE to SHADE or KEYGUARD to KEYGUARD, since we might have changed parts of the UI
-        // (such as showing AOD in the shade) without actually changing the StatusBarState. This
-        // ensures that the UI definitely reflects the desired state.
-        statusBar.updateIsKeyguard(true /* force */)
+        // If we can't control the screen off animation, we shouldn't mess with the StatusBar's
+        // keyguard state unnecessarily.
+        if (dozeParameters.get().canControlUnlockedScreenOff()) {
+            // Make sure the status bar is in the correct keyguard state, forcing it if necessary.
+            // This is required if the screen off animation is cancelled, since it might be
+            // incorrectly left in the KEYGUARD or SHADE states depending on when it was cancelled
+            // and whether 'lock instantly' is enabled. We need to force it so that the state is set
+            // even if we're going from SHADE to SHADE or KEYGUARD to KEYGUARD, since we might have
+            // changed parts of the UI (such as showing AOD in the shade) without actually changing
+            // the StatusBarState. This ensures that the UI definitely reflects the desired state.
+            statusBar.updateIsKeyguard(true /* force */)
+        }
     }
 
     override fun onStartedGoingToSleep() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
index a4063bb..418f588 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
@@ -20,11 +20,15 @@
 
 import com.android.keyguard.LockIconViewController;
 import com.android.systemui.biometrics.AuthRippleController;
+import com.android.systemui.statusbar.NotificationShelfController;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController;
 import com.android.systemui.statusbar.phone.SplitShadeHeaderController;
+import com.android.systemui.statusbar.phone.StatusBarCommandQueueCallbacks;
 import com.android.systemui.statusbar.phone.StatusBarDemoMode;
+import com.android.systemui.statusbar.phone.StatusBarHeadsUpChangeListener;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
 
 import java.lang.annotation.Documented;
@@ -32,7 +36,6 @@
 
 import javax.inject.Scope;
 
-import dagger.BindsInstance;
 import dagger.Subcomponent;
 
 /**
@@ -44,11 +47,9 @@
     /**
      * Builder for {@link StatusBarComponent}.
      */
-    @Subcomponent.Builder
-    interface Builder {
-        @BindsInstance Builder statusBarWindowView(
-                NotificationShadeWindowView notificationShadeWindowView);
-        StatusBarComponent build();
+    @Subcomponent.Factory
+    interface Factory {
+        StatusBarComponent create();
     }
 
     /**
@@ -60,6 +61,21 @@
     @interface StatusBarScope {}
 
     /**
+     * Creates a {@link NotificationShadeWindowView}/
+     * @return
+     */
+    @StatusBarScope
+    NotificationShadeWindowView getNotificationShadeWindowView();
+
+    /** */
+    @StatusBarScope
+    NotificationShelfController getNotificationShelfController();
+
+    /** */
+    @StatusBarScope
+    NotificationStackScrollLayoutController getNotificationStackScrollLayoutController();
+
+    /**
      * Creates a NotificationShadeWindowViewController.
      */
     @StatusBarScope
@@ -96,6 +112,18 @@
     StatusBarDemoMode getStatusBarDemoMode();
 
     /**
+     * Creates a StatusBarHeadsUpChangeListener.
+     */
+    @StatusBarScope
+    StatusBarHeadsUpChangeListener getStatusBarHeadsUpChangeListener();
+
+    /**
+     * Creates a StatusBarCommandQueueCallbacks.
+     */
+    @StatusBarScope
+    StatusBarCommandQueueCallbacks getStatusBarCommandQueueCallbacks();
+
+    /**
      * Creates a SplitShadeHeaderController.
      */
     @StatusBarScope
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 5e8eecb..a7b57b9 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
@@ -18,26 +18,25 @@
 
 import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
 
+import android.app.WallpaperManager;
 import android.content.Context;
 import android.os.Handler;
 import android.os.PowerManager;
 import android.util.DisplayMetrics;
 
-import androidx.annotation.Nullable;
-
 import com.android.internal.logging.MetricsLogger;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.InitController;
-import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.demomode.DemoModeController;
-import com.android.systemui.keyguard.DismissCallbackRegistry;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.ScreenLifecycle;
@@ -48,9 +47,7 @@
 import com.android.systemui.recents.ScreenPinningRequest;
 import com.android.systemui.settings.brightness.BrightnessSlider;
 import com.android.systemui.shared.plugins.PluginManager;
-import com.android.unfold.config.UnfoldTransitionConfig;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -59,13 +56,12 @@
 import com.android.systemui.statusbar.NotificationShadeDepthController;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
+import com.android.systemui.statusbar.OperatorNameViewController;
 import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
 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.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.init.NotificationsController;
@@ -81,9 +77,9 @@
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
-import com.android.systemui.statusbar.phone.KeyguardLiftController;
 import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.statusbar.phone.LightsOutNotifController;
+import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
 import com.android.systemui.statusbar.phone.LockscreenWallpaper;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy;
@@ -94,8 +90,8 @@
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
 import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy;
 import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
+import com.android.systemui.statusbar.phone.StatusBarWindowView;
 import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
 import com.android.systemui.statusbar.policy.BatteryController;
@@ -104,10 +100,13 @@
 import com.android.systemui.statusbar.policy.ExtensionController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
 import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.tuner.TunerService;
 import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
+import com.android.systemui.unfold.config.UnfoldTransitionConfig;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.concurrency.MessageRouter;
 import com.android.systemui.volume.VolumeComponent;
 import com.android.systemui.wmshell.BubblesManager;
 import com.android.wm.shell.bubbles.Bubbles;
@@ -118,7 +117,6 @@
 import java.util.concurrent.Executor;
 
 import javax.inject.Named;
-import javax.inject.Provider;
 
 import dagger.Lazy;
 import dagger.Module;
@@ -140,7 +138,6 @@
             LightBarController lightBarController,
             AutoHideController autoHideController,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
-            StatusBarSignalPolicy signalPolicy,
             PulseExpansionHandler pulseExpansionHandler,
             NotificationWakeUpCoordinator notificationWakeUpCoordinator,
             KeyguardBypassController keyguardBypassController,
@@ -151,7 +148,7 @@
             FalsingManager falsingManager,
             FalsingCollector falsingCollector,
             BroadcastDispatcher broadcastDispatcher,
-            RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler,
+            NotificationEntryManager notificationEntryManager,
             NotificationGutsManager notificationGutsManager,
             NotificationLogger notificationLogger,
             NotificationInterruptStateProvider notificationInterruptStateProvider,
@@ -170,20 +167,18 @@
             ScreenLifecycle screenLifecycle,
             WakefulnessLifecycle wakefulnessLifecycle,
             SysuiStatusBarStateController statusBarStateController,
-            VibratorHelper vibratorHelper,
             Optional<BubblesManager> bubblesManagerOptional,
             Optional<Bubbles> bubblesOptional,
             VisualStabilityManager visualStabilityManager,
             DeviceProvisionedController deviceProvisionedController,
             NavigationBarController navigationBarController,
-            AccessibilityFloatingMenuController accessibilityFloatingMenuController,
             Lazy<AssistManager> assistManagerLazy,
             ConfigurationController configurationController,
             NotificationShadeWindowController notificationShadeWindowController,
             DozeParameters dozeParameters,
             ScrimController scrimController,
-            @Nullable KeyguardLiftController keyguardLiftController,
             Lazy<LockscreenWallpaper> lockscreenWallpaperLazy,
+            LockscreenGestureLogger lockscreenGestureLogger,
             Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
             DozeServiceHost dozeServiceHost,
             PowerManager powerManager,
@@ -191,14 +186,14 @@
             DozeScrimController dozeScrimController,
             VolumeComponent volumeComponent,
             CommandQueue commandQueue,
-            Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
+            StatusBarComponent.Factory statusBarComponentFactory,
             PluginManager pluginManager,
             Optional<LegacySplitScreen> splitScreenOptional,
             LightsOutNotifController lightsOutNotifController,
             StatusBarNotificationActivityStarter.Builder
                     statusBarNotificationActivityStarterBuilder,
             ShadeController shadeController,
-            SuperStatusBarViewFactory superStatusBarViewFactory,
+            StatusBarWindowView statusBarWindowView,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             ViewMediatorCallback viewMediatorCallback,
             InitController initController,
@@ -207,15 +202,14 @@
             KeyguardDismissUtil keyguardDismissUtil,
             ExtensionController extensionController,
             UserInfoControllerImpl userInfoControllerImpl,
+            OperatorNameViewController.Factory operatorNameViewControllerFactory,
             PhoneStatusBarPolicy phoneStatusBarPolicy,
             KeyguardIndicationController keyguardIndicationController,
             DemoModeController demoModeController,
             Lazy<NotificationShadeDepthController> notificationShadeDepthController,
-            DismissCallbackRegistry dismissCallbackRegistry,
             StatusBarTouchableRegionManager statusBarTouchableRegionManager,
             NotificationIconAreaController notificationIconAreaController,
             BrightnessSlider.Factory brightnessSliderFactory,
-            WiredChargingRippleController chargingRippleAnimationController,
             UnfoldTransitionConfig unfoldTransitionConfig,
             Lazy<UnfoldLightRevealOverlayAnimation> unfoldLightRevealOverlayAnimation,
             OngoingCallController ongoingCallController,
@@ -225,15 +219,19 @@
             LockscreenShadeTransitionController transitionController,
             FeatureFlags featureFlags,
             KeyguardUnlockAnimationController keyguardUnlockAnimationController,
+            @Main Handler mainHandler,
+            @Main DelayableExecutor delayableExecutor,
+            @Main MessageRouter messageRouter,
+            WallpaperManager wallpaperManager,
             UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
-            Optional<StartingSurface> startingSurfaceOptional) {
+            Optional<StartingSurface> startingSurfaceOptional,
+            TunerService tunerService) {
         return new StatusBar(
                 context,
                 notificationsController,
                 lightBarController,
                 autoHideController,
                 keyguardUpdateMonitor,
-                signalPolicy,
                 pulseExpansionHandler,
                 notificationWakeUpCoordinator,
                 keyguardBypassController,
@@ -244,7 +242,7 @@
                 falsingManager,
                 falsingCollector,
                 broadcastDispatcher,
-                remoteInputQuickSettingsDisabler,
+                notificationEntryManager,
                 notificationGutsManager,
                 notificationLogger,
                 notificationInterruptStateProvider,
@@ -263,20 +261,18 @@
                 screenLifecycle,
                 wakefulnessLifecycle,
                 statusBarStateController,
-                vibratorHelper,
                 bubblesManagerOptional,
                 bubblesOptional,
                 visualStabilityManager,
                 deviceProvisionedController,
                 navigationBarController,
-                accessibilityFloatingMenuController,
                 assistManagerLazy,
                 configurationController,
                 notificationShadeWindowController,
                 dozeParameters,
                 scrimController,
-                keyguardLiftController,
                 lockscreenWallpaperLazy,
+                lockscreenGestureLogger,
                 biometricUnlockControllerLazy,
                 dozeServiceHost,
                 powerManager,
@@ -284,13 +280,13 @@
                 dozeScrimController,
                 volumeComponent,
                 commandQueue,
-                statusBarComponentBuilder,
+                statusBarComponentFactory,
                 pluginManager,
                 splitScreenOptional,
                 lightsOutNotifController,
                 statusBarNotificationActivityStarterBuilder,
                 shadeController,
-                superStatusBarViewFactory,
+                statusBarWindowView,
                 statusBarKeyguardViewManager,
                 viewMediatorCallback,
                 initController,
@@ -299,15 +295,14 @@
                 keyguardDismissUtil,
                 extensionController,
                 userInfoControllerImpl,
+                operatorNameViewControllerFactory,
                 phoneStatusBarPolicy,
                 keyguardIndicationController,
-                dismissCallbackRegistry,
                 demoModeController,
                 notificationShadeDepthController,
                 statusBarTouchableRegionManager,
                 notificationIconAreaController,
                 brightnessSliderFactory,
-                chargingRippleAnimationController,
                 unfoldTransitionConfig,
                 unfoldLightRevealOverlayAnimation,
                 ongoingCallController,
@@ -317,7 +312,12 @@
                 transitionController,
                 featureFlags,
                 keyguardUnlockAnimationController,
+                mainHandler,
+                delayableExecutor,
+                messageRouter,
+                wallpaperManager,
                 unlockedScreenOffAnimationController,
-                startingSurfaceOptional);
+                startingSurfaceOptional,
+                tunerService);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index d691dca..ecf3b86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -17,14 +17,23 @@
 package com.android.systemui.statusbar.phone.dagger;
 
 import android.annotation.Nullable;
+import android.content.Context;
+import android.view.LayoutInflater;
 import android.view.View;
 
 import com.android.keyguard.LockIconView;
 import com.android.systemui.R;
+import com.android.systemui.battery.BatteryMeterView;
 import com.android.systemui.biometrics.AuthRippleView;
+import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.NotificationShelfController;
+import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.phone.NotificationPanelView;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
 import com.android.systemui.statusbar.phone.TapAgainView;
+import com.android.systemui.util.InjectionInflationController;
 
 import javax.inject.Named;
 
@@ -39,6 +48,63 @@
     /** */
     @Provides
     @StatusBarComponent.StatusBarScope
+    public static NotificationShadeWindowView providesNotificationShadeWindowView(
+            InjectionInflationController injectionInflationController,
+            Context context) {
+        NotificationShadeWindowView notificationShadeWindowView = (NotificationShadeWindowView)
+                injectionInflationController.injectable(
+                        LayoutInflater.from(context)).inflate(R.layout.super_notification_shade,
+                        /* root= */ null);
+        if (notificationShadeWindowView == null) {
+            throw new IllegalStateException(
+                    "R.layout.super_notification_shade could not be properly inflated");
+        }
+
+        return notificationShadeWindowView;
+    }
+
+    /** */
+    @Provides
+    @StatusBarComponent.StatusBarScope
+    public static NotificationStackScrollLayout providesNotificationStackScrollLayout(
+            NotificationStackScrollLayoutController notificationStackScrollLayoutController) {
+        return notificationStackScrollLayoutController.getView();
+    }
+
+    /** */
+    @Provides
+    @StatusBarComponent.StatusBarScope
+    public static NotificationShelf providesNotificationShelf(LayoutInflater layoutInflater,
+            NotificationStackScrollLayout notificationStackScrollLayout) {
+        NotificationShelf view = (NotificationShelf) layoutInflater.inflate(
+                R.layout.status_bar_notification_shelf, notificationStackScrollLayout, false);
+
+        if (view == null) {
+            throw new IllegalStateException(
+                    "R.layout.status_bar_notification_shelf could not be properly inflated");
+        }
+        return view;
+    }
+
+    /** */
+    @Provides
+    @StatusBarComponent.StatusBarScope
+    public static NotificationShelfController providesStatusBarWindowView(
+            NotificationShelfComponent.Builder notificationShelfComponentBuilder,
+            NotificationShelf notificationShelf) {
+        NotificationShelfComponent component = notificationShelfComponentBuilder
+                .notificationShelf(notificationShelf)
+                .build();
+        NotificationShelfController notificationShelfController =
+                component.getNotificationShelfController();
+        notificationShelfController.init();
+
+        return notificationShelfController;
+    }
+
+    /** */
+    @Provides
+    @StatusBarComponent.StatusBarScope
     public static NotificationPanelView getNotificationPanelView(
             NotificationShadeWindowView notificationShadeWindowView) {
         return notificationShadeWindowView.getNotificationPanelView();
@@ -73,6 +139,13 @@
     /** */
     @Provides
     @StatusBarComponent.StatusBarScope
+    static BatteryMeterView getBatteryMeterView(@Named(SPLIT_SHADE_HEADER) View view) {
+        return view.findViewById(R.id.batteryRemainingIcon);
+    }
+
+    /** */
+    @Provides
+    @StatusBarComponent.StatusBarScope
     public static TapAgainView getTapAgainView(NotificationPanelView npv) {
         return npv.getTapAgainView();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 16fa5da..62ba56a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -24,7 +24,6 @@
 import android.content.Intent
 import android.util.Log
 import android.view.View
-import android.widget.Chronometer
 import androidx.annotation.VisibleForTesting
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.systemui.R
@@ -32,7 +31,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
@@ -130,7 +129,6 @@
         }
     }
 
-
     /**
      * Called when the chip's visibility may have changed.
      *
@@ -218,9 +216,17 @@
         isCallAppVisible = isProcessVisibleToUser(
                 iActivityManager.getUidProcessState(currentCallNotificationInfo.uid, null))
 
+        if (uidObserver != null) {
+            iActivityManager.unregisterUidObserver(uidObserver)
+        }
+
         uidObserver = object : IUidObserver.Stub() {
             override fun onUidStateChanged(
-                    uid: Int, procState: Int, procStateSeq: Long, capability: Int) {
+                uid: Int,
+                procState: Int,
+                procStateSeq: Long,
+                capability: Int
+            ) {
                 if (uid == currentCallNotificationInfo.uid) {
                     val oldIsCallAppVisible = isCallAppVisible
                     isCallAppVisible = isProcessVisibleToUser(procState)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
index ab58286..6d6320e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
@@ -40,6 +40,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.settings.UserTracker;
+import com.android.wifitrackerlib.MergedCarrierEntry;
 import com.android.wifitrackerlib.WifiEntry;
 import com.android.wifitrackerlib.WifiPickerTracker;
 
@@ -68,6 +69,7 @@
 
     private final ArrayList<AccessPointCallback> mCallbacks = new ArrayList<AccessPointCallback>();
     private final UserManager mUserManager;
+    private final UserTracker mUserTracker;
     private final Executor mMainExecutor;
 
     private @Nullable WifiPickerTracker mWifiPickerTracker;
@@ -84,6 +86,7 @@
             WifiPickerTrackerFactory wifiPickerTrackerFactory
     ) {
         mUserManager = userManager;
+        mUserTracker = userTracker;
         mCurrentUser = userTracker.getUserId();
         mMainExecutor = mainExecutor;
         mWifiPickerTrackerFactory = wifiPickerTrackerFactory;
@@ -118,6 +121,11 @@
                 new UserHandle(mCurrentUser));
     }
 
+    public boolean canConfigMobileData() {
+        return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
+                UserHandle.of(mCurrentUser)) && mUserTracker.getUserInfo().isAdmin();
+    }
+
     public void onUserSwitched(int newUserId) {
         mCurrentUser = newUserId;
     }
@@ -157,6 +165,15 @@
     }
 
     @Override
+    public MergedCarrierEntry getMergedCarrierEntry() {
+        if (mWifiPickerTracker == null) {
+            fireAcccessPointsCallback(Collections.emptyList());
+            return null;
+        }
+        return mWifiPickerTracker.getMergedCarrierEntry();
+    }
+
+    @Override
     public int getIcon(WifiEntry ap) {
         int level = ap.getLevel();
         return ICONS[Math.max(0, level)];
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
index 3a05ec7..e679c4c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
@@ -37,6 +37,7 @@
         default void onConfigChanged(Configuration newConfig) {}
         default void onDensityOrFontScaleChanged() {}
         default void onSmallestScreenWidthChanged() {}
+        default void onMaxBoundsChanged() {}
         default void onOverlayChanged() {}
         default void onUiModeChanged() {}
         default void onThemeChanged() {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
new file mode 100644
index 0000000..41cacf5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
@@ -0,0 +1,239 @@
+/*
+ * 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.policy;
+
+
+import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED;
+import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;
+import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED;
+
+import static com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule.DEVICE_STATE_ROTATION_LOCK_DEFAULTS;
+
+import android.annotation.Nullable;
+import android.hardware.devicestate.DeviceStateManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseIntArray;
+
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.util.settings.SecureSettings;
+import com.android.systemui.util.wrapper.RotationPolicyWrapper;
+
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * Handles reading and writing of rotation lock settings per device state, as well as setting
+ * the rotation lock when device state changes.
+ **/
+@SysUISingleton
+public final class DeviceStateRotationLockSettingController implements Listenable,
+        RotationLockController.RotationLockControllerCallback {
+
+    private static final String TAG = "DSRotateLockSettingCon";
+
+    private static final String SEPARATOR_REGEX = ":";
+
+    private final SecureSettings mSecureSettings;
+    private final RotationPolicyWrapper mRotationPolicyWrapper;
+    private final DeviceStateManager mDeviceStateManager;
+    private final Executor mMainExecutor;
+    private final String[] mDeviceStateRotationLockDefaults;
+
+    private SparseIntArray mDeviceStateRotationLockSettings;
+    // TODO(b/183001527): Add API to query current device state and initialize this.
+    private int mDeviceState = -1;
+    @Nullable
+    private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
+
+
+    @Inject
+    public DeviceStateRotationLockSettingController(
+            SecureSettings secureSettings,
+            RotationPolicyWrapper rotationPolicyWrapper,
+            DeviceStateManager deviceStateManager,
+            @Main Executor executor,
+            @Named(DEVICE_STATE_ROTATION_LOCK_DEFAULTS) String[] deviceStateRotationLockDefaults
+    ) {
+        mSecureSettings = secureSettings;
+        mRotationPolicyWrapper = rotationPolicyWrapper;
+        mDeviceStateManager = deviceStateManager;
+        mMainExecutor = executor;
+        mDeviceStateRotationLockDefaults = deviceStateRotationLockDefaults;
+    }
+
+    /**
+     * Loads the settings from storage.
+     */
+    public void initialize() {
+        String serializedSetting =
+                mSecureSettings.getStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                        UserHandle.USER_CURRENT);
+        if (TextUtils.isEmpty(serializedSetting)) {
+            // No settings saved, we should load the defaults and persist them.
+            fallbackOnDefaults();
+            return;
+        }
+        String[] values = serializedSetting.split(SEPARATOR_REGEX);
+        if (values.length % 2 != 0) {
+            // Each entry should be a key/value pair, so this is corrupt.
+            Log.wtf(TAG, "Can't deserialize saved settings, falling back on defaults");
+            fallbackOnDefaults();
+            return;
+        }
+        mDeviceStateRotationLockSettings = new SparseIntArray(values.length / 2);
+        int key;
+        int value;
+
+        for (int i = 0; i < values.length - 1; ) {
+            try {
+                key = Integer.parseInt(values[i++]);
+                value = Integer.parseInt(values[i++]);
+                mDeviceStateRotationLockSettings.put(key, value);
+            } catch (NumberFormatException e) {
+                Log.wtf(TAG, "Error deserializing one of the saved settings", e);
+                fallbackOnDefaults();
+                return;
+            }
+        }
+    }
+
+    private void fallbackOnDefaults() {
+        loadDefaults();
+        persistSettings();
+    }
+
+    @Override
+    public void setListening(boolean listening) {
+        if (listening) {
+            // Note that this is called once with the initial state of the device, even if there
+            // is no user action.
+            mDeviceStateCallback = this::updateDeviceState;
+            mDeviceStateManager.registerCallback(mMainExecutor, mDeviceStateCallback);
+        } else {
+            if (mDeviceStateCallback != null) {
+                mDeviceStateManager.unregisterCallback(mDeviceStateCallback);
+            }
+        }
+    }
+
+    @Override
+    public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible) {
+        if (mDeviceState == -1) {
+            Log.wtf(TAG, "Device state was not initialized.");
+            return;
+        }
+
+        if (rotationLocked == isRotationLockedForCurrentState()) {
+            Log.v(TAG, "Rotation lock same as the current setting, no need to update.");
+            return;
+        }
+
+        saveNewRotationLockSetting(rotationLocked);
+    }
+
+    private void saveNewRotationLockSetting(boolean isRotationLocked) {
+        Log.v(TAG, "saveNewRotationLockSetting [state=" + mDeviceState + "] [isRotationLocked="
+                + isRotationLocked + "]");
+
+        mDeviceStateRotationLockSettings.put(mDeviceState,
+                isRotationLocked
+                        ? DEVICE_STATE_ROTATION_LOCK_LOCKED
+                        : DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
+        persistSettings();
+    }
+
+    private boolean isRotationLockedForCurrentState() {
+        return mDeviceStateRotationLockSettings.get(mDeviceState,
+                DEVICE_STATE_ROTATION_LOCK_IGNORED) == DEVICE_STATE_ROTATION_LOCK_LOCKED;
+    }
+
+    private void updateDeviceState(int state) {
+        Log.v(TAG, "updateDeviceState [state=" + state + "]");
+        if (mDeviceState == state) {
+            return;
+        }
+
+        int rotationLockSetting =
+                mDeviceStateRotationLockSettings.get(state, DEVICE_STATE_ROTATION_LOCK_IGNORED);
+        if (rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
+            // We won't handle this device state. The same rotation lock setting as before should
+            // apply and any changes to the rotation lock setting will be written for the previous
+            // valid device state.
+            Log.v(TAG, "Ignoring new device state: " + state);
+            return;
+        }
+
+        // Accept the new state
+        mDeviceState = state;
+
+        // Update the rotation lock setting if needed for this new device state
+        boolean newRotationLockSetting = rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_LOCKED;
+        if (newRotationLockSetting != mRotationPolicyWrapper.isRotationLocked()) {
+            mRotationPolicyWrapper.setRotationLock(newRotationLockSetting);
+        }
+    }
+
+    private void persistSettings() {
+        if (mDeviceStateRotationLockSettings.size() == 0) {
+            mSecureSettings.putStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                    /* value= */"", UserHandle.USER_CURRENT);
+            return;
+        }
+
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append(mDeviceStateRotationLockSettings.keyAt(0))
+                .append(SEPARATOR_REGEX)
+                .append(mDeviceStateRotationLockSettings.valueAt(0));
+
+        for (int i = 1; i < mDeviceStateRotationLockSettings.size(); i++) {
+            stringBuilder
+                    .append(SEPARATOR_REGEX)
+                    .append(mDeviceStateRotationLockSettings.keyAt(i))
+                    .append(SEPARATOR_REGEX)
+                    .append(mDeviceStateRotationLockSettings.valueAt(i));
+        }
+        mSecureSettings.putStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                stringBuilder.toString(), UserHandle.USER_CURRENT);
+    }
+
+    private void loadDefaults() {
+        if (mDeviceStateRotationLockDefaults.length == 0) {
+            Log.w(TAG, "Empty default settings");
+            mDeviceStateRotationLockSettings = new SparseIntArray(/* initialCapacity= */0);
+            return;
+        }
+        mDeviceStateRotationLockSettings =
+                new SparseIntArray(mDeviceStateRotationLockDefaults.length);
+        for (String serializedDefault : mDeviceStateRotationLockDefaults) {
+            String[] entry = serializedDefault.split(SEPARATOR_REGEX);
+            try {
+                int key = Integer.parseInt(entry[0]);
+                int value = Integer.parseInt(entry[1]);
+                mDeviceStateRotationLockSettings.put(key, value);
+            } catch (NumberFormatException e) {
+                Log.wtf(TAG, "Error deserializing default settings", e);
+            }
+        }
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
index 742b1ab..7bf1601 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
@@ -251,5 +251,11 @@
          * {@link KeyguardStateController#isFaceAuthEnabled()}.
          */
         default void onFaceAuthEnabledChanged() {}
+
+        /**
+         * Triggered when the notification panel is starting or has finished
+         * fading away on transition to an app.
+         */
+        default void onLaunchTransitionFadingAwayChanged() {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 64750bd..f787ecf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -343,6 +343,7 @@
     @Override
     public void setLaunchTransitionFadingAway(boolean fadingAway) {
         mLaunchTransitionFadingAway = fadingAway;
+        new ArrayList<>(mCallbacks).forEach(Callback::onLaunchTransitionFadingAwayChanged);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 43781f3..3490e15 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -57,7 +57,7 @@
 import com.android.settingslib.mobile.TelephonyIcons;
 import com.android.settingslib.net.SignalStrengthUtil;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
 import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index ef2ca98..eeea699 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -23,6 +23,7 @@
 import com.android.settingslib.net.DataUsageController;
 import com.android.systemui.demomode.DemoMode;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import com.android.wifitrackerlib.MergedCarrierEntry;
 import com.android.wifitrackerlib.WifiEntry;
 
 import java.util.List;
@@ -223,9 +224,11 @@
         void addAccessPointCallback(AccessPointCallback callback);
         void removeAccessPointCallback(AccessPointCallback callback);
         void scanForAccessPoints();
+        MergedCarrierEntry getMergedCarrierEntry();
         int getIcon(WifiEntry ap);
         boolean connect(WifiEntry ap);
         boolean canConfigWifi();
+        boolean canConfigMobileData();
 
         public interface AccessPointCallback {
             void onAccessPointsChanged(List<WifiEntry> accessPoints);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index fa61115..156def5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -68,10 +68,14 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.demomode.DemoMode;
 import com.android.systemui.demomode.DemoModeController;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
+import com.android.systemui.qs.tiles.dialog.InternetDialogUtil;
 import com.android.systemui.settings.CurrentUserTracker;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
 import com.android.systemui.telephony.TelephonyListenerManager;
 import com.android.systemui.util.CarrierConfigTracker;
@@ -126,6 +130,7 @@
     private Config mConfig;
     private final CarrierConfigTracker mCarrierConfigTracker;
     private final FeatureFlags mFeatureFlags;
+    private final DumpManager mDumpManager;
 
     private TelephonyCallback.ActiveDataSubscriptionIdListener mPhoneStateListener;
     private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -189,6 +194,8 @@
     private boolean mUserSetup;
     private boolean mSimDetected;
     private boolean mForceCellularValidated;
+    private InternetDialogFactory mInternetDialogFactory;
+    private Handler mMainHandler;
 
     private ConfigurationController.ConfigurationListener mConfigurationListener =
             new ConfigurationController.ConfigurationListener() {
@@ -218,7 +225,10 @@
             AccessPointControllerImpl accessPointController,
             DemoModeController demoModeController,
             CarrierConfigTracker carrierConfigTracker,
-            FeatureFlags featureFlags) {
+            FeatureFlags featureFlags,
+            @Main Handler handler,
+            InternetDialogFactory internetDialogFactory,
+            DumpManager dumpManager) {
         this(context, connectivityManager,
                 telephonyManager,
                 telephonyListenerManager,
@@ -236,8 +246,11 @@
                 broadcastDispatcher,
                 demoModeController,
                 carrierConfigTracker,
-                featureFlags);
+                featureFlags,
+                dumpManager);
         mReceiverHandler.post(mRegisterListeners);
+        mMainHandler = handler;
+        mInternetDialogFactory = internetDialogFactory;
     }
 
     @VisibleForTesting
@@ -256,7 +269,8 @@
             BroadcastDispatcher broadcastDispatcher,
             DemoModeController demoModeController,
             CarrierConfigTracker carrierConfigTracker,
-            FeatureFlags featureFlags
+            FeatureFlags featureFlags,
+            DumpManager dumpManager
     ) {
         mContext = context;
         mTelephonyListenerManager = telephonyListenerManager;
@@ -275,6 +289,7 @@
         mDemoModeController = demoModeController;
         mCarrierConfigTracker = carrierConfigTracker;
         mFeatureFlags = featureFlags;
+        mDumpManager = dumpManager;
 
         // telephony
         mPhone = telephonyManager;
@@ -425,6 +440,8 @@
         mDemoModeController.addCallback(this);
         mProviderModelBehavior = mFeatureFlags.isCombinedStatusBarSignalIconsEnabled();
         mProviderModelSetting = mFeatureFlags.isProviderModelSettingEnabled();
+
+        mDumpManager.registerDumpable(TAG, this);
     }
 
     private final Runnable mClearForceValidated = () -> {
@@ -472,6 +489,9 @@
         filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
         filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
         filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+        if (InternetDialogUtil.isProviderModelEnabled(mContext)) {
+            filter.addAction(Settings.Panel.ACTION_INTERNET_CONNECTIVITY);
+        }
         mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mReceiverHandler);
         mListening = true;
 
@@ -780,6 +800,10 @@
                 mConfig = Config.readConfig(mContext);
                 mReceiverHandler.post(this::handleConfigurationChanged);
                 break;
+            case Settings.Panel.ACTION_INTERNET_CONNECTIVITY:
+                mMainHandler.post(() -> mInternetDialogFactory.create(true,
+                        mAccessPoints.canConfigMobileData(), mAccessPoints.canConfigWifi()));
+                break;
             default:
                 int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 84d7c05..5d7d480 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -77,7 +77,6 @@
 import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.nano.MetricsProto;
-import com.android.internal.util.ContrastColorUtil;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -204,7 +203,7 @@
         final int stroke = colorized ? mContext.getResources().getDimensionPixelSize(
                 R.dimen.remote_input_view_text_stroke) : 0;
         if (colorized) {
-            final boolean dark = !ContrastColorUtil.isColorLight(backgroundColor);
+            final boolean dark = Notification.Builder.isColorDark(backgroundColor);
             final int foregroundColor = dark ? Color.WHITE : Color.BLACK;
             final int inverseColor = dark ? Color.BLACK : Color.WHITE;
             editBgColor = backgroundColor;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
index 53d68d0..67f5364 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
@@ -16,36 +16,54 @@
 
 package com.android.systemui.statusbar.policy;
 
-import android.content.Context;
+import static com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule.DEVICE_STATE_ROTATION_LOCK_DEFAULTS;
+
 import android.os.UserHandle;
 
 import androidx.annotation.NonNull;
 
-import com.android.internal.view.RotationPolicy;
+import com.android.internal.view.RotationPolicy.RotationPolicyListener;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.util.wrapper.RotationPolicyWrapper;
 
 import java.util.concurrent.CopyOnWriteArrayList;
 
 import javax.inject.Inject;
+import javax.inject.Named;
 
 /** Platform implementation of the rotation lock controller. **/
 @SysUISingleton
 public final class RotationLockControllerImpl implements RotationLockController {
-    private final Context mContext;
     private final CopyOnWriteArrayList<RotationLockControllerCallback> mCallbacks =
-            new CopyOnWriteArrayList<RotationLockControllerCallback>();
+            new CopyOnWriteArrayList<>();
 
-    private final RotationPolicy.RotationPolicyListener mRotationPolicyListener =
-            new RotationPolicy.RotationPolicyListener() {
+    private final RotationPolicyListener mRotationPolicyListener =
+            new RotationPolicyListener() {
         @Override
         public void onChange() {
             notifyChanged();
         }
     };
 
+    private final RotationPolicyWrapper mRotationPolicy;
+    private final DeviceStateRotationLockSettingController
+            mDeviceStateRotationLockSettingController;
+    private final boolean mIsPerDeviceStateRotationLockEnabled;
+
     @Inject
-    public RotationLockControllerImpl(Context context) {
-        mContext = context;
+    public RotationLockControllerImpl(
+            RotationPolicyWrapper rotationPolicyWrapper,
+            DeviceStateRotationLockSettingController deviceStateRotationLockSettingController,
+            @Named(DEVICE_STATE_ROTATION_LOCK_DEFAULTS) String[] deviceStateRotationLockDefaults
+    ) {
+        mRotationPolicy = rotationPolicyWrapper;
+        mDeviceStateRotationLockSettingController = deviceStateRotationLockSettingController;
+        mIsPerDeviceStateRotationLockEnabled = deviceStateRotationLockDefaults.length > 0;
+        if (mIsPerDeviceStateRotationLockEnabled) {
+            deviceStateRotationLockSettingController.initialize();
+            mCallbacks.add(mDeviceStateRotationLockSettingController);
+        }
+
         setListening(true);
     }
 
@@ -61,32 +79,35 @@
     }
 
     public int getRotationLockOrientation() {
-        return RotationPolicy.getRotationLockOrientation(mContext);
+        return mRotationPolicy.getRotationLockOrientation();
     }
 
     public boolean isRotationLocked() {
-        return RotationPolicy.isRotationLocked(mContext);
+        return mRotationPolicy.isRotationLocked();
     }
 
     public void setRotationLocked(boolean locked) {
-        RotationPolicy.setRotationLock(mContext, locked);
+        mRotationPolicy.setRotationLock(locked);
     }
 
     public void setRotationLockedAtAngle(boolean locked, int rotation){
-        RotationPolicy.setRotationLockAtAngle(mContext, locked, rotation);
+        mRotationPolicy.setRotationLockAtAngle(locked, rotation);
     }
 
     public boolean isRotationLockAffordanceVisible() {
-        return RotationPolicy.isRotationLockToggleVisible(mContext);
+        return mRotationPolicy.isRotationLockToggleVisible();
     }
 
     @Override
     public void setListening(boolean listening) {
         if (listening) {
-            RotationPolicy.registerRotationPolicyListener(mContext, mRotationPolicyListener,
+            mRotationPolicy.registerRotationPolicyListener(mRotationPolicyListener,
                     UserHandle.USER_ALL);
         } else {
-            RotationPolicy.unregisterRotationPolicyListener(mContext, mRotationPolicyListener);
+            mRotationPolicy.unregisterRotationPolicyListener(mRotationPolicyListener);
+        }
+        if (mIsPerDeviceStateRotationLockEnabled) {
+            mDeviceStateRotationLockSettingController.setListening(listening);
         }
     }
 
@@ -97,7 +118,7 @@
     }
 
     private void notifyChanged(RotationLockControllerCallback callback) {
-        callback.onRotationLockStateChanged(RotationPolicy.isRotationLocked(mContext),
-                RotationPolicy.isRotationLockToggleVisible(mContext));
+        callback.onRotationLockStateChanged(mRotationPolicy.isRotationLocked(),
+                mRotationPolicy.isRotationLockToggleVisible());
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index 41b1dd1..4e33529 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -628,7 +628,7 @@
         mCurrentBackgroundColor = backgroundColor;
         mCurrentColorized = colorized;
 
-        final boolean dark = !ContrastColorUtil.isColorLight(backgroundColor);
+        final boolean dark = Notification.Builder.isColorDark(backgroundColor);
 
         mCurrentTextColor = ContrastColorUtil.ensureTextContrast(
                 dark ? mDefaultTextColorDarkBg : mDefaultTextColor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index ae73f73..251ecc6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -26,6 +26,7 @@
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.IActivityTaskManager;
+import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -65,8 +66,8 @@
 import com.android.systemui.SystemUISecondaryUserService;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.DetailAdapter;
@@ -114,6 +115,8 @@
     @VisibleForTesting
     final GuestResumeSessionReceiver mGuestResumeSessionReceiver;
     private final KeyguardStateController mKeyguardStateController;
+    private final DeviceProvisionedController mDeviceProvisionedController;
+    private final DevicePolicyManager mDevicePolicyManager;
     protected final Handler mHandler;
     private final ActivityStarter mActivityStarter;
     private final BroadcastDispatcher mBroadcastDispatcher;
@@ -138,7 +141,7 @@
     private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2);
     private final UiEventLogger mUiEventLogger;
     public final DetailAdapter mUserDetailAdapter;
-    private final Executor mUiBgExecutor;
+    private final Executor mBgExecutor;
     private final boolean mGuestUserAutoCreated;
     private final AtomicBoolean mGuestIsResetting;
     private final AtomicBoolean mGuestCreationScheduled;
@@ -149,6 +152,8 @@
             UserManager userManager,
             UserTracker userTracker,
             KeyguardStateController keyguardStateController,
+            DeviceProvisionedController deviceProvisionedController,
+            DevicePolicyManager devicePolicyManager,
             @Main Handler handler,
             ActivityStarter activityStarter,
             BroadcastDispatcher broadcastDispatcher,
@@ -158,7 +163,7 @@
             IActivityTaskManager activityTaskManager,
             UserDetailAdapter userDetailAdapter,
             SecureSettings secureSettings,
-            @UiBackground Executor uiBgExecutor) {
+            @Background Executor bgExecutor) {
         mContext = context;
         mUserTracker = userTracker;
         mBroadcastDispatcher = broadcastDispatcher;
@@ -169,7 +174,7 @@
         mGuestResumeSessionReceiver = new GuestResumeSessionReceiver(
                 this, mUserTracker, mUiEventLogger, secureSettings);
         mUserDetailAdapter = userDetailAdapter;
-        mUiBgExecutor = uiBgExecutor;
+        mBgExecutor = bgExecutor;
         if (!UserManager.isGuestUserEphemeral()) {
             mGuestResumeSessionReceiver.register(mBroadcastDispatcher);
         }
@@ -178,6 +183,8 @@
         mGuestIsResetting = new AtomicBoolean();
         mGuestCreationScheduled = new AtomicBoolean();
         mKeyguardStateController = keyguardStateController;
+        mDeviceProvisionedController = deviceProvisionedController;
+        mDevicePolicyManager = devicePolicyManager;
         mHandler = handler;
         mActivityStarter = activityStarter;
         mUserManager = userManager;
@@ -336,8 +343,7 @@
                                 true /* isGuest */, false /* isCurrent */,
                                 false /* isAddUser */, false /* isRestricted */,
                                 isSwitchToGuestEnabled);
-                        // Don't call checkIfAddUserDisallowedByAdminOnly if
-                        // config_guestUserAutoCreated=true.
+                        checkIfAddUserDisallowedByAdminOnly(guestRecord);
                         records.add(guestRecord);
                     } else if (canCreateGuest) {
                         guestRecord = new UserRecord(null /* info */, null /* picture */,
@@ -718,7 +724,7 @@
             return;
         }
 
-        mUiBgExecutor.execute(() -> {
+        mBgExecutor.execute(() -> {
             int newGuestId = createGuest();
             mGuestCreationScheduled.set(false);
             mGuestIsResetting.set(false);
@@ -733,10 +739,27 @@
     }
 
     /**
+     * Guarantee guest is present only if the device is provisioned. Otherwise, create a content
+     * observer to wait until the device is provisioned, then schedule the guest creation.
+     */
+    public void schedulePostBootGuestCreation() {
+        if (isDeviceAllowedToAddGuest()) {
+            guaranteeGuestPresent();
+        } else {
+            mDeviceProvisionedController.addCallback(mGuaranteeGuestPresentAfterProvisioned);
+        }
+    }
+
+    private boolean isDeviceAllowedToAddGuest() {
+        return mDeviceProvisionedController.isDeviceProvisioned()
+                && !mDevicePolicyManager.isDeviceManaged();
+    }
+
+    /**
      * If there is no guest on the device, schedule creation of a new guest user in the background.
      */
-    public void guaranteeGuestPresent() {
-        if (mUserManager.findCurrentGuestUser() == null) {
+    private void guaranteeGuestPresent() {
+        if (isDeviceAllowedToAddGuest() && mUserManager.findCurrentGuestUser() == null) {
             scheduleGuestCreation();
         }
     }
@@ -1056,6 +1079,21 @@
                 }
             };
 
+    private final DeviceProvisionedController.DeviceProvisionedListener
+            mGuaranteeGuestPresentAfterProvisioned =
+            new DeviceProvisionedController.DeviceProvisionedListener() {
+                @Override
+                public void onDeviceProvisionedChanged() {
+                    if (isDeviceAllowedToAddGuest()) {
+                        mBgExecutor.execute(
+                                () -> mDeviceProvisionedController.removeCallback(
+                                        mGuaranteeGuestPresentAfterProvisioned));
+                        guaranteeGuestPresent();
+                    }
+                }
+            };
+
+
     private final class ExitGuestDialog extends SystemUIDialog implements
             DialogInterface.OnClickListener {
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateView.kt
new file mode 100644
index 0000000..ae9d9ee
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateView.kt
@@ -0,0 +1,79 @@
+/*
+ * 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.policy
+
+import android.content.Context
+import android.text.StaticLayout
+import android.util.AttributeSet
+import android.widget.TextView
+import com.android.systemui.R
+
+/**
+ * View for showing a date that can toggle between two different formats depending on size.
+ *
+ * If no pattern can fit, it will display empty.
+ *
+ * @see R.styleable.VariableDateView_longDatePattern
+ * @see R.styleable.VariableDateView_shortDatePattern
+ */
+class VariableDateView(context: Context, attrs: AttributeSet) : TextView(context, attrs) {
+
+    val longerPattern: String
+    val shorterPattern: String
+
+    init {
+        val a = context.theme.obtainStyledAttributes(
+                attrs,
+                R.styleable.VariableDateView,
+                0, 0)
+        longerPattern = a.getString(R.styleable.VariableDateView_longDatePattern)
+                ?: context.getString(R.string.system_ui_date_pattern)
+        shorterPattern = a.getString(R.styleable.VariableDateView_shortDatePattern)
+                ?: context.getString(R.string.abbrev_month_day_no_year)
+
+        a.recycle()
+    }
+
+    /**
+     * Freeze the pattern switching
+     *
+     * Use during animations if the container will change its size but this view should not change
+     */
+    var freezeSwitching = false
+
+    private var onMeasureListener: OnMeasureListener? = null
+
+    fun onAttach(listener: OnMeasureListener?) {
+        onMeasureListener = listener
+    }
+
+    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+        val availableWidth = MeasureSpec.getSize(widthMeasureSpec) - paddingStart - paddingEnd
+        if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED && !freezeSwitching) {
+            onMeasureListener?.onMeasureAction(availableWidth)
+        }
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+    }
+
+    fun getDesiredWidthForText(text: CharSequence): Float {
+        return StaticLayout.getDesiredWidth(text, paint)
+    }
+
+    interface OnMeasureListener {
+        fun onMeasureAction(availableWidth: Int)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt
new file mode 100644
index 0000000..99d84c4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt
@@ -0,0 +1,221 @@
+/*
+ * 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.policy
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.icu.text.DateFormat
+import android.icu.text.DisplayContext
+import android.icu.util.Calendar
+import android.os.Handler
+import android.os.HandlerExecutor
+import android.os.UserHandle
+import android.text.TextUtils
+import android.util.Log
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.Dependency
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.util.ViewController
+import com.android.systemui.util.time.SystemClock
+import java.text.FieldPosition
+import java.text.ParsePosition
+import java.util.Date
+import java.util.Locale
+import javax.inject.Inject
+import javax.inject.Named
+
+@VisibleForTesting
+internal fun getTextForFormat(date: Date?, format: DateFormat): String {
+    return if (format === EMPTY_FORMAT) { // Check if same object
+        ""
+    } else format.format(date)
+}
+
+@VisibleForTesting
+internal fun getFormatFromPattern(pattern: String?): DateFormat {
+    if (TextUtils.equals(pattern, "")) {
+        return EMPTY_FORMAT
+    }
+    val l = Locale.getDefault()
+    val format = DateFormat.getInstanceForSkeleton(pattern, l)
+    format.setContext(DisplayContext.CAPITALIZATION_FOR_STANDALONE)
+    return format
+}
+
+private val EMPTY_FORMAT: DateFormat = object : DateFormat() {
+    override fun format(
+        cal: Calendar,
+        toAppendTo: StringBuffer,
+        fieldPosition: FieldPosition
+    ): StringBuffer? {
+        return null
+    }
+
+    override fun parse(text: String, cal: Calendar, pos: ParsePosition) {}
+}
+
+private const val DEBUG = false
+private const val TAG = "VariableDateViewController"
+
+class VariableDateViewController(
+    private val systemClock: SystemClock,
+    private val broadcastDispatcher: BroadcastDispatcher,
+    private val timeTickHandler: Handler,
+    view: VariableDateView
+) : ViewController<VariableDateView>(view) {
+
+    private var dateFormat: DateFormat? = null
+    private var datePattern = view.longerPattern
+        set(value) {
+            if (field == value) return
+            field = value
+            dateFormat = null
+            if (isAttachedToWindow) {
+                post(::updateClock)
+            }
+        }
+    private var lastWidth = Integer.MAX_VALUE
+    private var lastText = ""
+    private var currentTime = Date()
+
+    // View class easy accessors
+    private val longerPattern: String
+        get() = mView.longerPattern
+    private val shorterPattern: String
+        get() = mView.shorterPattern
+    private fun post(block: () -> Unit) = mView.handler?.post(block)
+
+    private val intentReceiver: BroadcastReceiver = object : BroadcastReceiver() {
+        override fun onReceive(context: Context, intent: Intent) {
+            // If the handler is null, it means we received a broadcast while the view has not
+            // finished being attached or in the process of being detached.
+            // In that case, do not post anything.
+            val handler = mView.handler ?: return
+            val action = intent.action
+            if (
+                    Intent.ACTION_TIME_TICK == action ||
+                    Intent.ACTION_TIME_CHANGED == action ||
+                    Intent.ACTION_TIMEZONE_CHANGED == action ||
+                    Intent.ACTION_LOCALE_CHANGED == action
+            ) {
+                if (
+                        Intent.ACTION_LOCALE_CHANGED == action ||
+                        Intent.ACTION_TIMEZONE_CHANGED == action
+                ) {
+                    // need to get a fresh date format
+                    handler.post { dateFormat = null }
+                }
+                handler.post(::updateClock)
+            }
+        }
+    }
+
+    private val onMeasureListener = object : VariableDateView.OnMeasureListener {
+        override fun onMeasureAction(availableWidth: Int) {
+            if (availableWidth != lastWidth) {
+                // maybeChangeFormat will post if the pattern needs to change.
+                maybeChangeFormat(availableWidth)
+                lastWidth = availableWidth
+            }
+        }
+    }
+
+    override fun onViewAttached() {
+        val filter = IntentFilter().apply {
+            addAction(Intent.ACTION_TIME_TICK)
+            addAction(Intent.ACTION_TIME_CHANGED)
+            addAction(Intent.ACTION_TIMEZONE_CHANGED)
+            addAction(Intent.ACTION_LOCALE_CHANGED)
+        }
+
+        broadcastDispatcher.registerReceiver(intentReceiver, filter,
+                HandlerExecutor(timeTickHandler), UserHandle.SYSTEM)
+
+        post(::updateClock)
+        mView.onAttach(onMeasureListener)
+    }
+
+    override fun onViewDetached() {
+        dateFormat = null
+        mView.onAttach(null)
+        broadcastDispatcher.unregisterReceiver(intentReceiver)
+    }
+
+    private fun updateClock() {
+        if (dateFormat == null) {
+            dateFormat = getFormatFromPattern(datePattern)
+        }
+
+        currentTime.time = systemClock.currentTimeMillis()
+
+        val text = getTextForFormat(currentTime, dateFormat!!)
+        if (text != lastText) {
+            mView.setText(text)
+            lastText = text
+        }
+    }
+
+    private fun maybeChangeFormat(availableWidth: Int) {
+        if (mView.freezeSwitching ||
+                availableWidth > lastWidth && datePattern == longerPattern ||
+                availableWidth < lastWidth && datePattern == ""
+        ) {
+            // Nothing to do
+            return
+        }
+        if (DEBUG) Log.d(TAG, "Width changed. Maybe changing pattern")
+        // Start with longer pattern and see what fits
+        var text = getTextForFormat(currentTime, getFormatFromPattern(longerPattern))
+        var length = mView.getDesiredWidthForText(text)
+        if (length <= availableWidth) {
+            changePattern(longerPattern)
+            return
+        }
+
+        text = getTextForFormat(currentTime, getFormatFromPattern(shorterPattern))
+        length = mView.getDesiredWidthForText(text)
+        if (length <= availableWidth) {
+            changePattern(shorterPattern)
+            return
+        }
+
+        changePattern("")
+    }
+
+    private fun changePattern(newPattern: String) {
+        if (newPattern.equals(datePattern)) return
+        if (DEBUG) Log.d(TAG, "Changing pattern to $newPattern")
+        datePattern = newPattern
+    }
+
+    class Factory @Inject constructor(
+        private val systemClock: SystemClock,
+        private val broadcastDispatcher: BroadcastDispatcher,
+        @Named(Dependency.TIME_TICK_HANDLER_NAME) private val handler: Handler
+    ) {
+        fun create(view: VariableDateView): VariableDateViewController {
+            return VariableDateViewController(
+                    systemClock,
+                    broadcastDispatcher,
+                    handler,
+                    view
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index f8e3647..fc19564 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -36,7 +36,7 @@
 import com.android.settingslib.mobile.TelephonyIcons;
 import com.android.settingslib.wifi.WifiStatusTracker;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
 import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
index c224cf5..1eec639 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -16,8 +16,10 @@
 
 package com.android.systemui.statusbar.policy.dagger;
 
+import android.content.res.Resources;
 import android.os.UserManager;
 
+import com.android.internal.R;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.settings.UserTracker;
@@ -57,6 +59,8 @@
 
 import java.util.concurrent.Executor;
 
+import javax.inject.Named;
+
 import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
@@ -65,6 +69,9 @@
 /** Dagger Module for code in the statusbar.policy package. */
 @Module
 public interface StatusBarPolicyModule {
+
+    String DEVICE_STATE_ROTATION_LOCK_DEFAULTS = "DEVICE_STATE_ROTATION_LOCK_DEFAULTS";
+
     /** */
     @Binds
     BluetoothController provideBluetoothController(BluetoothControllerImpl controllerImpl);
@@ -154,4 +161,14 @@
         controller.init();
         return controller;
     }
+
+    /**
+     * Default values for per-device state rotation lock settings.
+     */
+    @Provides
+    @Named(DEVICE_STATE_ROTATION_LOCK_DEFAULTS)
+    static String[] providesDeviceStateRotationLockDefaults(@Main Resources resources) {
+        return resources.getStringArray(
+                R.array.config_perDeviceStateRotationLockDefaults);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 81999b5..a53d528 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -57,9 +57,9 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
 import com.android.systemui.util.settings.SecureSettings;
@@ -530,6 +530,10 @@
             mThemeManager.applyCurrentUserOverlays(categoryToPackage, null, currentUser,
                     managedProfiles);
         }
+        onOverlaysApplied();
+    }
+
+    protected void onOverlaysApplied() {
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
index 0a29e04..20857ea 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
@@ -106,8 +106,8 @@
                 PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.GET_SERVICES);
         apps.forEach(app -> {
             if (!plugins.containsKey(app.packageName)) return;
-            if (ArrayUtils.contains(manager.getWhitelistedPlugins(), app.packageName)) {
-                // Don't manage whitelisted plugins, they are part of the OS.
+            if (ArrayUtils.contains(manager.getPrivilegedPlugins(), app.packageName)) {
+                // Don't manage privileged plugins, they are part of the OS.
                 return;
             }
             SwitchPreference pref = new PluginPreference(prefContext, app, mPluginEnabler);
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index df889f2..8912448 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -39,6 +39,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.EnhancedEstimates;
 import com.android.systemui.power.EnhancedEstimatesImpl;
+import com.android.systemui.power.dagger.PowerModule;
 import com.android.systemui.qs.dagger.QSModule;
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
 import com.android.systemui.recents.Recents;
@@ -69,6 +70,7 @@
 import com.android.systemui.statusbar.policy.SensorPrivacyController;
 import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl;
 import com.android.systemui.statusbar.tv.notifications.TvNotificationHandler;
+import com.android.systemui.volume.dagger.VolumeModule;
 
 import javax.inject.Named;
 
@@ -81,7 +83,9 @@
  * overridden by the System UI implementation.
  */
 @Module(includes = {
-            QSModule.class
+            PowerModule.class,
+            QSModule.class,
+            VolumeModule.class,
         },
         subcomponents = {
         })
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index 8e7c49a..9283403 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -23,8 +23,7 @@
 import android.view.WindowManager
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.unfold.UnfoldTransitionProgressProvider
-import com.android.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
 import com.android.systemui.statusbar.LightRevealScrim
 import com.android.systemui.statusbar.LinearLightRevealEffect
 import java.util.concurrent.Executor
@@ -93,7 +92,7 @@
         val newScrimView = LightRevealScrim(context, null)
             .apply {
                 revealEffect = LinearLightRevealEffect(isVerticalFold)
-                revealAmountListener = Consumer {}
+                isScrimOpaqueChangedListener = Consumer {}
                 revealAmount = 0f
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
index 98b4209e..bfa50bc 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
@@ -36,6 +36,7 @@
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.WindowManager;
 import android.widget.CheckBox;
 import android.widget.CompoundButton;
 import android.widget.TextView;
@@ -63,6 +64,8 @@
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
+        getWindow().addPrivateFlags(
+                WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
         Intent intent = getIntent();
         mDevice = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
         mAccessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
diff --git a/packages/SystemUI/src/com/android/systemui/util/ViewController.java b/packages/SystemUI/src/com/android/systemui/util/ViewController.java
index 32bbe1c..0dd5788 100644
--- a/packages/SystemUI/src/com/android/systemui/util/ViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/ViewController.java
@@ -110,16 +110,6 @@
     }
 
     /**
-     * Destroys this controller so that it never receives view attach and detach events again.
-     * Does nothing if the view is null.
-     */
-    public void destroy() {
-        if (mView != null) {
-            mView.removeOnAttachStateChangeListener(mOnAttachStateListener);
-        }
-    }
-
-    /**
      * Called when the view is attached and a call to {@link #init()} has been made in either order.
      */
     protected abstract void onViewAttached();
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java
index 1c50496..107fe87 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java
@@ -22,8 +22,10 @@
 
 import com.android.systemui.dagger.qualifiers.Main;
 
+import java.util.Optional;
 import java.util.concurrent.Executor;
 
+import javax.inject.Named;
 import javax.inject.Singleton;
 
 import dagger.Binds;
@@ -35,6 +37,7 @@
  */
 @Module
 public abstract class GlobalConcurrencyModule {
+    public static final String PRE_HANDLER = "pre_handler";
 
     /**
      * Binds {@link ThreadFactoryImpl} to {@link ThreadFactory}.
@@ -64,13 +67,32 @@
      * Provide a Main-Thread Executor.
      */
     @Provides
+    @Singleton
     @Main
     public static Executor provideMainExecutor(Context context) {
         return context.getMainExecutor();
     }
 
+    /**
+     * Provide a Main-Thread DelayableExecutor.
+     */
+    @Provides
+    @Singleton
+    @Main
+    public static DelayableExecutor provideMainDelayableExecutor(@Main Looper looper) {
+        return new ExecutorImpl(looper);
+    }
+
+
     /** */
     @Binds
     @Singleton
     public abstract Execution provideExecution(ExecutionImpl execution);
+
+    /** */
+    @Provides
+    @Named(PRE_HANDLER)
+    public static Optional<Thread.UncaughtExceptionHandler> providesUncaughtExceptionHandler() {
+        return Optional.ofNullable(Thread.getUncaughtExceptionPreHandler());
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
index e9e794e..e8a9bc7 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
@@ -120,16 +120,6 @@
     }
 
     /**
-     * Provide a Main-Thread Executor.
-     */
-    @Provides
-    @SysUISingleton
-    @Main
-    public static DelayableExecutor provideMainDelayableExecutor(@Main Looper looper) {
-        return new ExecutorImpl(looper);
-    }
-
-    /**
      * Provide a Background-Thread Executor by default.
      */
     @Provides
diff --git a/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java b/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
index cdfa145..981bf01 100644
--- a/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
@@ -18,12 +18,15 @@
 
 import com.android.systemui.util.RingerModeTracker;
 import com.android.systemui.util.RingerModeTrackerImpl;
+import com.android.systemui.util.wrapper.UtilWrapperModule;
 
 import dagger.Binds;
 import dagger.Module;
 
 /** Dagger Module for code in the util package. */
-@Module
+@Module(includes = {
+                UtilWrapperModule.class
+        })
 public interface UtilModule {
     /** */
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
index 19ed284..bd11039 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
@@ -87,15 +87,23 @@
                     && (mLastPrimaryEvent == null
                     || !mLastPrimaryEvent.getBelow()
                     || !event.getBelow())) {
-                mSecondaryThresholdSensor.pause();
+                chooseSensor();
                 if (mLastPrimaryEvent == null || !mLastPrimaryEvent.getBelow()) {
                     // Only check the secondary as long as the primary thinks we're near.
-                    mCancelSecondaryRunnable = null;
+                    if (mCancelSecondaryRunnable != null) {
+                        mCancelSecondaryRunnable.run();
+                        mCancelSecondaryRunnable = null;
+                    }
                     return;
                 } else {
                     // Check this sensor again in a moment.
-                    mCancelSecondaryRunnable = mDelayableExecutor.executeDelayed(
-                            mSecondaryThresholdSensor::resume, SECONDARY_PING_INTERVAL_MS);
+                    mCancelSecondaryRunnable = mDelayableExecutor.executeDelayed(() -> {
+                        // This is safe because we know that mSecondaryThresholdSensor
+                        // is loaded, otherwise we wouldn't be here.
+                        mPrimaryThresholdSensor.pause();
+                        mSecondaryThresholdSensor.resume();
+                    },
+                        SECONDARY_PING_INTERVAL_MS);
                 }
             }
             logDebug("Secondary sensor event: " + event.getBelow() + ".");
@@ -159,12 +167,8 @@
      * of what is reported by the primary sensor.
      */
     public void setSecondarySafe(boolean safe) {
-        mSecondarySafe = safe;
-        if (!mSecondarySafe) {
-            mSecondaryThresholdSensor.pause();
-        } else {
-            mSecondaryThresholdSensor.resume();
-        }
+        mSecondarySafe = mSecondaryThresholdSensor.isLoaded() && safe;
+        chooseSensor();
     }
 
     /**
@@ -209,16 +213,30 @@
             return;
         }
         if (!mInitializedListeners) {
+            mPrimaryThresholdSensor.pause();
+            mSecondaryThresholdSensor.pause();
             mPrimaryThresholdSensor.register(mPrimaryEventListener);
-            if (!mSecondarySafe) {
-                mSecondaryThresholdSensor.pause();
-            }
             mSecondaryThresholdSensor.register(mSecondaryEventListener);
             mInitializedListeners = true;
         }
         logDebug("Registering sensor listener");
-        mPrimaryThresholdSensor.resume();
+
         mRegistered = true;
+        chooseSensor();
+    }
+
+    private void chooseSensor() {
+        mExecution.assertIsMainThread();
+        if (!mRegistered || mPaused || mListeners.isEmpty()) {
+            return;
+        }
+        if (mSecondarySafe) {
+            mSecondaryThresholdSensor.resume();
+            mPrimaryThresholdSensor.pause();
+        } else {
+            mPrimaryThresholdSensor.resume();
+            mSecondaryThresholdSensor.pause();
+        }
     }
 
     /**
@@ -281,17 +299,26 @@
 
         mLastPrimaryEvent = event;
 
-        if (event.getBelow() && mSecondaryThresholdSensor.isLoaded()) {
-            logDebug("Primary sensor is near. Checking secondary.");
+        if (mSecondarySafe && mSecondaryThresholdSensor.isLoaded()) {
+            logDebug("Primary sensor reported " + (event.getBelow() ? "near" : "far")
+                    + ". Checking secondary.");
             if (mCancelSecondaryRunnable == null) {
                 mSecondaryThresholdSensor.resume();
             }
-        } else {
-            if (!mSecondaryThresholdSensor.isLoaded()) {
-                logDebug("Primary sensor event: " + event.getBelow() + ". No secondary.");
-            } else {
-                logDebug("Primary sensor event: " + event.getBelow() + ".");
+            return;
+        }
+
+
+        if (!mSecondaryThresholdSensor.isLoaded()) {  // No secondary
+            logDebug("Primary sensor event: " + event.getBelow() + ". No secondary.");
+            onSensorEvent(event);
+        } else if (event.getBelow()) {  // Covered? Check secondary.
+            logDebug("Primary sensor event: " + event.getBelow() + ". Checking secondary.");
+            if (mCancelSecondaryRunnable != null) {
+                mCancelSecondaryRunnable.run();
             }
+            mSecondaryThresholdSensor.resume();
+        } else {  // Uncovered. Report immediately.
             onSensorEvent(event);
         }
     }
@@ -303,7 +330,7 @@
         }
 
         if (!mSecondarySafe && !event.getBelow()) {
-            mSecondaryThresholdSensor.pause();
+            chooseSensor();
         }
 
         mLastEvent = event;
diff --git a/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt b/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
new file mode 100644
index 0000000..2a0cc7d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.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.util.wrapper
+
+import android.content.Context
+import com.android.internal.view.RotationPolicy
+import com.android.internal.view.RotationPolicy.RotationPolicyListener
+import javax.inject.Inject
+
+/**
+ * Testable wrapper interface around RotationPolicy {link com.android.internal.view.RotationPolicy}
+ */
+interface RotationPolicyWrapper {
+    fun setRotationLock(enabled: Boolean)
+    fun setRotationLockAtAngle(enabled: Boolean, rotation: Int)
+    fun getRotationLockOrientation(): Int
+    fun isRotationLockToggleVisible(): Boolean
+    fun isRotationLocked(): Boolean
+    fun registerRotationPolicyListener(listener: RotationPolicyListener, userHandle: Int)
+    fun unregisterRotationPolicyListener(listener: RotationPolicyListener)
+}
+
+class RotationPolicyWrapperImpl @Inject constructor(private val context: Context) :
+    RotationPolicyWrapper {
+
+    override fun setRotationLock(enabled: Boolean) {
+        RotationPolicy.setRotationLock(context, enabled)
+    }
+
+    override fun setRotationLockAtAngle(enabled: Boolean, rotation: Int) {
+        RotationPolicy.setRotationLockAtAngle(context, enabled, rotation)
+    }
+
+    override fun getRotationLockOrientation(): Int =
+        RotationPolicy.getRotationLockOrientation(context)
+
+    override fun isRotationLockToggleVisible(): Boolean =
+        RotationPolicy.isRotationLockToggleVisible(context)
+
+    override fun isRotationLocked(): Boolean =
+        RotationPolicy.isRotationLocked(context)
+
+    override fun registerRotationPolicyListener(
+        listener: RotationPolicyListener,
+        userHandle: Int
+    ) {
+        RotationPolicy.registerRotationPolicyListener(context, listener, userHandle)
+    }
+
+    override fun unregisterRotationPolicyListener(listener: RotationPolicyListener) {
+        RotationPolicy.unregisterRotationPolicyListener(context, listener)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/wrapper/UtilWrapperModule.kt b/packages/SystemUI/src/com/android/systemui/util/wrapper/UtilWrapperModule.kt
new file mode 100644
index 0000000..7e3aa27
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/wrapper/UtilWrapperModule.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.wrapper
+
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+
+@Module
+abstract class UtilWrapperModule {
+
+    @Binds
+    @SysUISingleton
+    abstract fun bindRotationPolicyWrapper(impl: RotationPolicyWrapperImpl): RotationPolicyWrapper
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index 56f1c09..c083c14 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -20,13 +20,11 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
-import android.media.AudioManager;
 import android.media.VolumePolicy;
 import android.os.Bundle;
 import android.view.WindowManager.LayoutParams;
 
 import com.android.settingslib.applications.InterestingConfigChanges;
-import com.android.systemui.Dependency;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.demomode.DemoMode;
 import com.android.systemui.demomode.DemoModeController;
@@ -67,6 +65,7 @@
             ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE
             | ActivityInfo.CONFIG_ASSETS_PATHS | ActivityInfo.CONFIG_UI_MODE);
     private final KeyguardViewMediator mKeyguardViewMediator;
+    private final ActivityStarter mActivityStarter;
     private VolumeDialog mDialog;
     private VolumePolicy mVolumePolicy = new VolumePolicy(
             DEFAULT_VOLUME_DOWN_TO_ENTER_SILENT,  // volumeDownToEnterSilent
@@ -79,18 +78,23 @@
     public VolumeDialogComponent(
             Context context,
             KeyguardViewMediator keyguardViewMediator,
+            ActivityStarter activityStarter,
             VolumeDialogControllerImpl volumeDialogController,
-            DemoModeController demoModeController) {
+            DemoModeController demoModeController,
+            PluginDependencyProvider pluginDependencyProvider,
+            ExtensionController extensionController,
+            TunerService tunerService,
+            VolumeDialog volumeDialog) {
         mContext = context;
         mKeyguardViewMediator = keyguardViewMediator;
+        mActivityStarter = activityStarter;
         mController = volumeDialogController;
         mController.setUserActivityListener(this);
         // Allow plugins to reference the VolumeDialogController.
-        Dependency.get(PluginDependencyProvider.class)
-                .allowPluginDependency(VolumeDialogController.class);
-        Dependency.get(ExtensionController.class).newExtension(VolumeDialog.class)
+        pluginDependencyProvider.allowPluginDependency(VolumeDialogController.class);
+        extensionController.newExtension(VolumeDialog.class)
                 .withPlugin(VolumeDialog.class)
-                .withDefault(this::createDefault)
+                .withDefault(() -> volumeDialog)
                 .withCallback(dialog -> {
                     if (mDialog != null) {
                         mDialog.destroy();
@@ -99,19 +103,11 @@
                     mDialog.init(LayoutParams.TYPE_VOLUME_OVERLAY, mVolumeDialogCallback);
                 }).build();
         applyConfiguration();
-        Dependency.get(TunerService.class).addTunable(this, VOLUME_DOWN_SILENT, VOLUME_UP_SILENT,
+        tunerService.addTunable(this, VOLUME_DOWN_SILENT, VOLUME_UP_SILENT,
                 VOLUME_SILENT_DO_NOT_DISTURB);
         demoModeController.addCallback(this);
     }
 
-    protected VolumeDialog createDefault() {
-        VolumeDialogImpl impl = new VolumeDialogImpl(mContext);
-        impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
-        impl.setAutomute(true);
-        impl.setSilentMode(false);
-        return impl;
-    }
-
     @Override
     public void onTuningChanged(String key, String newValue) {
         boolean volumeDownToEnterSilent = mVolumePolicy.volumeDownToEnterSilent;
@@ -189,8 +185,7 @@
     }
 
     private void startSettings(Intent intent) {
-        Dependency.get(ActivityStarter.class).startActivity(intent,
-                true /* onlyProvisioned */, true /* dismissShade */);
+        mActivityStarter.startActivity(intent, true /* onlyProvisioned */, true /* dismissShade */);
     }
 
     private final VolumeDialogImpl.Callback mVolumeDialogCallback = new VolumeDialogImpl.Callback() {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index f317628..58f74a0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -106,7 +106,6 @@
 import com.android.internal.graphics.drawable.BackgroundBlurDrawable;
 import com.android.internal.view.RotationPolicy;
 import com.android.settingslib.Utils;
-import com.android.systemui.Dependency;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
@@ -235,6 +234,10 @@
     private final Object mSafetyWarningLock = new Object();
     private final Accessibility mAccessibility = new Accessibility();
 
+    private final ConfigurationController mConfigurationController;
+    private final MediaOutputDialogFactory mMediaOutputDialogFactory;
+    private final ActivityStarter mActivityStarter;
+
     private boolean mShowing;
     private boolean mShowA11yStream;
 
@@ -256,14 +259,24 @@
     private Consumer<Boolean> mCrossWindowBlurEnabledListener;
     private BackgroundBlurDrawable mDialogRowsViewBackground;
 
-    public VolumeDialogImpl(Context context) {
+    public VolumeDialogImpl(
+            Context context,
+            VolumeDialogController volumeDialogController,
+            AccessibilityManagerWrapper accessibilityManagerWrapper,
+            DeviceProvisionedController deviceProvisionedController,
+            ConfigurationController configurationController,
+            MediaOutputDialogFactory mediaOutputDialogFactory,
+            ActivityStarter activityStarter) {
         mContext =
                 new ContextThemeWrapper(context, R.style.volume_dialog_theme);
-        mController = Dependency.get(VolumeDialogController.class);
+        mController = volumeDialogController;
         mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
         mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
-        mAccessibilityMgr = Dependency.get(AccessibilityManagerWrapper.class);
-        mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
+        mAccessibilityMgr = accessibilityManagerWrapper;
+        mDeviceProvisionedController = deviceProvisionedController;
+        mConfigurationController = configurationController;
+        mMediaOutputDialogFactory = mediaOutputDialogFactory;
+        mActivityStarter = activityStarter;
         mShowActiveStreamOnly = showActiveStreamOnly();
         mHasSeenODICaptionsTooltip =
                 Prefs.getBoolean(context, Prefs.Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP, false);
@@ -306,14 +319,14 @@
         mController.addCallback(mControllerCallbackH, mHandler);
         mController.getState();
 
-        Dependency.get(ConfigurationController.class).addCallback(this);
+        mConfigurationController.addCallback(this);
     }
 
     @Override
     public void destroy() {
         mController.removeCallback(mControllerCallbackH);
         mHandler.removeCallbacksAndMessages(null);
-        Dependency.get(ConfigurationController.class).removeCallback(this);
+        mConfigurationController.removeCallback(this);
     }
 
     @Override
@@ -1017,9 +1030,8 @@
                 Events.writeEvent(Events.EVENT_SETTINGS_CLICK);
                 Intent intent = new Intent(Settings.Panel.ACTION_VOLUME);
                 dismissH(DISMISS_REASON_SETTINGS_CLICKED);
-                Dependency.get(MediaOutputDialogFactory.class).dismiss();
-                Dependency.get(ActivityStarter.class).startActivity(intent,
-                        true /* dismissShade */);
+                mMediaOutputDialogFactory.dismiss();
+                mActivityStarter.startActivity(intent, true /* dismissShade */);
             });
         }
     }
@@ -2255,6 +2267,11 @@
 
         @Override
         public void onClick(View view) {
+            // If the ringer drawer isn't open, don't let anything in it be clicked.
+            if (!mIsRingerDrawerOpen) {
+                return;
+            }
+
             setRingerMode(mClickedRingerMode);
 
             mRingerDrawerIconAnimatingSelected = getDrawerIconViewForMode(mClickedRingerMode);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index 1ef4c16..79aa643 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -16,11 +16,23 @@
 
 package com.android.systemui.volume.dagger;
 
+import android.content.Context;
+import android.media.AudioManager;
+
+import com.android.systemui.media.dialog.MediaOutputDialogFactory;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.VolumeDialog;
+import com.android.systemui.plugins.VolumeDialogController;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.volume.VolumeComponent;
 import com.android.systemui.volume.VolumeDialogComponent;
+import com.android.systemui.volume.VolumeDialogImpl;
 
 import dagger.Binds;
 import dagger.Module;
+import dagger.Provides;
 
 
 /** Dagger Module for code in the volume package. */
@@ -29,4 +41,28 @@
     /** */
     @Binds
     VolumeComponent provideVolumeComponent(VolumeDialogComponent volumeDialogComponent);
+
+    /** */
+    @Provides
+    static VolumeDialog provideVolumeDialog(
+            Context context,
+            VolumeDialogController volumeDialogController,
+            AccessibilityManagerWrapper accessibilityManagerWrapper,
+            DeviceProvisionedController deviceProvisionedController,
+            ConfigurationController configurationController,
+            MediaOutputDialogFactory mediaOutputDialogFactory,
+            ActivityStarter activityStarter) {
+        VolumeDialogImpl impl = new VolumeDialogImpl(
+                context,
+                volumeDialogController,
+                accessibilityManagerWrapper,
+                deviceProvisionedController,
+                configurationController,
+                mediaOutputDialogFactory,
+                activityStarter);
+        impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
+        impl.setAutomute(true);
+        impl.setSilentMode(false);
+        return impl;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardCarousel.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardCarousel.java
index 1e1b459..77fd2e8 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardCarousel.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletCardCarousel.java
@@ -190,6 +190,15 @@
     }
 
     /**
+     * Sets the adapter again in the RecyclerView, updating the ViewHolders children's layout.
+     * This is needed when changing the state of the device (eg fold/unfold) so the ViewHolders are
+     * recreated.
+     */
+    void resetAdapter() {
+        setAdapter(mWalletCardCarouselAdapter);
+    }
+
+    /**
      * Returns true if the data set is changed.
      */
     boolean setData(List<WalletCardViewInfo> data, int selectedIndex, boolean hasLockStateChanged) {
@@ -376,8 +385,8 @@
             CardView cardView = viewHolder.mCardView;
             cardView.setRadius(mCornerRadiusPx);
             ViewGroup.LayoutParams layoutParams = cardView.getLayoutParams();
-            layoutParams.width = mCardWidthPx;
-            layoutParams.height = mCardHeightPx;
+            layoutParams.width = getCardWidthPx();
+            layoutParams.height = getCardHeightPx();
             view.setTag(viewHolder);
             return viewHolder;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
index 420f84a..9b2702f 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
@@ -99,17 +99,13 @@
         mCardCarousel.setExpectedViewWidth(getWidth());
     }
 
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        updateViewForOrientation(newConfig.orientation);
-    }
-
     private void updateViewForOrientation(@Configuration.Orientation int orientation) {
         if (orientation == Configuration.ORIENTATION_PORTRAIT) {
             renderViewPortrait();
         } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
             renderViewLandscape();
         }
+        mCardCarousel.resetAdapter(); // necessary to update cards width
         ViewGroup.LayoutParams params = mCardCarouselContainer.getLayoutParams();
         if (params instanceof MarginLayoutParams) {
             ((MarginLayoutParams) params).topMargin =
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index a29a638..d3581a9 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -50,7 +50,6 @@
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseArray;
-import android.view.View;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -61,11 +60,10 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.scrim.ScrimView;
 import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.notification.NotificationChannelHelper;
@@ -80,7 +78,6 @@
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ZenModeController;
@@ -118,7 +115,6 @@
     private final NotifPipeline mNotifPipeline;
     private final Executor mSysuiMainExecutor;
 
-    private ScrimView mBubbleScrim;
     private final Bubbles.SysuiProxy mSysuiProxy;
     // TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline
     private final List<NotifCallback> mCallbacks = new ArrayList<>();
@@ -193,12 +189,6 @@
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE))
                 : statusBarService;
 
-        mBubbleScrim = new ScrimView(mContext);
-        mBubbleScrim.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
-        mBubbles.setBubbleScrim(mBubbleScrim, (executor, looper) -> {
-            mBubbleScrim.setExecutor(executor, looper);
-        });
-
         if (featureFlags.isNewNotifPipelineRenderingEnabled()) {
             setupNotifPipeline();
         } else {
@@ -603,15 +593,6 @@
     }
 
     /**
-     * Returns the scrim drawn behind the bubble stack. This is managed by {@link ScrimController}
-     * since we want the scrim's appearance and behavior to be identical to that of the notification
-     * shade scrim.
-     */
-    public ScrimView getScrimForBubble() {
-        return mBubbleScrim;
-    }
-
-    /**
      * We intercept notification entries (including group summaries) dismissed by the user when
      * there is an active bubble associated with it. We do this so that developers can still
      * cancel it (and hence the bubbles associated with it).
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
index 39a8bd9..dbdc460 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
@@ -25,6 +25,7 @@
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayInsetsController;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.SystemWindows;
@@ -58,10 +59,10 @@
     @WMSingleton
     @Provides
     static DisplayImeController provideDisplayImeController(IWindowManager wmService,
-            DisplayController displayController, @ShellMainThread ShellExecutor mainExecutor,
-            TransactionPool transactionPool) {
-        return new DisplayImeController(wmService, displayController, mainExecutor,
-                transactionPool);
+            DisplayController displayController, DisplayInsetsController displayInsetsController,
+            @ShellMainThread ShellExecutor mainExecutor, TransactionPool transactionPool) {
+        return new DisplayImeController(wmService, displayController, displayInsetsController,
+                mainExecutor, transactionPool);
     }
 
     //
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index 7e733a9..c178b29 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -44,6 +44,7 @@
 import com.android.wm.shell.bubbles.Bubbles;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayInsetsController;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.common.ShellExecutor;
@@ -55,6 +56,7 @@
 import com.android.wm.shell.common.annotations.ShellMainThread;
 import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
 import com.android.wm.shell.draganddrop.DragAndDropController;
+import com.android.wm.shell.freeform.FreeformTaskListener;
 import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
 import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
@@ -109,6 +111,14 @@
 
     @WMSingleton
     @Provides
+    static DisplayInsetsController provideDisplayInsetsController( IWindowManager wmService,
+            DisplayController displayController,
+            @ShellMainThread ShellExecutor mainExecutor) {
+        return new DisplayInsetsController(wmService, displayController, mainExecutor);
+    }
+
+    @WMSingleton
+    @Provides
     static DisplayLayout provideDisplayLayout() {
         return new DisplayLayout();
     }
@@ -116,8 +126,8 @@
     @WMSingleton
     @Provides
     static DragAndDropController provideDragAndDropController(Context context,
-            DisplayController displayController) {
-        return new DragAndDropController(context, displayController);
+            DisplayController displayController, UiEventLogger uiEventLogger) {
+        return new DragAndDropController(context, displayController, uiEventLogger);
     }
 
     @WMSingleton
@@ -213,6 +223,13 @@
     }
 
     //
+    // Freeform (optional feature)
+    //
+
+    @BindsOptionalOf
+    abstract Optional<FreeformTaskListener> optionalFreeformTaskListener();
+
+    //
     // Hide display cutout
     //
 
@@ -328,9 +345,11 @@
     @WMSingleton
     @Provides
     static Transitions provideTransitions(ShellTaskOrganizer organizer, TransactionPool pool,
-            Context context, @ShellMainThread ShellExecutor mainExecutor,
+            DisplayController displayController, Context context,
+            @ShellMainThread ShellExecutor mainExecutor,
             @ShellAnimationThread ShellExecutor animExecutor) {
-        return new Transitions(organizer, pool, context, mainExecutor, animExecutor);
+        return new Transitions(organizer, pool, displayController, context, mainExecutor,
+                animExecutor);
     }
 
     //
@@ -442,7 +461,9 @@
 
     @WMSingleton
     @Provides
-    static ShellInitImpl provideShellInitImpl(DisplayImeController displayImeController,
+    static ShellInitImpl provideShellInitImpl(DisplayController displayController,
+            DisplayImeController displayImeController,
+            DisplayInsetsController displayInsetsController,
             DragAndDropController dragAndDropController,
             ShellTaskOrganizer shellTaskOrganizer,
             Optional<BubbleController> bubblesOptional,
@@ -451,10 +472,13 @@
             Optional<AppPairsController> appPairsOptional,
             Optional<PipTouchHandler> pipTouchHandlerOptional,
             FullscreenTaskListener fullscreenTaskListener,
+            Optional<Optional<FreeformTaskListener>> freeformTaskListener,
             Transitions transitions,
             StartingWindowController startingWindow,
             @ShellMainThread ShellExecutor mainExecutor) {
-        return new ShellInitImpl(displayImeController,
+        return new ShellInitImpl(displayController,
+                displayImeController,
+                displayInsetsController,
                 dragAndDropController,
                 shellTaskOrganizer,
                 bubblesOptional,
@@ -463,6 +487,7 @@
                 appPairsOptional,
                 pipTouchHandlerOptional,
                 fullscreenTaskListener,
+                freeformTaskListener,
                 transitions,
                 startingWindow,
                 mainExecutor);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index be7813e..83c2a9b 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -28,6 +28,7 @@
 import com.android.wm.shell.apppairs.AppPairsController;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayInsetsController;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
@@ -36,6 +37,7 @@
 import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.common.annotations.ChoreographerSfVsync;
 import com.android.wm.shell.common.annotations.ShellMainThread;
+import com.android.wm.shell.freeform.FreeformTaskListener;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
 import com.android.wm.shell.onehanded.OneHandedController;
 import com.android.wm.shell.pip.Pip;
@@ -82,10 +84,23 @@
     @WMSingleton
     @Provides
     static DisplayImeController provideDisplayImeController(IWindowManager wmService,
-            DisplayController displayController, @ShellMainThread ShellExecutor mainExecutor,
+            DisplayController displayController, DisplayInsetsController displayInsetsController,
+            @ShellMainThread ShellExecutor mainExecutor,
             TransactionPool transactionPool) {
-        return new DisplayImeController(wmService, displayController, mainExecutor,
-                transactionPool);
+        return new DisplayImeController(wmService, displayController, displayInsetsController,
+                mainExecutor, transactionPool);
+    }
+
+    //
+    // Freeform
+    //
+
+    @WMSingleton
+    @Provides
+    static Optional<FreeformTaskListener> provideFreeformTaskListener(
+            Context context,
+            SyncTransactionQueue syncQueue) {
+        return Optional.ofNullable(FreeformTaskListener.create(context, syncQueue));
     }
 
     //
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index f1c687f..2c9c980 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -93,6 +93,13 @@
         <activity android:name="com.android.systemui.screenshot.RecyclerViewActivity"
                   android:exported="false" />
 
+        <!-- started from UsbDeviceSettingsManager -->
+        <activity android:name=".usb.UsbPermissionActivityTest$UsbPermissionActivityTestable"
+                  android:exported="false"
+                  android:theme="@style/Theme.SystemUI.Dialog.Alert"
+                  android:finishOnCloseSystemDialogs="true"
+                  android:excludeFromRecents="true" />
+
         <provider
             android:name="androidx.lifecycle.ProcessLifecycleOwnerInitializer"
             tools:replace="android:authorities"
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
new file mode 100644
index 0000000..db87c5d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
@@ -0,0 +1,101 @@
+/*
+ * 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.keyguard
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class KeyguardListenQueueTest : SysuiTestCase() {
+
+    @Test
+    fun testQueueIsBounded() {
+        val size = 5
+        val queue = KeyguardListenQueue(sizePerModality = size)
+
+        val fingerprints = List(100) { fingerprintModel(it) }
+        fingerprints.forEach { queue.add(it) }
+
+        assertThat(queue.models).containsExactlyElementsIn(fingerprints.takeLast(size))
+
+        val faces = List(100) { faceModel(it) }
+        faces.forEach { queue.add(it) }
+
+        assertThat(queue.models).containsExactlyElementsIn(
+            faces.takeLast(size) + fingerprints.takeLast(5)
+        )
+
+        repeat(100) {
+            queue.add(faceModel(-1))
+            queue.add(fingerprintModel(-1))
+        }
+        assertThat(queue.models).hasSize(2 * size)
+        assertThat(queue.models.count { it.userId == -1 }).isEqualTo(2 * size)
+    }
+}
+
+private fun fingerprintModel(user: Int) = KeyguardFingerprintListenModel(
+    timeMillis = System.currentTimeMillis(),
+    userId = user,
+    listening = false,
+    biometricEnabledForUser = false,
+    bouncer = false,
+    canSkipBouncer = false,
+    credentialAttempted = false,
+    deviceInteractive = false,
+    dreaming = false,
+    encryptedOrLockdown = false,
+    fingerprintDisabled = false,
+    fingerprintLockedOut = false,
+    goingToSleep = false,
+    keyguardGoingAway = false,
+    keyguardIsVisible = false,
+    keyguardOccluded = false,
+    occludingAppRequestingFp = false,
+    primaryUser = false,
+    shouldListenForFingerprintAssistant = false,
+    switchingUser = false,
+    udfps = false,
+    userDoesNotHaveTrust = false,
+    userNeedsStrongAuth = false
+)
+
+private fun faceModel(user: Int) = KeyguardFaceListenModel(
+    timeMillis = System.currentTimeMillis(),
+    userId = user,
+    listening = false,
+    authInterruptActive = false,
+    becauseCannotSkipBouncer = false,
+    biometricSettingEnabledForUser = false,
+    bouncer = false,
+    faceAuthenticated = false,
+    faceDisabled = false,
+    keyguardAwake = false,
+    keyguardGoingAway = false,
+    listeningForFaceAssistant = false,
+    lockIconPressed = false,
+    occludingAppRequestingFaceAuth = false,
+    primaryUser = false,
+    scanningAllowedByStrongAuth = false,
+    secureCameraLaunched = false,
+    switchingUser = false
+)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index bb71bed8..8e1e42a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.classifier.FalsingCollectorFake
+import com.android.systemui.statusbar.policy.DevicePostureController
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -62,6 +63,8 @@
     private lateinit var mKeyguardMessageAreaController: KeyguardMessageAreaController
     @Mock
     private lateinit var mLockPatternView: LockPatternView
+    @Mock
+    private lateinit var mPostureController: DevicePostureController
 
     private lateinit var mKeyguardPatternViewController: KeyguardPatternViewController
 
@@ -78,7 +81,7 @@
         mKeyguardPatternViewController = KeyguardPatternViewController(mKeyguardPatternView,
         mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
                 mLatencyTracker, mFalsingCollector, mEmergencyButtonController,
-                mKeyguardMessageAreaControllerFactory)
+                mKeyguardMessageAreaControllerFactory, mPostureController)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index 0276323..2efd369 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -165,7 +165,8 @@
 
         mKeyguardSecurityContainer.setOneHandedModeLeftAligned(
                 /* leftAligned= */false, /* animate= */false);
-        verify(mSecurityViewFlipper).setTranslationX(SCREEN_WIDTH / 2.0f);
+        verify(mSecurityViewFlipper).setTranslationX(
+                mKeyguardSecurityContainer.getWidth() - mSecurityViewFlipper.getWidth());
 
         mKeyguardSecurityContainer.setOneHandedModeLeftAligned(
                 /* leftAligned= */true, /* animate= */false);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index ec4dfba..d3557d4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -86,8 +86,8 @@
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.telephony.TelephonyListenerManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 76cc7a0e..7794136 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -188,6 +188,7 @@
     public void deleteWindowMagnification_enableAtTheBottom_overlapFlagIsFalse() {
         final WindowManager wm = mContext.getSystemService(WindowManager.class);
         final Rect bounds = wm.getCurrentWindowMetrics().getBounds();
+        setSystemGestureInsets();
 
         mInstrumentation.runOnMainSync(() -> {
             mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
@@ -463,10 +464,7 @@
     @Test
     public void moveWindowMagnificationToTheBottom_enabledWithGestureInset_overlapFlagIsTrue() {
         final Rect bounds = mWindowManager.getCurrentWindowMetrics().getBounds();
-        final WindowInsets testInsets = new WindowInsets.Builder()
-                .setInsets(systemGestures(), Insets.of(0, 0, 0, 10))
-                .build();
-        mWindowManager.setWindowInsets(testInsets);
+        setSystemGestureInsets();
         mInstrumentation.runOnMainSync(() -> {
             mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
                     Float.NaN);
@@ -492,4 +490,11 @@
     private boolean hasMagnificationOverlapFlag() {
         return (mSysUiState.getFlags() & SYSUI_STATE_MAGNIFICATION_OVERLAP) != 0;
     }
+
+    private void setSystemGestureInsets() {
+        final WindowInsets testInsets = new WindowInsets.Builder()
+                .setInsets(systemGestures(), Insets.of(0, 0, 0, 10))
+                .build();
+        mWindowManager.setWindowInsets(testInsets);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java
index 06e27b5..f09d7b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.accessibility.floatingmenu;
 
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.view.View.OVER_SCROLL_ALWAYS;
 import static android.view.View.OVER_SCROLL_NEVER;
 import static android.view.WindowInsets.Type.ime;
@@ -134,7 +135,10 @@
         mMenuHalfHeight = menuHeight / 2;
         mScreenHalfWidth = screenWidth / 2;
         mScreenHalfHeight = mScreenHeight / 2;
-        mMaxWindowX = screenWidth - margin - menuWidth;
+        int marginStartEnd =
+                mContext.getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT
+                        ? margin : 0;
+        mMaxWindowX = screenWidth - marginStartEnd - menuWidth;
         mMenuWindowHeight = menuHeight + margin * 2;
         mMaxWindowY = mScreenHeight - mMenuWindowHeight;
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
index 14f112b..cc35a8f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
@@ -196,7 +196,7 @@
         return RemoteAnimationTarget(
                 0, RemoteAnimationTarget.MODE_OPENING, SurfaceControl(), false, Rect(), Rect(), 0,
                 Point(), Rect(), bounds, WindowConfiguration(), false, SurfaceControl(), Rect(),
-                taskInfo
+                taskInfo, false
         )
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java
new file mode 100644
index 0000000..1d038a4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.battery;
+
+import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;
+
+import static com.android.systemui.util.mockito.KotlinMockitoHelpersKt.eq;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.os.Handler;
+import android.provider.Settings;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.tuner.TunerService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+public class BatteryMeterViewControllerTest extends SysuiTestCase {
+    @Mock
+    private BatteryMeterView mBatteryMeterView;
+
+    @Mock
+    private ConfigurationController mConfigurationController;
+    @Mock
+    private TunerService mTunerService;
+    @Mock
+    private BroadcastDispatcher mBroadcastDispatcher;
+    @Mock
+    private Handler mHandler;
+    @Mock
+    private ContentResolver mContentResolver;
+    @Mock
+    private BatteryController mBatteryController;
+
+    private BatteryMeterViewController mController;
+
+    @Before
+    public void setup() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        when(mBatteryMeterView.getContext()).thenReturn(mContext);
+        when(mBatteryMeterView.getResources()).thenReturn(mContext.getResources());
+
+        mController = new BatteryMeterViewController(
+                mBatteryMeterView,
+                mConfigurationController,
+                mTunerService,
+                mBroadcastDispatcher,
+                mHandler,
+                mContentResolver,
+                mBatteryController
+        );
+    }
+
+    @Test
+    public void onViewAttached_callbacksRegistered() {
+        mController.onViewAttached();
+
+        verify(mConfigurationController).addCallback(any());
+        verify(mTunerService).addTunable(any(), any());
+        verify(mContentResolver).registerContentObserver(
+                eq(Settings.System.getUriFor(SHOW_BATTERY_PERCENT)), anyBoolean(), any(), anyInt()
+        );
+        verify(mContentResolver).registerContentObserver(
+                eq(Settings.Global.getUriFor(Settings.Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME)),
+                anyBoolean(),
+                any()
+        );
+        verify(mBatteryController).addCallback(any());
+    }
+
+    @Test
+    public void onViewDetached_callbacksUnregistered() {
+        // Set everything up first.
+        mController.onViewAttached();
+
+        mController.onViewDetached();
+
+        verify(mConfigurationController).removeCallback(any());
+        verify(mTunerService).removeTunable(any());
+        verify(mContentResolver).unregisterContentObserver(any());
+        verify(mBatteryController).removeCallback(any());
+    }
+
+    @Test
+    public void ignoreTunerUpdates_afterOnViewAttached_callbackUnregistered() {
+        // Start out receiving tuner updates
+        mController.onViewAttached();
+
+        mController.ignoreTunerUpdates();
+
+        verify(mTunerService).removeTunable(any());
+    }
+
+    @Test
+    public void ignoreTunerUpdates_beforeOnViewAttached_callbackNeverRegistered() {
+        mController.ignoreTunerUpdates();
+
+        mController.onViewAttached();
+
+        verify(mTunerService, never()).addTunable(any(), any());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt
new file mode 100644
index 0000000..b4ff2a5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.battery
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.battery.BatteryMeterView.BatteryEstimateFetcher
+import com.android.systemui.statusbar.policy.BatteryController.EstimateFetchCompletion
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class BatteryMeterViewTest : SysuiTestCase() {
+
+    private lateinit var mBatteryMeterView: BatteryMeterView
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        mBatteryMeterView = BatteryMeterView(mContext, null)
+    }
+
+    @Test
+    fun updatePercentText_estimateModeAndNotCharging_estimateFetched() {
+        mBatteryMeterView.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
+        mBatteryMeterView.setBatteryEstimateFetcher(Fetcher())
+
+        mBatteryMeterView.updatePercentText()
+
+        assertThat(mBatteryMeterView.batteryPercentViewText).isEqualTo(ESTIMATE)
+    }
+
+    @Test
+    fun updatePercentText_noBatteryEstimateFetcher_noCrash() {
+        mBatteryMeterView.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
+
+        mBatteryMeterView.updatePercentText()
+        // No assert needed
+    }
+
+    private class Fetcher : BatteryEstimateFetcher {
+        override fun fetchBatteryTimeRemainingEstimate(
+                completion: EstimateFetchCompletion) {
+            completion.onBatteryRemainingEstimateRetrieved(ESTIMATE)
+        }
+    }
+
+    private companion object {
+        const val ESTIMATE = "2 hours 2 minutes"
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java
index 5cd7810..b6d1e42 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceToFingerprintViewTest.java
@@ -23,7 +23,6 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
@@ -48,9 +47,9 @@
 import com.android.systemui.SysuiTestCase;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -78,14 +77,16 @@
     @Mock private TextView mIndicatorView;
     @Mock private ImageView mIconView;
     @Mock private View mIconHolderView;
-    @Mock private AuthBiometricFaceView.IconController mIconController;
+    @Mock private AuthBiometricFaceView.IconController mFaceIconController;
+    @Mock private AuthBiometricFaceToFingerprintView.UdfpsIconController mUdfpsIconController;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
 
         mFaceToFpView = new TestableView(mContext);
-        mFaceToFpView.mIconController = mIconController;
+        mFaceToFpView.mFaceIconController = mFaceIconController;
+        mFaceToFpView.mUdfpsIconController = mUdfpsIconController;
         mFaceToFpView.setCallback(mCallback);
 
         mFaceToFpView.mNegativeButton = mNegativeButton;
@@ -99,20 +100,23 @@
     @Test
     public void testStateUpdated_whenDialogAnimatedIn() {
         mFaceToFpView.onDialogAnimatedIn();
-        verify(mFaceToFpView.mIconController)
+        verify(mFaceToFpView.mFaceIconController)
                 .updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING));
+        verify(mFaceToFpView.mUdfpsIconController, never()).updateState(anyInt());
     }
 
     @Test
     public void testIconUpdatesState_whenDialogStateUpdated() {
         mFaceToFpView.onDialogAnimatedIn();
-        verify(mFaceToFpView.mIconController)
+        verify(mFaceToFpView.mFaceIconController)
                 .updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING));
+        verify(mFaceToFpView.mUdfpsIconController, never()).updateState(anyInt());
 
         mFaceToFpView.updateState(AuthBiometricFaceView.STATE_AUTHENTICATED);
-        verify(mFaceToFpView.mIconController).updateState(
+        verify(mFaceToFpView.mFaceIconController).updateState(
                 eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING),
                 eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATED));
+        verify(mFaceToFpView.mUdfpsIconController, never()).updateState(anyInt());
 
         assertEquals(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATED, mFaceToFpView.mState);
     }
@@ -120,21 +124,20 @@
     @Test
     public void testStateUpdated_whenSwitchToFingerprint() {
         mFaceToFpView.onDialogAnimatedIn();
-        verify(mFaceToFpView.mIconController)
+        verify(mFaceToFpView.mFaceIconController)
                 .updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING));
 
         mFaceToFpView.updateState(AuthBiometricFaceToFingerprintView.STATE_ERROR);
+
+        verify(mFaceToFpView.mFaceIconController).deactivate();
+        verify(mFaceToFpView.mUdfpsIconController).updateState(
+                eq(AuthBiometricFaceToFingerprintView.STATE_IDLE));
+        verify(mConfirmButton).setVisibility(eq(View.GONE));
+
         mFaceToFpView.updateState(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING);
 
-        InOrder order = inOrder(mFaceToFpView.mIconController);
-        order.verify(mFaceToFpView.mIconController).updateState(
-                eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING),
-                eq(AuthBiometricFaceToFingerprintView.STATE_ERROR));
-        order.verify(mFaceToFpView.mIconController).updateState(
-                eq(AuthBiometricFaceToFingerprintView.STATE_ERROR),
+        verify(mFaceToFpView.mUdfpsIconController).updateState(
                 eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING));
-
-        verify(mConfirmButton).setVisibility(eq(View.GONE));
     }
 
     @Test
@@ -163,6 +166,7 @@
     }
 
     @Test
+    @Ignore("flaky, b/189031816")
     public void testModeUpdated_onSoftError_whenSwitchToFingerprint() {
         mFaceToFpView.onDialogAnimatedIn();
         mFaceToFpView.onAuthenticationFailed(TYPE_FACE, "no face");
@@ -172,10 +176,14 @@
                 eq(mContext.getString(R.string.fingerprint_dialog_use_fingerprint_instead)));
         verify(mCallback).onAction(
                 eq(AuthBiometricView.Callback.ACTION_START_DELAYED_FINGERPRINT_SENSOR));
-        assertEquals(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING, mFaceToFpView.mState);
+
+        // First we enter the error state, since we need to show the error animation/text. The
+        // error state is later cleared based on a timer, and we enter STATE_AUTHENTICATING.
+        assertEquals(AuthBiometricFaceToFingerprintView.STATE_ERROR, mFaceToFpView.mState);
     }
 
     @Test
+    @Ignore("flaky, b/189031816")
     public void testModeUpdated_onHardError_whenSwitchToFingerprint() {
         mFaceToFpView.onDialogAnimatedIn();
         mFaceToFpView.onError(TYPE_FACE, "oh no!");
@@ -185,13 +193,16 @@
                 eq(mContext.getString(R.string.fingerprint_dialog_use_fingerprint_instead)));
         verify(mCallback).onAction(
                 eq(AuthBiometricView.Callback.ACTION_START_DELAYED_FINGERPRINT_SENSOR));
-        assertEquals(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING, mFaceToFpView.mState);
+
+        // First we enter the error state, since we need to show the error animation/text. The
+        // error state is later cleared based on a timer, and we enter STATE_AUTHENTICATING.
+        assertEquals(AuthBiometricFaceToFingerprintView.STATE_ERROR, mFaceToFpView.mState);
     }
 
     @Test
     public void testFingerprintOnlyStartsOnFirstError() {
         mFaceToFpView.onDialogAnimatedIn();
-        verify(mFaceToFpView.mIconController)
+        verify(mFaceToFpView.mFaceIconController)
                 .updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING));
 
         mFaceToFpView.onDialogAnimatedIn();
@@ -260,11 +271,6 @@
         protected int getDelayAfterAuthenticatedDurationMs() {
             return 0;
         }
-
-        @Override
-        protected IconController createUdfpsIconController() {
-            return AuthBiometricFaceToFingerprintViewTest.this.mIconController;
-        }
     }
 
     private class MockInjector extends AuthBiometricView.Injector {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceViewTest.java
index 043bd5c..b93381d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceViewTest.java
@@ -62,7 +62,7 @@
     public void setup() {
         MockitoAnnotations.initMocks(this);
         mFaceView = new TestableFaceView(mContext);
-        mFaceView.mIconController = mock(TestableFaceView.TestableIconController.class);
+        mFaceView.mFaceIconController = mock(TestableFaceView.TestableIconController.class);
         mFaceView.setCallback(mCallback);
 
         mFaceView.mNegativeButton = mNegativeButton;
@@ -78,18 +78,18 @@
     @Test
     public void testStateUpdated_whenDialogAnimatedIn() {
         mFaceView.onDialogAnimatedIn();
-        verify(mFaceView.mIconController)
+        verify(mFaceView.mFaceIconController)
                 .updateState(anyInt(), eq(AuthBiometricFaceView.STATE_AUTHENTICATING));
     }
 
     @Test
     public void testIconUpdatesState_whenDialogStateUpdated() {
         mFaceView.updateState(AuthBiometricFaceView.STATE_AUTHENTICATING);
-        verify(mFaceView.mIconController)
+        verify(mFaceView.mFaceIconController)
                 .updateState(anyInt(), eq(AuthBiometricFaceView.STATE_AUTHENTICATING));
 
         mFaceView.updateState(AuthBiometricFaceView.STATE_AUTHENTICATED);
-        verify(mFaceView.mIconController).updateState(
+        verify(mFaceView.mFaceIconController).updateState(
                 eq(AuthBiometricFaceView.STATE_AUTHENTICATING),
                 eq(AuthBiometricFaceView.STATE_AUTHENTICATED));
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
index bd518ff..f8e38e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
@@ -45,6 +45,7 @@
 import com.android.systemui.SysuiTestCase;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -199,6 +200,7 @@
     }
 
     @Test
+    @Ignore("flaky, b/189031816")
     public void testError_sendsActionError() {
         initDialog(mContext, false /* allowDeviceCredential */, mCallback, new MockInjector());
         final String testError = "testError";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index f53dddb..f5c6f98 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.biometrics;
 
+import static android.media.AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY;
+
 import static junit.framework.Assert.assertEquals;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -24,6 +26,7 @@
 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;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -62,11 +65,13 @@
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.concurrency.Execution;
 import com.android.systemui.util.concurrency.FakeExecution;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.util.time.SystemClock;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -143,6 +148,10 @@
     private DisplayManager mDisplayManager;
     @Mock
     private Handler mHandler;
+    @Mock
+    private ConfigurationController mConfigurationController;
+    @Mock
+    private SystemClock mSystemClock;
 
     private FakeExecutor mFgExecutor;
 
@@ -150,6 +159,10 @@
     @Mock
     private UdfpsView mUdfpsView;
     @Mock
+    private UdfpsEnrollView mEnrollView;
+    @Mock
+    private UdfpsKeyguardView mKeyguardView;
+    @Mock
     private UdfpsKeyguardViewController mUdfpsKeyguardViewController;
     @Mock
     private TypedArray mBrightnessValues;
@@ -171,7 +184,13 @@
         setUpResources();
         mExecution = new FakeExecution();
 
-        when(mLayoutInflater.inflate(R.layout.udfps_view, null, false)).thenReturn(mUdfpsView);
+        when(mLayoutInflater.inflate(R.layout.udfps_view, null, false))
+                .thenReturn(mUdfpsView);
+        when(mLayoutInflater.inflate(R.layout.udfps_enroll_view, null))
+                .thenReturn(mEnrollView); // for showOverlay REASON_ENROLL_ENROLLING
+        when(mLayoutInflater.inflate(R.layout.udfps_keyguard_view, null))
+                .thenReturn(mKeyguardView); // for showOverlay REASON_AUTH_FPM_KEYGUARD
+        when(mEnrollView.getContext()).thenReturn(mContext);
         final List<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
 
         final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
@@ -214,7 +233,9 @@
                 mKeyguardStateController,
                 mKeyguardBypassController,
                 mDisplayManager,
-                mHandler);
+                mHandler,
+                mConfigurationController,
+                mSystemClock);
         verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
         mOverlayController = mOverlayCaptor.getValue();
         verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
@@ -264,6 +285,75 @@
     }
 
     @Test
+    public void onActionMove_dozing_setDeviceEntryIntent() throws RemoteException {
+        // GIVEN the current animation is UdfpsKeyguardViewController and device IS dozing
+        when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false);
+        when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
+        when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController);
+        when(mStatusBarStateController.isDozing()).thenReturn(true);
+
+        // GIVEN that the overlay is showing
+        mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
+                IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+        mFgExecutor.runAllReady();
+
+        // WHEN ACTION_DOWN is received
+        verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+        MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
+        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
+        moveEvent.recycle();
+
+        // THEN device entry intent is never to true b/c device was dozing on touch
+        verify(mKeyguardBypassController, never()).setUserHasDeviceEntryIntent(true);
+    }
+
+    @Test
+    public void onActionMove_onKeyguard_setDeviceEntryIntent() throws RemoteException {
+        // GIVEN the current animation is UdfpsKeyguardViewController and device isn't dozing
+        when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false);
+        when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
+        when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController);
+        when(mStatusBarStateController.isDozing()).thenReturn(false);
+
+        // GIVEN that the overlay is showing
+        mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
+                IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+        mFgExecutor.runAllReady();
+
+        // WHEN ACTION_DOWN is received
+        verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+        MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
+        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
+        moveEvent.recycle();
+
+        // THEN device entry intent is set to true
+        verify(mKeyguardBypassController).setUserHasDeviceEntryIntent(true);
+    }
+
+    @Test
+    public void onActionMove_onEnrollment_neverSetDeviceEntryIntent() throws RemoteException {
+        // GIVEN the current animation is UdfpsEnrollViewController
+        when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false);
+        when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
+        when(mUdfpsView.getAnimationViewController()).thenReturn(
+                mock(UdfpsEnrollViewController.class));
+
+        // GIVEN that the overlay is showing
+        mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
+                IUdfpsOverlayController.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
+        mFgExecutor.runAllReady();
+
+        // WHEN ACTION_DOWN is received
+        verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+        MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
+        mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
+        moveEvent.recycle();
+
+        // THEN device entry intent is never set
+        verify(mKeyguardBypassController, never()).setUserHasDeviceEntryIntent(anyBoolean());
+    }
+
+    @Test
     public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice() throws RemoteException {
         // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController
         when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
@@ -329,6 +419,21 @@
     }
 
     @Test
+    public void hideUdfpsOverlay_resetsAltAuthBouncerWhenShowing() throws RemoteException {
+        // GIVEN overlay was showing and the udfps bouncer is showing
+        mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
+                IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+        when(mStatusBarKeyguardViewManager.isShowingAlternateAuth()).thenReturn(true);
+
+        // WHEN the overlay is hidden
+        mOverlayController.hideUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
+        mFgExecutor.runAllReady();
+
+        // THEN the udfps bouncer is reset
+        verify(mStatusBarKeyguardViewManager).resetAlternateAuth(eq(true));
+    }
+
+    @Test
     public void testSubscribesToOrientationChangesWhenShowingOverlay() throws Exception {
         mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
                 IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
@@ -460,5 +565,10 @@
                 eq(mUdfpsController.EFFECT_CLICK),
                 eq("udfps-onStart"),
                 eq(UdfpsController.VIBRATION_SONIFICATION_ATTRIBUTES));
+
+        // THEN make sure vibration attributes has so that it always will play the haptic,
+        // even in battery saver mode
+        assertEquals(USAGE_ASSISTANCE_ACCESSIBILITY,
+                UdfpsController.VIBRATION_SONIFICATION_ATTRIBUTES.getUsage());
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index 57c57ec..decec63 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -18,8 +18,13 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -30,7 +35,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -39,7 +43,10 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -50,6 +57,7 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.Optional;
+import java.util.List;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -75,9 +83,14 @@
     @Mock
     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Mock
+    private KeyguardStateController mKeyguardStateController;
+    @Mock
     private KeyguardViewMediator mKeyguardViewMediator;
     @Mock
+    private ConfigurationController mConfigurationController;
+    @Mock
     private UdfpsController mUdfpsController;
+    private FakeSystemClock mSystemClock = new FakeSystemClock();
 
     private UdfpsKeyguardViewController mController;
 
@@ -86,14 +99,15 @@
     private StatusBarStateController.StateListener mStatusBarStateListener;
 
     @Captor private ArgumentCaptor<StatusBar.ExpansionChangedListener> mExpansionListenerCaptor;
-    private StatusBar.ExpansionChangedListener mExpansionListener;
+    private List<StatusBar.ExpansionChangedListener> mExpansionListeners;
 
     @Captor private ArgumentCaptor<StatusBarKeyguardViewManager.AlternateAuthInterceptor>
             mAltAuthInterceptorCaptor;
     private StatusBarKeyguardViewManager.AlternateAuthInterceptor mAltAuthInterceptor;
 
-    @Captor private ArgumentCaptor<KeyguardUpdateMonitorCallback> mUpdateMonitorCallbackCaptor;
-    private KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback;
+    @Captor private ArgumentCaptor<KeyguardStateController.Callback>
+            mKeyguardStateControllerCallbackCaptor;
+    private KeyguardStateController.Callback mKeyguardStateControllerCallback;
 
     @Before
     public void setUp() {
@@ -111,13 +125,16 @@
                 mDumpManager,
                 mKeyguardViewMediator,
                 mLockscreenShadeTransitionController,
+                mConfigurationController,
+                mSystemClock,
+                mKeyguardStateController,
                 mUdfpsController);
     }
 
     @Test
     public void testRegistersExpansionChangedListenerOnAttached() {
         mController.onViewAttached();
-        captureExpansionListener();
+        captureExpansionListeners();
     }
 
     @Test
@@ -146,11 +163,15 @@
     public void testListenersUnregisteredOnDetached() {
         mController.onViewAttached();
         captureStatusBarStateListeners();
-        captureExpansionListener();
+        captureExpansionListeners();
+        captureKeyguardStateControllerCallback();
         mController.onViewDetached();
 
         verify(mStatusBarStateController).removeCallback(mStatusBarStateListener);
-        verify(mStatusBar).removeExpansionChangedListener(mExpansionListener);
+        for (StatusBar.ExpansionChangedListener listener : mExpansionListeners) {
+            verify(mStatusBar).removeExpansionChangedListener(listener);
+        }
+        verify(mKeyguardStateController).removeCallback(mKeyguardStateControllerCallback);
     }
 
     @Test
@@ -169,7 +190,6 @@
     public void testShouldPauseAuthBouncerShowing() {
         mController.onViewAttached();
         captureStatusBarStateListeners();
-        captureExpansionListener();
 
         sendStatusBarStateChanged(StatusBarState.KEYGUARD);
 
@@ -180,7 +200,6 @@
     public void testShouldNotPauseAuthOnKeyguard() {
         mController.onViewAttached();
         captureStatusBarStateListeners();
-        captureExpansionListener();
 
         sendStatusBarStateChanged(StatusBarState.KEYGUARD);
 
@@ -188,10 +207,25 @@
     }
 
     @Test
+    public void testShouldPauseAuthIsLaunchTransitionFadingAway() {
+        // GIVEN view is attached and we're on the keyguard (see testShouldNotPauseAuthOnKeyguard)
+        mController.onViewAttached();
+        captureStatusBarStateListeners();
+        sendStatusBarStateChanged(StatusBarState.KEYGUARD);
+
+        // WHEN isLaunchTransitionFadingAway=true
+        captureKeyguardStateControllerCallback();
+        when(mKeyguardStateController.isLaunchTransitionFadingAway()).thenReturn(true);
+        mKeyguardStateControllerCallback.onLaunchTransitionFadingAwayChanged();
+
+        // THEN pause auth
+        assertTrue(mController.shouldPauseAuth());
+    }
+
+    @Test
     public void testShouldPauseAuthOnShadeLocked() {
         mController.onViewAttached();
         captureStatusBarStateListeners();
-        captureExpansionListener();
 
         sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED);
 
@@ -202,7 +236,6 @@
     public void testShouldPauseAuthOnShade() {
         mController.onViewAttached();
         captureStatusBarStateListeners();
-        captureExpansionListener();
 
         // WHEN not on keyguard yet (shade = home)
         sendStatusBarStateChanged(StatusBarState.SHADE);
@@ -215,7 +248,6 @@
     public void testShouldPauseAuthAnimatingScreenOffFromShade() {
         mController.onViewAttached();
         captureStatusBarStateListeners();
-        captureExpansionListener();
 
         // WHEN transitioning from home/shade => keyguard + animating screen off
         mStatusBarStateListener.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD);
@@ -229,7 +261,6 @@
     public void testDoNotPauseAuthAnimatingScreenOffFromLS() {
         mController.onViewAttached();
         captureStatusBarStateListeners();
-        captureExpansionListener();
 
         // WHEN animating screen off transition from LS => AOD
         sendStatusBarStateChanged(StatusBarState.KEYGUARD);
@@ -270,6 +301,74 @@
         verify(mStatusBarKeyguardViewManager).removeAlternateAuthInterceptor(mAltAuthInterceptor);
     }
 
+    @Test
+    public void testHiddenUdfpsBouncerOnTouchOutside_nothingHappens() {
+        // GIVEN view is attached
+        mController.onViewAttached();
+        captureAltAuthInterceptor();
+
+        // GIVEN udfps bouncer isn't showing
+        mAltAuthInterceptor.hideAlternateAuthBouncer();
+
+        // WHEN touch is observed outside the view
+        mController.onTouchOutsideView();
+
+        // THEN bouncer / alt auth methods are never called
+        verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
+        verify(mStatusBarKeyguardViewManager, never()).resetAlternateAuth(anyBoolean());
+    }
+
+    @Test
+    public void testShowingUdfpsBouncerOnTouchOutsideWithinThreshold_nothingHappens() {
+        // GIVEN view is attached
+        mController.onViewAttached();
+        captureAltAuthInterceptor();
+
+        // GIVEN udfps bouncer is showing
+        mAltAuthInterceptor.showAlternateAuthBouncer();
+
+        // WHEN touch is observed outside the view 200ms later (just within threshold)
+        mSystemClock.advanceTime(200);
+        mController.onTouchOutsideView();
+
+        // THEN bouncer / alt auth methods are never called because not enough time has passed
+        verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
+        verify(mStatusBarKeyguardViewManager, never()).resetAlternateAuth(anyBoolean());
+    }
+
+    @Test
+    public void testShowingUdfpsBouncerOnTouchOutsideAboveThreshold_showInputBouncer() {
+        // GIVEN view is attached
+        mController.onViewAttached();
+        captureAltAuthInterceptor();
+
+        // GIVEN udfps bouncer is showing
+        mAltAuthInterceptor.showAlternateAuthBouncer();
+
+        // WHEN touch is observed outside the view 205ms later
+        mSystemClock.advanceTime(205);
+        mController.onTouchOutsideView();
+
+        // THEN show the bouncer and reset alt auth
+        verify(mStatusBarKeyguardViewManager).showBouncer(eq(true));
+        verify(mStatusBarKeyguardViewManager).resetAlternateAuth(anyBoolean());
+    }
+
+    @Test
+    public void testFadeInWithStatusBarExpansion() {
+        // GIVEN view is attached
+        mController.onViewAttached();
+        captureExpansionListeners();
+        captureKeyguardStateControllerCallback();
+        reset(mView);
+
+        // WHEN status bar expansion is 0
+        updateStatusBarExpansion(0, true);
+
+        // THEN alpha is 0
+        verify(mView).setUnpausedAlpha(0);
+    }
+
     private void sendStatusBarStateChanged(int statusBarState) {
         mStatusBarStateListener.onStateChanged(statusBarState);
     }
@@ -279,9 +378,18 @@
         mStatusBarStateListener = mStateListenerCaptor.getValue();
     }
 
-    private void captureExpansionListener() {
-        verify(mStatusBar).addExpansionChangedListener(mExpansionListenerCaptor.capture());
-        mExpansionListener = mExpansionListenerCaptor.getValue();
+    private void captureExpansionListeners() {
+        verify(mStatusBar, times(2))
+                .addExpansionChangedListener(mExpansionListenerCaptor.capture());
+        // first (index=0) is from super class, UdfpsAnimationViewController.
+        // second (index=1) is from UdfpsKeyguardViewController
+        mExpansionListeners = mExpansionListenerCaptor.getAllValues();
+    }
+
+    private void updateStatusBarExpansion(float expansion, boolean expanded) {
+        for (StatusBar.ExpansionChangedListener listener : mExpansionListeners) {
+            listener.onExpansionChanged(expansion, expanded);
+        }
     }
 
     private void captureAltAuthInterceptor() {
@@ -290,8 +398,11 @@
         mAltAuthInterceptor = mAltAuthInterceptorCaptor.getValue();
     }
 
-    private void captureKeyguardUpdateMonitorCallback() {
-        verify(mKeyguardUpdateMonitor).registerCallback(mUpdateMonitorCallbackCaptor.capture());
-        mKeyguardUpdateMonitorCallback = mUpdateMonitorCallbackCaptor.getValue();
+
+
+    private void captureKeyguardStateControllerCallback() {
+        verify(mKeyguardStateController).addCallback(
+                mKeyguardStateControllerCallbackCaptor.capture());
+        mKeyguardStateControllerCallback = mKeyguardStateControllerCallbackCaptor.getValue();
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
index d6226aa..a32cb9b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
@@ -43,6 +43,7 @@
         when(params.singleTapUsesProx()).thenReturn(true);
         when(params.longPressUsesProx()).thenReturn(true);
         when(params.getQuickPickupAodDuration()).thenReturn(500);
+        when(params.brightnessUsesProx()).thenReturn(true);
 
         doneHolder[0] = true;
         return params;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index 4e8b59c..deb7d31 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -29,6 +29,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
@@ -47,6 +48,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dock.DockManager;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.util.concurrency.FakeExecutor;
@@ -82,6 +84,8 @@
     WakefulnessLifecycle mWakefulnessLifecycle;
     @Mock
     DozeParameters mDozeParameters;
+    @Mock
+    DockManager mDockManager;
     private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
     private FakeThreadFactory mFakeThreadFactory = new FakeThreadFactory(mFakeExecutor);
 
@@ -109,7 +113,7 @@
         mSensor = fakeSensorManager.getFakeLightSensor();
         mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
                 Optional.of(mSensor.getSensor()), mDozeHost, null /* handler */,
-                mAlwaysOnDisplayPolicy, mWakefulnessLifecycle, mDozeParameters);
+                mAlwaysOnDisplayPolicy, mWakefulnessLifecycle, mDozeParameters, mDockManager);
 
         mScreen.onScreenState(Display.STATE_ON);
     }
@@ -157,6 +161,67 @@
     }
 
     @Test
+    public void testAodDocked_doNotSelectivelyUseProx_usesLightSensor() {
+        // GIVEN the device doesn't need to selectively register for prox sensors and
+        // brightness sensor uses prox
+        when(mDozeParameters.getSelectivelyRegisterSensorsUsingProx()).thenReturn(false);
+        when(mDozeParameters.brightnessUsesProx()).thenReturn(true);
+
+        // GIVEN the device is docked and the display state changes to ON
+        when(mDockManager.isDocked()).thenReturn(true);
+        mScreen.onScreenState(Display.STATE_ON);
+        waitForSensorManager();
+
+        // WHEN new sensor event sent
+        mSensor.sendSensorEvent(3);
+
+        // THEN brightness is updated
+        assertEquals(3, mServiceFake.screenBrightness);
+    }
+
+    @Test
+    public void testAodDocked_brightnessDoesNotUseProx_usesLightSensor() {
+        // GIVEN the device doesn't need to selectively register for prox sensors but
+        // the brightness sensor doesn't use prox
+        when(mDozeParameters.getSelectivelyRegisterSensorsUsingProx()).thenReturn(true);
+        when(mDozeParameters.brightnessUsesProx()).thenReturn(false);
+
+        // GIVEN the device is docked and the display state changes to ON
+        when(mDockManager.isDocked()).thenReturn(true);
+        mScreen.onScreenState(Display.STATE_ON);
+        waitForSensorManager();
+
+        // WHEN new sensor event sent
+        mSensor.sendSensorEvent(3);
+
+        // THEN brightness is updated
+        assertEquals(3, mServiceFake.screenBrightness);
+    }
+
+
+    @Test
+    public void testAodDocked_noProx_brightnessUsesProx_doNotUseLightSensor() {
+        final int startBrightness = mServiceFake.screenBrightness;
+
+        // GIVEN the device needs to selectively register for prox sensors and
+        // the brightness sensor uses prox
+        when(mDozeParameters.getSelectivelyRegisterSensorsUsingProx()).thenReturn(true);
+        when(mDozeParameters.brightnessUsesProx()).thenReturn(true);
+
+        // GIVEN the device is docked and the display state is on
+        when(mDockManager.isDocked()).thenReturn(true);
+        mScreen.onScreenState(Display.STATE_ON);
+        waitForSensorManager();
+
+        // WHEN new sensor event sent
+        mSensor.sendSensorEvent(3);
+
+        // THEN brightness is NOT changed
+        assertNotSame(3, mServiceFake.screenBrightness);
+        assertEquals(startBrightness, mServiceFake.screenBrightness);
+    }
+
+    @Test
     public void testPausingAod_doesNotResetBrightness() throws Exception {
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE_AOD);
@@ -175,7 +240,7 @@
     public void testPulsing_withoutLightSensor_setsAoDDimmingScrimTransparent() throws Exception {
         mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
                 Optional.empty() /* sensor */, mDozeHost, null /* handler */,
-                mAlwaysOnDisplayPolicy, mWakefulnessLifecycle, mDozeParameters);
+                mAlwaysOnDisplayPolicy, mWakefulnessLifecycle, mDozeParameters, mDockManager);
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE);
         reset(mDozeHost);
@@ -216,7 +281,7 @@
     public void testNullSensor() throws Exception {
         mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
                 Optional.empty() /* sensor */, mDozeHost, null /* handler */,
-                mAlwaysOnDisplayPolicy, mWakefulnessLifecycle, mDozeParameters);
+                mAlwaysOnDisplayPolicy, mWakefulnessLifecycle, mDozeParameters, mDockManager);
 
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE_AOD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
index 0c94f09..5c4c27c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -88,7 +88,7 @@
     private FakeSettings mFakeSettings = new FakeSettings();
     private SensorManagerPlugin.SensorEventListener mWakeLockScreenListener;
     private TestableLooper mTestableLooper;
-    private DozeSensors mDozeSensors;
+    private TestableDozeSensors mDozeSensors;
     private TriggerSensor mSensorTap;
 
     @Before
@@ -170,6 +170,94 @@
         assertTrue(mSensorTap.mRequested);
     }
 
+    @Test
+    public void testDozeSensorSetListening() {
+        // GIVEN doze sensors enabled
+        when(mAmbientDisplayConfiguration.enabled(anyInt())).thenReturn(true);
+
+        // GIVEN a trigger sensor
+        Sensor mockSensor = mock(Sensor.class);
+        TriggerSensor triggerSensor = mDozeSensors.createDozeSensor(
+                mockSensor,
+                /* settingEnabled */ true,
+                /* requiresTouchScreen */ true);
+        when(mSensorManager.requestTriggerSensor(eq(triggerSensor), eq(mockSensor)))
+                .thenReturn(true);
+
+        // WHEN we want to listen for the trigger sensor
+        triggerSensor.setListening(true);
+
+        // THEN the sensor is registered
+        assertTrue(triggerSensor.mRegistered);
+    }
+
+    @Test
+    public void testDozeSensorSettingDisabled() {
+        // GIVEN doze sensors enabled
+        when(mAmbientDisplayConfiguration.enabled(anyInt())).thenReturn(true);
+
+        // GIVEN a trigger sensor
+        Sensor mockSensor = mock(Sensor.class);
+        TriggerSensor triggerSensor = mDozeSensors.createDozeSensor(
+                mockSensor,
+                /* settingEnabled*/ false,
+                /* requiresTouchScreen */ true);
+        when(mSensorManager.requestTriggerSensor(eq(triggerSensor), eq(mockSensor)))
+                .thenReturn(true);
+
+        // WHEN setListening is called
+        triggerSensor.setListening(true);
+
+        // THEN the sensor is not registered
+        assertFalse(triggerSensor.mRegistered);
+    }
+
+    @Test
+    public void testDozeSensorIgnoreSetting() {
+        // GIVEN doze sensors enabled
+        when(mAmbientDisplayConfiguration.enabled(anyInt())).thenReturn(true);
+
+        // GIVEN a trigger sensor that's
+        Sensor mockSensor = mock(Sensor.class);
+        TriggerSensor triggerSensor = mDozeSensors.createDozeSensor(
+                mockSensor,
+                /* settingEnabled*/ false,
+                /* requiresTouchScreen */ true);
+        when(mSensorManager.requestTriggerSensor(eq(triggerSensor), eq(mockSensor)))
+                .thenReturn(true);
+
+        // GIVEN sensor is listening
+        triggerSensor.setListening(true);
+
+        // WHEN ignoreSetting is called
+        triggerSensor.ignoreSetting(true);
+
+        // THEN the sensor is registered
+        assertTrue(triggerSensor.mRegistered);
+    }
+
+    @Test
+    public void testUpdateListeningAfterAlreadyRegistered() {
+        // GIVEN doze sensors enabled
+        when(mAmbientDisplayConfiguration.enabled(anyInt())).thenReturn(true);
+
+        // GIVEN a trigger sensor
+        Sensor mockSensor = mock(Sensor.class);
+        TriggerSensor triggerSensor = mDozeSensors.createDozeSensor(
+                mockSensor,
+                /* settingEnabled*/ true,
+                /* requiresTouchScreen */ true);
+        when(mSensorManager.requestTriggerSensor(eq(triggerSensor), eq(mockSensor)))
+                .thenReturn(true);
+
+        // WHEN setListening is called AND updateListening is called
+        triggerSensor.setListening(true);
+        triggerSensor.updateListening();
+
+        // THEN the sensor is still registered
+        assertTrue(triggerSensor.mRegistered);
+    }
+
     private class TestableDozeSensors extends DozeSensors {
 
         TestableDozeSensors() {
@@ -187,5 +275,17 @@
             }
             mSensors = new TriggerSensor[] {mTriggerSensor, mSensorTap};
         }
+
+        public TriggerSensor createDozeSensor(Sensor sensor, boolean settingEnabled,
+                boolean requiresTouchScreen) {
+            return new TriggerSensor(/* sensor */ sensor,
+                    /* setting name */ "test_setting",
+                    /* settingDefault */ settingEnabled,
+                    /* configured */ true,
+                    /* pulseReason*/ 0,
+                    /* reportsTouchCoordinate*/ false,
+                    requiresTouchScreen,
+                    mDozeLog);
+        }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java
index 223714c..7bc5f86 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagReaderTest.java
@@ -32,6 +32,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.util.wrapper.BuildInfo;
 
 import org.junit.Before;
@@ -43,6 +44,7 @@
 public class FeatureFlagReaderTest extends SysuiTestCase {
     @Mock private Resources mResources;
     @Mock private BuildInfo mBuildInfo;
+    @Mock private PluginManager mPluginManager;
     @Mock private SystemPropertiesHelper mSystemPropertiesHelper;
 
     private FeatureFlagReader mReader;
@@ -63,7 +65,8 @@
     private void initialize(boolean isDebuggable, boolean isOverrideable) {
         when(mBuildInfo.isDebuggable()).thenReturn(isDebuggable);
         when(mResources.getBoolean(R.bool.are_flags_overrideable)).thenReturn(isOverrideable);
-        mReader = new FeatureFlagReader(mResources, mBuildInfo, mSystemPropertiesHelper);
+        mReader = new FeatureFlagReader(
+                mResources, mBuildInfo, mPluginManager, mSystemPropertiesHelper);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsTest.java
new file mode 100644
index 0000000..1a96178
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.flags;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.FlagReaderPlugin;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+public class FeatureFlagsTest extends SysuiTestCase {
+
+    @Mock FeatureFlagReader mFeatureFlagReader;
+
+    private FeatureFlags mFeatureFlags;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+
+        mFeatureFlags = new FeatureFlags(mFeatureFlagReader, getContext());
+    }
+
+    @Test
+    public void testAddListener() {
+        Flag<?> flag = new BooleanFlag(1);
+        mFeatureFlags.addFlag(flag);
+
+        // Assert and capture that a plugin listener was added.
+        ArgumentCaptor<FlagReaderPlugin.Listener> pluginListenerCaptor =
+                ArgumentCaptor.forClass(FlagReaderPlugin.Listener.class);
+
+        verify(mFeatureFlagReader).addListener(pluginListenerCaptor.capture());
+        FlagReaderPlugin.Listener pluginListener = pluginListenerCaptor.getValue();
+
+        // Signal a change. No listeners, so no real effect.
+        pluginListener.onFlagChanged(flag.getId());
+
+        // Add a listener for the flag
+        final Flag<?>[] changedFlag = {null};
+        FeatureFlags.Listener listener = f -> changedFlag[0] = f;
+        mFeatureFlags.addFlagListener(flag, listener);
+
+        // No changes seen yet.
+        assertThat(changedFlag[0]).isNull();
+
+        // Signal a change.
+        pluginListener.onFlagChanged(flag.getId());
+
+        // Assert that the change was for the correct flag.
+        assertThat(changedFlag[0]).isEqualTo(flag);
+    }
+
+    @Test
+    public void testRemoveListener() {
+        Flag<?> flag = new BooleanFlag(1);
+        mFeatureFlags.addFlag(flag);
+
+        // Assert and capture that a plugin listener was added.
+        ArgumentCaptor<FlagReaderPlugin.Listener> pluginListenerCaptor =
+                ArgumentCaptor.forClass(FlagReaderPlugin.Listener.class);
+
+        verify(mFeatureFlagReader).addListener(pluginListenerCaptor.capture());
+        FlagReaderPlugin.Listener pluginListener = pluginListenerCaptor.getValue();
+
+        // Add a listener for the flag
+        final Flag<?>[] changedFlag = {null};
+        FeatureFlags.Listener listener = f -> changedFlag[0] = f;
+        mFeatureFlags.addFlagListener(flag, listener);
+
+        // Signal a change.
+        pluginListener.onFlagChanged(flag.getId());
+
+        // Assert that the change was for the correct flag.
+        assertThat(changedFlag[0]).isEqualTo(flag);
+
+        changedFlag[0] = null;
+
+        // Now remove the listener.
+        mFeatureFlags.removeFlagListener(flag, listener);
+        // Signal a change.
+        pluginListener.onFlagChanged(flag.getId());
+        // Assert that the change was not triggered
+        assertThat(changedFlag[0]).isNull();
+
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java
new file mode 100644
index 0000000..25c3028
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagsTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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.flags;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.util.Pair;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@SmallTest
+public class FlagsTest extends SysuiTestCase {
+
+    @Test
+    public void testDuplicateFlagIdCheckWorks() {
+        List<Pair<String, Flag<?>>> flags = collectFlags(DuplicateFlagContainer.class);
+        Map<Integer, List<String>> duplicates = groupDuplicateFlags(flags);
+
+        assertWithMessage(generateAssertionMessage(duplicates))
+                .that(duplicates.size()).isEqualTo(2);
+    }
+
+    @Test
+    public void testNoDuplicateFlagIds() {
+        List<Pair<String, Flag<?>>> flags = collectFlags(Flags.class);
+        Map<Integer, List<String>> duplicates = groupDuplicateFlags(flags);
+
+        assertWithMessage(generateAssertionMessage(duplicates))
+                .that(duplicates.size()).isEqualTo(0);
+    }
+
+    private String generateAssertionMessage(Map<Integer, List<String>> duplicates) {
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append("Duplicate flag keys found: {");
+        for (int id : duplicates.keySet()) {
+            stringBuilder
+                    .append(" ")
+                    .append(id)
+                    .append(": [")
+                    .append(String.join(", ", duplicates.get(id)))
+                    .append("]");
+        }
+        stringBuilder.append(" }");
+
+        return stringBuilder.toString();
+    }
+
+    private List<Pair<String, Flag<?>>> collectFlags(Class<?> clz) {
+        List<Pair<String, Flag<?>>> flags = new ArrayList<>();
+
+        Field[] fields = clz.getFields();
+
+        for (Field field : fields) {
+            Class<?> t = field.getType();
+            if (Flag.class.isAssignableFrom(t)) {
+                try {
+                    flags.add(Pair.create(field.getName(), (Flag<?>) field.get(null)));
+                } catch (IllegalAccessException e) {
+                    // no-op
+                }
+            }
+        }
+
+        return flags;
+    }
+
+    private Map<Integer, List<String>> groupDuplicateFlags(List<Pair<String, Flag<?>>> flags) {
+        Map<Integer, List<String>> grouping = new HashMap<>();
+
+        for (Pair<String, Flag<?>> flag : flags) {
+            grouping.putIfAbsent(flag.second.getId(), new ArrayList<>());
+            grouping.get(flag.second.getId()).add(flag.first);
+        }
+
+        Map<Integer, List<String>> result = new HashMap<>();
+        for (Integer id : grouping.keySet()) {
+            if (grouping.get(id).size() > 1) {
+                result.put(id, grouping.get(id));
+            }
+        }
+
+        return result;
+    }
+
+    private static class DuplicateFlagContainer {
+        public static final BooleanFlag A_FLAG = new BooleanFlag(0);
+        public static final BooleanFlag B_FLAG = new BooleanFlag(0);
+        public static final StringFlag C_FLAG = new StringFlag(0);
+
+        public static final BooleanFlag D_FLAG = new BooleanFlag(1);
+
+        public static final DoubleFlag E_FLAG = new DoubleFlag(3);
+        public static final DoubleFlag F_FLAG = new DoubleFlag(3);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index 8c0d21e..0e344a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
@@ -51,6 +52,7 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
@@ -112,6 +114,7 @@
     @Mock private Handler mHandler;
     @Mock private UserContextProvider mUserContextProvider;
     @Mock private StatusBar mStatusBar;
+    @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
 
     private TestableLooper mTestableLooper;
 
@@ -155,7 +158,8 @@
                 mSysUiState,
                 mHandler,
                 mPackageManager,
-                Optional.of(mStatusBar)
+                Optional.of(mStatusBar),
+                mKeyguardUpdateMonitor
         );
         mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting();
 
@@ -421,4 +425,31 @@
         restartAction.onLongPress();
         verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_REBOOT_LONG_PRESS);
     }
+
+    @Test
+    public void testOnLockScreen_disableSmartLock() {
+        mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
+        int user = KeyguardUpdateMonitor.getCurrentUser();
+        doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
+        doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
+        doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
+        doReturn(false).when(mStatusBar).isKeyguardShowing();
+        String[] actions = {
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY,
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN,
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_POWER,
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_RESTART,
+        };
+        doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
+
+        // When entering power menu from lockscreen, with smart lock enabled
+        when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
+        mGlobalActionsDialogLite.showOrHideDialog(true, true);
+
+        // Then smart lock will be disabled
+        verify(mLockPatternUtils).requireCredentialEntry(eq(user));
+
+        // hide dialog again
+        mGlobalActionsDialogLite.showOrHideDialog(true, true);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
index 6d1db37..f8ab42f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
@@ -56,6 +56,7 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
@@ -127,6 +128,7 @@
     @Mock private PackageManager mPackageManager;
     @Mock private SecureSettings mSecureSettings;
     @Mock private StatusBar mStatusBar;
+    @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
 
     private TestableLooper mTestableLooper;
 
@@ -170,7 +172,8 @@
                 mSysUiState,
                 mHandler,
                 mPackageManager,
-                Optional.of(mStatusBar)
+                Optional.of(mStatusBar),
+                mKeyguardUpdateMonitor
         );
         mGlobalActionsDialog.setZeroDialogPressDelayForTesting();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 1dacc62..31d70f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -23,6 +23,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -33,8 +34,9 @@
 import android.app.trust.TrustManager;
 import android.os.PowerManager;
 import android.os.PowerManager.WakeLock;
+import android.telephony.TelephonyManager;
 import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper.RunWithLooper;
+import android.testing.TestableLooper;
 
 import androidx.test.filters.SmallTest;
 
@@ -65,7 +67,7 @@
 import org.mockito.MockitoAnnotations;
 
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper
+@TestableLooper.RunWithLooper
 @SmallTest
 public class KeyguardViewMediatorTest extends SysuiTestCase {
     private KeyguardViewMediator mViewMediator;
@@ -160,4 +162,29 @@
         mViewMediator.onDozeAmountChanged(1f, 1f);
         assertFalse(mViewMediator.isAnimatingScreenOff());
     }
+
+    @Test
+    @TestableLooper.RunWithLooper(setAsMainLooper = true)
+    public void restoreBouncerWhenSimLockedAndKeyguardIsGoingAway() {
+        // When showing and provisioned
+        mViewMediator.onSystemReady();
+        when(mUpdateMonitor.isDeviceProvisioned()).thenReturn(true);
+        mViewMediator.setShowingLocked(true);
+
+        // and a SIM becomes locked and requires a PIN
+        mViewMediator.mUpdateCallback.onSimStateChanged(
+                1 /* subId */,
+                0 /* slotId */,
+                TelephonyManager.SIM_STATE_PIN_REQUIRED);
+
+        // and the keyguard goes away
+        mViewMediator.setShowingLocked(false);
+        when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(false);
+        mViewMediator.mUpdateCallback.onKeyguardVisibilityChanged(false);
+
+        TestableLooper.get(this).processAllMessages();
+
+        // then make sure it comes back
+        verify(mStatusBarKeyguardViewManager, atLeast(1)).show(null);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
new file mode 100644
index 0000000..d279bbb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
@@ -0,0 +1,189 @@
+/*
+ * 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.keyguard;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.PointF;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.os.Vibrator;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.DisplayMetrics;
+import android.view.View;
+import android.view.accessibility.AccessibilityManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardViewController;
+import com.android.keyguard.LockIconView;
+import com.android.keyguard.LockIconViewController;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.biometrics.AuthRippleController;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class LockIconViewControllerTest extends SysuiTestCase {
+    private @Mock LockIconView mLockIconView;
+    private @Mock Context mContext;
+    private @Mock Resources mResources;
+    private @Mock DisplayMetrics mDisplayMetrics;
+    private @Mock StatusBarStateController mStatusBarStateController;
+    private @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private @Mock KeyguardViewController mKeyguardViewController;
+    private @Mock KeyguardStateController mKeyguardStateController;
+    private @Mock FalsingManager mFalsingManager;
+    private @Mock AuthController mAuthController;
+    private @Mock DumpManager mDumpManager;
+    private @Mock AccessibilityManager mAccessibilityManager;
+    private @Mock ConfigurationController mConfigurationController;
+    private @Mock DelayableExecutor mDelayableExecutor;
+    private @Mock Vibrator mVibrator;
+    private @Mock AuthRippleController mAuthRippleController;
+
+    private LockIconViewController mLockIconViewController;
+
+    // Capture listeners so that they can be used to send events
+    @Captor private ArgumentCaptor<View.OnAttachStateChangeListener> mAttachCaptor =
+            ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
+    private View.OnAttachStateChangeListener mAttachListener;
+
+    @Captor private ArgumentCaptor<AuthController.Callback> mAuthControllerCallbackCaptor;
+    private AuthController.Callback mAuthControllerCallback;
+
+    @Captor private ArgumentCaptor<PointF> mPointCaptor;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        when(mLockIconView.getResources()).thenReturn(mResources);
+        when(mLockIconView.getContext()).thenReturn(mContext);
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics);
+
+        mLockIconViewController = new LockIconViewController(
+                mLockIconView,
+                mStatusBarStateController,
+                mKeyguardUpdateMonitor,
+                mKeyguardViewController,
+                mKeyguardStateController,
+                mFalsingManager,
+                mAuthController,
+                mDumpManager,
+                mAccessibilityManager,
+                mConfigurationController,
+                mDelayableExecutor,
+                mVibrator,
+                mAuthRippleController
+        );
+    }
+
+    @Test
+    public void testUpdateFingerprintLocationOnInit() {
+        // GIVEN fp sensor location is available pre-init
+        final PointF udfpsLocation = new PointF(50, 75);
+        final int radius = 33;
+        final FingerprintSensorPropertiesInternal fpProps =
+                new FingerprintSensorPropertiesInternal(
+                        /* sensorId */ 0,
+                        /* strength */ 0,
+                        /* max enrollments per user */ 5,
+                        /* component info */ new ArrayList<>(),
+                        /* sensorType */ 3,
+                        /* resetLockoutRequiresHwToken */ false,
+                        (int) udfpsLocation.x, (int) udfpsLocation.y, radius);
+        when(mAuthController.getUdfpsSensorLocation()).thenReturn(udfpsLocation);
+        when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps));
+
+        // WHEN lock icon view controller is initialized and attached
+        mLockIconViewController.init();
+        captureAttachListener();
+        mAttachListener.onViewAttachedToWindow(null);
+
+        // THEN lock icon view location is updated with the same coordinates as fpProps
+        verify(mLockIconView).setCenterLocation(mPointCaptor.capture(), eq(radius));
+        assertEquals(udfpsLocation, mPointCaptor.getValue());
+    }
+
+    @Test
+    public void testUpdateFingerprintLocationOnAuthenticatorsRegistered() {
+        // GIVEN fp sensor location is not available pre-init
+        when(mAuthController.getFingerprintSensorLocation()).thenReturn(null);
+        when(mAuthController.getUdfpsProps()).thenReturn(null);
+        mLockIconViewController.init();
+
+        // GIVEN fp sensor location is available post-init
+        captureAuthControllerCallback();
+        final PointF udfpsLocation = new PointF(50, 75);
+        final int radius = 33;
+        final FingerprintSensorPropertiesInternal fpProps =
+                new FingerprintSensorPropertiesInternal(
+                        /* sensorId */ 0,
+                        /* strength */ 0,
+                        /* max enrollments per user */ 5,
+                        /* component info */ new ArrayList<>(),
+                        /* sensorType */ 3,
+                        /* resetLockoutRequiresHwToken */ false,
+                        (int) udfpsLocation.x, (int) udfpsLocation.y, radius);
+        when(mAuthController.getUdfpsSensorLocation()).thenReturn(udfpsLocation);
+        when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps));
+
+        // WHEN all authenticators are registered
+        mAuthControllerCallback.onAllAuthenticatorsRegistered();
+
+        // THEN lock icon view location is updated with the same coordinates as fpProps
+        verify(mLockIconView).setCenterLocation(mPointCaptor.capture(), eq(radius));
+        assertEquals(udfpsLocation, mPointCaptor.getValue());
+    }
+
+    private void captureAuthControllerCallback() {
+        verify(mAuthController).addCallback(mAuthControllerCallbackCaptor.capture());
+        mAuthControllerCallback = mAuthControllerCallbackCaptor.getValue();
+    }
+
+    private void captureAttachListener() {
+        verify(mLockIconView).addOnAttachStateChangeListener(mAttachCaptor.capture());
+        mAttachListener = mAttachCaptor.getValue();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
index 79b0dd0..8cc2776 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
@@ -69,7 +69,7 @@
         whenever(notificationLockscreenUserManager.shouldShowLockscreenNotifications())
                 .thenReturn(true)
         whenever(mediaHost.hostView).thenReturn(hostView)
-
+        hostView.layoutParams = FrameLayout.LayoutParams(100, 100)
         keyguardMediaController = KeyguardMediaController(
             mediaHost,
             bypassController,
@@ -79,6 +79,7 @@
             configurationController
         )
         keyguardMediaController.attachSinglePaneContainer(mediaHeaderView)
+        keyguardMediaController.useSplitShade = false
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index 8fd2a32..3ea57be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -142,6 +142,8 @@
 
     @Test
     public void testCreateNavigationBarsIncludeDefaultTrue() {
+        // Tablets may be using taskbar and the logic is different
+        mNavigationBarController.mIsTablet = false;
         doNothing().when(mNavigationBarController).createNavigationBar(any(), any(), any());
 
         mNavigationBarController.createNavigationBars(true, null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
index de7abf8..922c6b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
@@ -14,13 +14,19 @@
 
 package com.android.systemui.qs;
 
-import static com.android.systemui.statusbar.phone.AutoTileManager.INVERSION;
 import static com.android.systemui.statusbar.phone.AutoTileManager.SAVER;
-import static com.android.systemui.statusbar.phone.AutoTileManager.WORK;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
 
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.os.UserHandle;
 import android.provider.Settings.Secure;
 import android.testing.AndroidTestingRunner;
@@ -28,13 +34,24 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.systemui.Prefs;
-import com.android.systemui.Prefs.Key;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.util.settings.FakeSettings;
+import com.android.systemui.util.settings.SecureSettings;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+import java.util.concurrent.Executor;
 
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper
@@ -43,42 +60,38 @@
 
     private static final int USER = 0;
 
+    @Mock
+    private BroadcastDispatcher mBroadcastDispatcher;
+    @Mock
+    private QSHost mQSHost;
+    @Mock
+    private DumpManager mDumpManager;
+    @Captor
+    private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverArgumentCaptor;
+    @Captor
+    private ArgumentCaptor<IntentFilter> mIntentFilterArgumentCaptor;
+
+    private Executor mBackgroundExecutor = Runnable::run; // Direct executor
     private AutoAddTracker mAutoTracker;
+    private SecureSettings mSecureSettings;
 
     @Before
     public void setUp() {
-        Secure.putString(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES, "");
-    }
+        MockitoAnnotations.initMocks(this);
 
-    @Test
-    public void testMigration() {
-        Prefs.putBoolean(mContext, Key.QS_DATA_SAVER_ADDED, true);
-        Prefs.putBoolean(mContext, Key.QS_WORK_ADDED, true);
-        mAutoTracker = new AutoAddTracker(mContext, USER);
+        mSecureSettings = new FakeSettings();
+
+        mSecureSettings.putStringForUser(Secure.QS_AUTO_ADDED_TILES, null, USER);
+
+        mAutoTracker = createAutoAddTracker(USER);
         mAutoTracker.initialize();
-
-        assertTrue(mAutoTracker.isAdded(SAVER));
-        assertTrue(mAutoTracker.isAdded(WORK));
-        assertFalse(mAutoTracker.isAdded(INVERSION));
-
-        // These keys have been removed; retrieving their values should always return the default.
-        assertTrue(Prefs.getBoolean(mContext, Key.QS_DATA_SAVER_ADDED, true ));
-        assertFalse(Prefs.getBoolean(mContext, Key.QS_DATA_SAVER_ADDED, false));
-        assertTrue(Prefs.getBoolean(mContext, Key.QS_WORK_ADDED, true));
-        assertFalse(Prefs.getBoolean(mContext, Key.QS_WORK_ADDED, false));
-
-        mAutoTracker.destroy();
     }
 
     @Test
     public void testChangeFromBackup() {
-        mAutoTracker = new AutoAddTracker(mContext, USER);
-        mAutoTracker.initialize();
-
         assertFalse(mAutoTracker.isAdded(SAVER));
 
-        Secure.putString(mContext.getContentResolver(), Secure.QS_AUTO_ADDED_TILES, SAVER);
-        mAutoTracker.mObserver.onChange(false);
+        mSecureSettings.putStringForUser(Secure.QS_AUTO_ADDED_TILES, SAVER, USER);
 
         assertTrue(mAutoTracker.isAdded(SAVER));
 
@@ -87,9 +100,6 @@
 
     @Test
     public void testSetAdded() {
-        mAutoTracker = new AutoAddTracker(mContext, USER);
-        mAutoTracker.initialize();
-
         assertFalse(mAutoTracker.isAdded(SAVER));
         mAutoTracker.setTileAdded(SAVER);
 
@@ -100,14 +110,12 @@
 
     @Test
     public void testPersist() {
-        mAutoTracker = new AutoAddTracker(mContext, USER);
-        mAutoTracker.initialize();
-
         assertFalse(mAutoTracker.isAdded(SAVER));
         mAutoTracker.setTileAdded(SAVER);
 
         mAutoTracker.destroy();
-        mAutoTracker = new AutoAddTracker(mContext, USER);
+        mAutoTracker = createAutoAddTracker(USER);
+        mAutoTracker.initialize();
 
         assertTrue(mAutoTracker.isAdded(SAVER));
 
@@ -116,22 +124,158 @@
 
     @Test
     public void testIndependentUsers() {
-        mAutoTracker = new AutoAddTracker(mContext, USER);
-        mAutoTracker.initialize();
         mAutoTracker.setTileAdded(SAVER);
 
-        mAutoTracker = new AutoAddTracker(mContext, USER + 1);
+        mAutoTracker = createAutoAddTracker(USER + 1);
+        mAutoTracker.initialize();
         assertFalse(mAutoTracker.isAdded(SAVER));
     }
 
     @Test
     public void testChangeUser() {
-        mAutoTracker = new AutoAddTracker(mContext, USER);
-        mAutoTracker.initialize();
         mAutoTracker.setTileAdded(SAVER);
 
-        mAutoTracker = new AutoAddTracker(mContext, USER + 1);
+        mAutoTracker = createAutoAddTracker(USER + 1);
         mAutoTracker.changeUser(UserHandle.of(USER));
         assertTrue(mAutoTracker.isAdded(SAVER));
     }
+
+    @Test
+    public void testBroadcastReceiverRegistered() {
+        verify(mBroadcastDispatcher).registerReceiver(
+                any(), mIntentFilterArgumentCaptor.capture(), any(), eq(UserHandle.of(USER)));
+
+        assertTrue(
+                mIntentFilterArgumentCaptor.getValue().hasAction(Intent.ACTION_SETTING_RESTORED));
+    }
+
+    @Test
+    public void testBroadcastReceiverChangesWithUser() {
+        mAutoTracker.changeUser(UserHandle.of(USER + 1));
+
+        InOrder inOrder = Mockito.inOrder(mBroadcastDispatcher);
+        inOrder.verify(mBroadcastDispatcher).unregisterReceiver(any());
+        inOrder.verify(mBroadcastDispatcher)
+                .registerReceiver(any(), any(), any(), eq(UserHandle.of(USER + 1)));
+    }
+
+    @Test
+    public void testSettingRestoredWithTilesNotRemovedInSource_noAutoAddedInTarget() {
+        verify(mBroadcastDispatcher).registerReceiver(
+                mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any());
+
+        // These tiles were present in the original device
+        String restoredTiles = "saver,work,internet,cast";
+        Intent restoreTilesIntent = makeRestoreIntent(Secure.QS_TILES, null, restoredTiles);
+        mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
+
+        // And these tiles have been auto-added in the original device
+        // (no auto-added before restore)
+        String restoredAutoAddTiles = "work";
+        Intent restoreAutoAddTilesIntent =
+                makeRestoreIntent(Secure.QS_AUTO_ADDED_TILES, null, restoredAutoAddTiles);
+        mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreAutoAddTilesIntent);
+
+        // Then, don't remove any current tiles
+        verify(mQSHost, never()).removeTiles(any());
+        assertEquals(restoredAutoAddTiles,
+                mSecureSettings.getStringForUser(Secure.QS_AUTO_ADDED_TILES, USER));
+    }
+
+    @Test
+    public void testSettingRestoredWithTilesRemovedInSource_noAutoAddedInTarget() {
+        verify(mBroadcastDispatcher)
+                .registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any());
+
+        // These tiles were present in the original device
+        String restoredTiles = "saver,internet,cast";
+        Intent restoreTilesIntent = makeRestoreIntent(Secure.QS_TILES, null, restoredTiles);
+        mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
+
+        // And these tiles have been auto-added in the original device
+        // (no auto-added before restore)
+        String restoredAutoAddTiles = "work";
+        Intent restoreAutoAddTilesIntent =
+                makeRestoreIntent(Secure.QS_AUTO_ADDED_TILES, null, restoredAutoAddTiles);
+        mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreAutoAddTilesIntent);
+
+        // Then, remove work tile
+        verify(mQSHost).removeTiles(List.of("work"));
+        assertEquals(restoredAutoAddTiles,
+                mSecureSettings.getStringForUser(Secure.QS_AUTO_ADDED_TILES, USER));
+    }
+
+    @Test
+    public void testSettingRestoredWithTilesRemovedInSource_sameAutoAddedinTarget() {
+        verify(mBroadcastDispatcher)
+                .registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any());
+
+        // These tiles were present in the original device
+        String restoredTiles = "saver,internet,cast";
+        Intent restoreTilesIntent =
+                makeRestoreIntent(Secure.QS_TILES, "saver, internet, cast, work", restoredTiles);
+        mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
+
+        // And these tiles have been auto-added in the original device
+        // (no auto-added before restore)
+        String restoredAutoAddTiles = "work";
+        Intent restoreAutoAddTilesIntent =
+                makeRestoreIntent(Secure.QS_AUTO_ADDED_TILES, "work", restoredAutoAddTiles);
+        mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreAutoAddTilesIntent);
+
+        // Then, remove work tile
+        verify(mQSHost).removeTiles(List.of("work"));
+        assertEquals(restoredAutoAddTiles,
+                mSecureSettings.getStringForUser(Secure.QS_AUTO_ADDED_TILES, USER));
+    }
+
+    @Test
+    public void testSettingRestoredWithTilesRemovedInSource_othersAutoAddedinTarget() {
+        verify(mBroadcastDispatcher)
+                .registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), any(), any(), any());
+
+        // These tiles were present in the original device
+        String restoredTiles = "saver,internet,cast";
+        Intent restoreTilesIntent =
+                makeRestoreIntent(Secure.QS_TILES, "saver, internet, cast, work", restoredTiles);
+        mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreTilesIntent);
+
+        // And these tiles have been auto-added in the original device
+        // (no auto-added before restore)
+        String restoredAutoAddTiles = "work";
+        Intent restoreAutoAddTilesIntent =
+                makeRestoreIntent(Secure.QS_AUTO_ADDED_TILES, "inversion", restoredAutoAddTiles);
+        mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, restoreAutoAddTilesIntent);
+
+        // Then, remove work tile
+        verify(mQSHost).removeTiles(List.of("work"));
+
+        String setting = mSecureSettings.getStringForUser(Secure.QS_AUTO_ADDED_TILES, USER);
+        assertEquals(2, setting.split(",").length);
+        assertTrue(setting.contains("work"));
+        assertTrue(setting.contains("inversion"));
+    }
+
+
+    private Intent makeRestoreIntent(
+            String settingName, String previousValue, String restoredValue) {
+        Intent intent = new Intent(Intent.ACTION_SETTING_RESTORED);
+        intent.putExtra(Intent.EXTRA_SETTING_NAME, settingName);
+        intent.putExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE, previousValue);
+        intent.putExtra(Intent.EXTRA_SETTING_NEW_VALUE, restoredValue);
+        return intent;
+    }
+
+    private AutoAddTracker createAutoAddTracker(int user) {
+        // Null handler wil dispatch sync.
+        return new AutoAddTracker(
+                mSecureSettings,
+                mBroadcastDispatcher,
+                mQSHost,
+                mDumpManager,
+                null,
+                mBackgroundExecutor,
+                user
+        );
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
new file mode 100644
index 0000000..e54a6ec
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
@@ -0,0 +1,93 @@
+package com.android.systemui.qs
+
+import com.android.systemui.R
+import android.os.UserManager
+import android.view.LayoutInflater
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.UiEventLogger
+import com.android.internal.logging.testing.FakeMetricsLogger
+import com.android.systemui.Dependency
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.globalactions.GlobalActionsDialogLite
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.FooterActionsController.ExpansionState
+import com.android.systemui.statusbar.phone.MultiUserSwitchController
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.UserInfoController
+import com.android.systemui.tuner.TunerService
+import com.android.systemui.utils.leaks.FakeTunerService
+import com.android.systemui.utils.leaks.LeakCheckedTest
+import org.junit.Before
+import org.junit.Test
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+class FooterActionsControllerTest : LeakCheckedTest() {
+    @Mock
+    private lateinit var userManager: UserManager
+    @Mock
+    private lateinit var activityStarter: ActivityStarter
+    @Mock
+    private lateinit var deviceProvisionedController: DeviceProvisionedController
+    @Mock
+    private lateinit var userInfoController: UserInfoController
+    @Mock
+    private lateinit var qsPanelController: QSPanelController
+    @Mock
+    private lateinit var multiUserSwitchController: MultiUserSwitchController
+    @Mock
+    private lateinit var globalActionsDialog: GlobalActionsDialogLite
+    @Mock
+    private lateinit var uiEventLogger: UiEventLogger
+    @Mock
+    private lateinit var controller: FooterActionsController
+
+    private val metricsLogger: MetricsLogger = FakeMetricsLogger()
+    private lateinit var view: FooterActionsView
+    private val falsingManager: FalsingManagerFake = FalsingManagerFake()
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        injectLeakCheckedDependencies(*LeakCheckedTest.ALL_SUPPORTED_CLASSES)
+        val fakeTunerService = Dependency.get(TunerService::class.java) as FakeTunerService
+
+        view = LayoutInflater.from(context)
+                .inflate(R.layout.footer_actions, null) as FooterActionsView
+
+        controller = FooterActionsController(view, qsPanelController, activityStarter,
+                userManager, userInfoController, multiUserSwitchController,
+                deviceProvisionedController, falsingManager, metricsLogger, fakeTunerService,
+                globalActionsDialog, uiEventLogger, showPMLiteButton = true,
+                buttonsVisibleState = ExpansionState.EXPANDED)
+        controller.init()
+        controller.onViewAttached()
+    }
+
+    @Test
+    fun testLogPowerMenuClick() {
+        controller.expanded = true
+        falsingManager.setFalseTap(false)
+
+        view.findViewById<View>(R.id.pm_lite).performClick()
+        // Verify clicks are logged
+        verify(uiEventLogger, Mockito.times(1))
+                .log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS)
+    }
+
+    @Test
+    fun testSettings_UserNotSetup() {
+        whenever(deviceProvisionedController.isCurrentUserSetup).thenReturn(false)
+        view.findViewById<View>(R.id.settings_button).performClick()
+        // Verify Settings wasn't launched.
+        verify<ActivityStarter>(activityStarter, Mockito.never()).startActivity(any(), anyBoolean())
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
index 6f7bf3b..8b19c50 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
@@ -18,16 +18,11 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.ClipData;
 import android.content.ClipboardManager;
-import android.os.UserManager;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.View;
@@ -35,21 +30,8 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.testing.FakeMetricsLogger;
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.classifier.FalsingManagerFake;
-import com.android.systemui.globalactions.GlobalActionsDialogLite;
-import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.phone.MultiUserSwitchController;
-import com.android.systemui.statusbar.phone.SettingsButton;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.UserInfoController;
-import com.android.systemui.tuner.TunerService;
-import com.android.systemui.utils.leaks.FakeTunerService;
 import com.android.systemui.utils.leaks.LeakCheckedTest;
 
 import org.junit.Before;
@@ -67,14 +49,6 @@
     @Mock
     private QSFooterView mView;
     @Mock
-    private UserManager mUserManager;
-    @Mock
-    private ActivityStarter mActivityStarter;
-    @Mock
-    private DeviceProvisionedController mDeviceProvisionedController;
-    @Mock
-    private UserInfoController mUserInfoController;
-    @Mock
     private UserTracker mUserTracker;
     @Mock
     private QSPanelController mQSPanelController;
@@ -82,36 +56,19 @@
     private ClipboardManager mClipboardManager;
     @Mock
     private QuickQSPanelController mQuickQSPanelController;
-    private FakeTunerService mFakeTunerService;
-    private MetricsLogger mMetricsLogger = new FakeMetricsLogger();
-    private FalsingManagerFake mFalsingManager;
-
-    @Mock
-    private SettingsButton mSettingsButton;
     @Mock
     private TextView mBuildText;
     @Mock
-    private View mEdit;
-    @Mock
-    private MultiUserSwitchController mMultiUserSwitchController;
-    @Mock
-    private View mPowerMenuLiteView;
-    @Mock
-    private GlobalActionsDialogLite mGlobalActionsDialog;
-    @Mock
-    private UiEventLogger mUiEventLogger;
+    private FooterActionsController mFooterActionsController;
 
     private QSFooterViewController mController;
 
     @Before
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mFalsingManager = new FalsingManagerFake();
 
         injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
 
-        mFakeTunerService = (FakeTunerService) Dependency.get(TunerService.class);
-
         mContext.addMockSystemService(ClipboardManager.class, mClipboardManager);
 
         when(mView.getContext()).thenReturn(mContext);
@@ -119,16 +76,10 @@
         when(mUserTracker.getUserContext()).thenReturn(mContext);
 
         when(mView.isAttachedToWindow()).thenReturn(true);
-        when(mView.findViewById(R.id.settings_button)).thenReturn(mSettingsButton);
         when(mView.findViewById(R.id.build)).thenReturn(mBuildText);
-        when(mView.findViewById(android.R.id.edit)).thenReturn(mEdit);
-        when(mView.findViewById(R.id.pm_lite)).thenReturn(mPowerMenuLiteView);
 
-        mController = new QSFooterViewController(mView, mUserManager, mUserInfoController,
-                mActivityStarter, mDeviceProvisionedController, mUserTracker, mQSPanelController,
-                mMultiUserSwitchController, mQuickQSPanelController, mFakeTunerService,
-                mMetricsLogger, mFalsingManager, false, mGlobalActionsDialog,
-                mUiEventLogger);
+        mController = new QSFooterViewController(mView, mUserTracker, mQSPanelController,
+                mQuickQSPanelController, mFooterActionsController);
 
         mController.init();
     }
@@ -148,40 +99,4 @@
         verify(mClipboardManager).setPrimaryClip(captor.capture());
         assertThat(captor.getValue().getItemAt(0).getText()).isEqualTo(text);
     }
-
-    @Test
-    public void testSettings_UserNotSetup() {
-        ArgumentCaptor<View.OnClickListener> onClickCaptor =
-                ArgumentCaptor.forClass(View.OnClickListener.class);
-        verify(mSettingsButton).setOnClickListener(onClickCaptor.capture());
-
-        when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(false);
-
-        onClickCaptor.getValue().onClick(mSettingsButton);
-        // Verify Settings wasn't launched.
-        verify(mActivityStarter, never()).startActivity(any(), anyBoolean());
-    }
-
-    @Test
-    public void testLogPowerMenuClick() {
-        // Enable power menu button
-        mController = new QSFooterViewController(mView, mUserManager, mUserInfoController,
-                mActivityStarter, mDeviceProvisionedController, mUserTracker, mQSPanelController,
-                mMultiUserSwitchController, mQuickQSPanelController, mFakeTunerService,
-                mMetricsLogger, new FalsingManagerFake(), true, mGlobalActionsDialog,
-                mUiEventLogger);
-        mController.init();
-        mController.setExpanded(true);
-        mFalsingManager.setFalseTap(false);
-
-        ArgumentCaptor<View.OnClickListener> onClickCaptor =
-                ArgumentCaptor.forClass(View.OnClickListener.class);
-        verify(mPowerMenuLiteView).setOnClickListener(onClickCaptor.capture());
-
-        onClickCaptor.getValue().onClick(mPowerMenuLiteView);
-
-        // Verify clicks are logged
-        verify(mUiEventLogger, times(1))
-                .log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS);
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index ad16e9d..109721f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -186,6 +186,7 @@
                 mQQSMediaHost,
                 mBypassController,
                 mQsComponentFactory,
-                mFalsingManager);
+                mFalsingManager,
+                mock(DumpManager.class));
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelSwitchToParentTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelSwitchToParentTest.kt
new file mode 100644
index 0000000..56f2905
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelSwitchToParentTest.kt
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs
+
+import com.google.common.truth.Truth.assertThat
+
+import androidx.test.filters.SmallTest
+
+import android.testing.AndroidTestingRunner
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.children
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class QSPanelSwitchToParentTest : SysuiTestCase() {
+
+    private lateinit var parent1: FrameLayout
+    private lateinit var parent2: FrameLayout
+
+    private lateinit var movingView: View
+
+    private lateinit var view1A: View
+    private lateinit var view1B: View
+    private lateinit var view1C: View
+
+    private lateinit var view2A: View
+    private lateinit var view2B: View
+    private lateinit var view2C: View
+
+    @Before
+    fun setUp() {
+        parent1 = FrameLayout(mContext)
+        parent2 = FrameLayout(mContext)
+
+        movingView = View(mContext)
+
+        view1A = View(mContext)
+        parent1.addView(view1A)
+        view1B = View(mContext)
+        parent1.addView(view1B)
+        view1C = View(mContext)
+        parent1.addView(view1C)
+
+        view2A = View(mContext)
+        parent2.addView(view2A)
+        view2B = View(mContext)
+        parent2.addView(view2B)
+        view2C = View(mContext)
+        parent2.addView(view2C)
+    }
+
+    @Test
+    fun testNullTargetNoInteractions() {
+        QSPanel.switchToParent(movingView, null, -1, "")
+
+        assertThat(movingView.parent).isNull()
+    }
+
+    @Test
+    fun testMoveToEndNoParent() {
+        QSPanel.switchToParent(movingView, parent2, -1, "")
+
+        assertThat(parent1.childrenList).containsExactly(
+                view1A, view1B, view1C
+        )
+
+        assertThat(parent2.childrenList).containsExactly(
+                view2A, view2B, view2C, movingView
+        )
+    }
+
+    @Test
+    fun testMoveToEndDifferentParent() {
+        parent1.addView(movingView, 0)
+
+        QSPanel.switchToParent(movingView, parent2, -1, "")
+
+        assertThat(parent1.childrenList).containsExactly(
+                view1A, view1B, view1C
+        )
+        assertThat(parent2.childrenList).containsExactly(
+                view2A, view2B, view2C, movingView
+        )
+    }
+
+    @Test
+    fun testMoveToEndSameParent() {
+        parent2.addView(movingView, 0)
+
+        QSPanel.switchToParent(movingView, parent2, -1, "")
+
+        assertThat(parent1.childrenList).containsExactly(
+                view1A, view1B, view1C
+        )
+        assertThat(parent2.childrenList).containsExactly(
+                view2A, view2B, view2C, movingView
+        )
+    }
+
+    @Test
+    fun testMoveToMiddleFromNoParent() {
+        QSPanel.switchToParent(movingView, parent2, 1, "")
+
+        assertThat(parent1.childrenList).containsExactly(
+                view1A, view1B, view1C
+        )
+        assertThat(parent2.childrenList).containsExactly(
+                view2A, movingView, view2B, view2C
+        )
+    }
+
+    @Test
+    fun testMoveToMiddleDifferentParent() {
+        parent1.addView(movingView, 1)
+
+        QSPanel.switchToParent(movingView, parent2, 2, "")
+
+        assertThat(parent1.childrenList).containsExactly(
+                view1A, view1B, view1C
+        )
+        assertThat(parent2.childrenList).containsExactly(
+                view2A, view2B, movingView, view2C
+        )
+    }
+
+    @Test
+    fun testMoveToMiddleSameParent() {
+        parent2.addView(movingView, 0)
+
+        QSPanel.switchToParent(movingView, parent2, 1, "")
+
+        assertThat(parent1.childrenList).containsExactly(
+                view1A, view1B, view1C
+        )
+        assertThat(parent2.childrenList).containsExactly(
+                view2A, movingView, view2B, view2C
+        )
+    }
+
+    private val ViewGroup.childrenList: List<View>
+        get() = children.toList()
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
index a83a5e1..3500c18 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
@@ -69,6 +69,8 @@
     @Mock
     private lateinit var mQSTileView: QSTileView
 
+    private lateinit var mFooter: View
+
     @Before
     @Throws(Exception::class)
     fun setup() {
@@ -81,7 +83,8 @@
             mQsPanel = QSPanel(mContext, null)
             mQsPanel.initialize()
             // QSPanel inflates a footer inside of it, mocking it here
-            mQsPanel.addView(LinearLayout(mContext).apply { id = R.id.qs_footer })
+            mFooter = LinearLayout(mContext).apply { id = R.id.qs_footer }
+            mQsPanel.addView(mFooter)
             mQsPanel.onFinishInflate()
             mQsPanel.setSecurityFooter(View(mContext), false)
             mQsPanel.setHeaderContainer(LinearLayout(mContext))
@@ -125,7 +128,10 @@
             mQsPanel.isExpanded = true
         }
 
-        assertThat(mQsPanel.indexOfChild(mQsPanel.mSecurityFooter)).isEqualTo(2)
+        // After mFooter
+        assertThat(mQsPanel.indexOfChild(mQsPanel.mSecurityFooter)).isEqualTo(
+                mQsPanel.indexOfChild(mFooter) + 1
+        )
     }
 
     @Test
@@ -137,7 +143,10 @@
             mQsPanel.isExpanded = true
         }
 
-        assertThat(mQsPanel.indexOfChild(mQsPanel.mSecurityFooter)).isEqualTo(2)
+        // After mFooter
+        assertThat(mQsPanel.indexOfChild(mQsPanel.mSecurityFooter)).isEqualTo(
+                mQsPanel.indexOfChild(mFooter) + 1
+        )
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index f208b80..6c7f770 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -18,14 +18,11 @@
 
 
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
-import static junit.framework.TestCase.assertFalse;
 
-import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -44,7 +41,6 @@
 import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
 
-import com.android.dx.mockito.inline.extended.ExtendedMockito;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.util.CollectionUtils;
@@ -53,6 +49,7 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.QSFactory;
 import com.android.systemui.plugins.qs.QSTile;
@@ -64,22 +61,18 @@
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.phone.AutoTileManager;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.settings.FakeSettings;
 import com.android.systemui.util.settings.SecureSettings;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.mockito.MockitoSession;
-import org.mockito.quality.Strictness;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -125,7 +118,6 @@
     private UiEventLogger mUiEventLogger;
     @Mock
     private UserTracker mUserTracker;
-    @Mock
     private SecureSettings mSecureSettings;
     @Mock
     private CustomTileStatePersister mCustomTileStatePersister;
@@ -133,34 +125,21 @@
     private Handler mHandler;
     private TestableLooper mLooper;
     private QSTileHost mQSTileHost;
-    MockitoSession mMockingSession = null;
 
     @Before
     public void setUp() {
-        // TODO(b/174753536): Remove the mMockingSession when
-        // FeatureFlagUtils.SETTINGS_PROVIDER_MODEL is removed.
-        mMockingSession = ExtendedMockito.mockitoSession().strictness(Strictness.LENIENT)
-                .mockStatic(FeatureFlags.class).startMocking();
-        ExtendedMockito.doReturn(false)
-                .when(() -> FeatureFlags.isProviderModelSettingEnabled(mContext));
         MockitoAnnotations.initMocks(this);
         mLooper = TestableLooper.get(this);
         mHandler = new Handler(mLooper.getLooper());
+
+        mSecureSettings = new FakeSettings();
+        mSecureSettings.putStringForUser(
+                QSTileHost.TILES_SETTING, "", "", false, mUserTracker.getUserId(), false);
         mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler,
                 mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager,
                 mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger, mUserTracker,
                 mSecureSettings, mCustomTileStatePersister);
         setUpTileFactory();
-
-        when(mSecureSettings.getStringForUser(eq(QSTileHost.TILES_SETTING), anyInt()))
-                .thenReturn("");
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        if (mMockingSession != null) {
-            mMockingSession.finishMocking();
-        }
     }
 
     private void setUpTileFactory() {
@@ -383,6 +362,16 @@
                 .removeState(new TileServiceKey(CUSTOM_TILE, mQSTileHost.getUserId()));
     }
 
+    @Test
+    public void testRemoveTiles() {
+        List<String> tiles = List.of("spec1", "spec2", "spec3");
+        mQSTileHost.saveTilesToSettings(tiles);
+
+        mQSTileHost.removeTiles(List.of("spec1", "spec2"));
+
+        assertEquals(List.of("spec3"), mQSTileHost.mTileSpecs);
+    }
+
     private class TestQSTileHost extends QSTileHost {
         TestQSTileHost(Context context, StatusBarIconController iconController,
                 QSFactory defaultFactory, Handler mainHandler, Looper bgLooper,
@@ -408,14 +397,11 @@
         @Override
         void saveTilesToSettings(List<String> tileSpecs) {
             super.saveTilesToSettings(tileSpecs);
-
-            ArgumentCaptor<String> specs = ArgumentCaptor.forClass(String.class);
-            verify(mSecureSettings, atLeastOnce()).putStringForUser(eq(QSTileHost.TILES_SETTING),
-                    specs.capture(), isNull(), eq(false), anyInt(), eq(true));
-
             // After tiles are changed, make sure to call onTuningChanged with the new setting if it
             // changed
-            onTuningChanged(TILES_SETTING, specs.getValue());
+            String specs = mSecureSettings.getStringForUser(
+                    QSTileHost.TILES_SETTING, mUserTracker.getUserId());
+            onTuningChanged(TILES_SETTING, specs);
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSBrightnessControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSBrightnessControllerTest.kt
index f8373ff..de1d86b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSBrightnessControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSBrightnessControllerTest.kt
@@ -19,13 +19,15 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.settings.brightness.BrightnessController
+import com.android.systemui.statusbar.policy.BrightnessMirrorController
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.mockito.Mock
-import org.mockito.Mockito.verify
 import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
 import org.mockito.Mockito.never
+import org.mockito.Mockito.mock
 import org.mockito.junit.MockitoJUnit
 
 @SmallTest
@@ -104,4 +106,13 @@
 
         verify(brightnessController, never()).registerCallbacks()
     }
+
+    @Test
+    fun testMirrorIsSetWhenSliderIsShown() {
+        val mirrorController = mock(BrightnessMirrorController::class.java)
+        quickQSBrightnessController.setMirror(mirrorController)
+        quickQSBrightnessController.refreshVisibility(shouldUseSplitNotificationShade = true)
+
+        verify(brightnessController).setMirror(mirrorController)
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index 62ac72e..59948d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs
 
+import android.content.res.Configuration
 import android.test.suitebuilder.annotation.SmallTest
 import android.testing.AndroidTestingRunner
 import com.android.internal.logging.MetricsLogger
@@ -27,12 +28,13 @@
 import com.android.systemui.plugins.qs.QSTileView
 import com.android.systemui.qs.customize.QSCustomizerController
 import com.android.systemui.qs.logging.QSLogger
-import com.android.systemui.statusbar.FeatureFlags
 import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.Captor
 import org.mockito.Mock
 import org.mockito.Mockito.`when`
 import org.mockito.Mockito.any
@@ -65,9 +67,11 @@
     @Mock
     private lateinit var tileView: QSTileView
     @Mock
-    private lateinit var featureFlags: FeatureFlags
-    @Mock
     private lateinit var quickQsBrightnessController: QuickQSBrightnessController
+    @Mock
+    private lateinit var footerActionsController: FooterActionsController
+    @Captor
+    private lateinit var captor: ArgumentCaptor<QSPanel.OnConfigurationChangedListener>
 
     private lateinit var controller: QuickQSPanelController
 
@@ -76,6 +80,7 @@
         MockitoAnnotations.initMocks(this)
 
         `when`(quickQSPanel.tileLayout).thenReturn(tileLayout)
+        `when`(quickQSPanel.isAttachedToWindow).thenReturn(true)
         `when`(quickQSPanel.dumpableTag).thenReturn("")
         `when`(quickQSPanel.resources).thenReturn(mContext.resources)
         `when`(qsTileHost.createTileView(any(), any(), anyBoolean())).thenReturn(tileView)
@@ -90,7 +95,8 @@
                 uiEventLogger,
                 qsLogger,
                 dumpManager,
-                quickQsBrightnessController
+                quickQsBrightnessController,
+                footerActionsController
         )
 
         controller.init()
@@ -120,4 +126,16 @@
 
         verify(quickQSPanel, times(limit)).addTile(any())
     }
+
+    @Test
+    fun testBrightnessAndFooterVisibilityRefreshedWhenConfigurationChanged() {
+        // times(2) because both controller and base controller are registering their listeners
+        verify(quickQSPanel, times(2)).addOnConfigurationChangedListener(captor.capture())
+
+        captor.allValues.forEach { it.onConfigurationChange(Configuration.EMPTY) }
+
+        verify(quickQsBrightnessController).refreshVisibility(anyBoolean())
+        // times(2) because footer visibility is also refreshed on controller init
+        verify(footerActionsController, times(2)).refreshVisibility(anyBoolean())
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
index 35360bd..92b9f75 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
@@ -23,8 +23,10 @@
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.battery.BatteryMeterViewController
 import com.android.systemui.colorextraction.SysuiColorExtractor
 import com.android.systemui.demomode.DemoModeController
+import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.privacy.OngoingPrivacyChip
 import com.android.systemui.privacy.PrivacyDialogController
@@ -32,10 +34,11 @@
 import com.android.systemui.privacy.logging.PrivacyLogger
 import com.android.systemui.qs.carrier.QSCarrierGroup
 import com.android.systemui.qs.carrier.QSCarrierGroupController
-import com.android.systemui.statusbar.FeatureFlags
 import com.android.systemui.statusbar.phone.StatusBarIconController
 import com.android.systemui.statusbar.phone.StatusIconContainer
 import com.android.systemui.statusbar.policy.Clock
+import com.android.systemui.statusbar.policy.VariableDateView
+import com.android.systemui.statusbar.policy.VariableDateViewController
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.capture
@@ -47,9 +50,9 @@
 import org.mockito.Answers
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
-import org.mockito.Mockito.`when`
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 
 @SmallTest
@@ -87,8 +90,16 @@
     @Mock
     private lateinit var privacyDialogController: PrivacyDialogController
     @Mock
+    private lateinit var variableDateViewControllerFactory: VariableDateViewController.Factory
+    @Mock
+    private lateinit var variableDateViewController: VariableDateViewController
+    @Mock
+    private lateinit var batteryMeterViewController: BatteryMeterViewController
+    @Mock
     private lateinit var clock: Clock
     @Mock
+    private lateinit var variableDateView: VariableDateView
+    @Mock
     private lateinit var mockView: View
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private lateinit var context: Context
@@ -109,6 +120,8 @@
         stubViews()
         `when`(iconContainer.context).thenReturn(context)
         `when`(qsCarrierGroupControllerBuilder.build()).thenReturn(qsCarrierGroupController)
+        `when`(variableDateViewControllerFactory.create(any()))
+                .thenReturn(variableDateViewController)
         `when`(view.resources).thenReturn(mContext.resources)
         `when`(view.isAttachedToWindow).thenReturn(true)
         `when`(view.context).thenReturn(context)
@@ -133,7 +146,9 @@
                 colorExtractor,
                 privacyDialogController,
                 qsExpansionPathInterpolator,
-                featureFlags
+                batteryMeterViewController,
+                featureFlags,
+                variableDateViewControllerFactory
         )
     }
 
@@ -274,6 +289,8 @@
         `when`(view.findViewById<StatusIconContainer>(R.id.statusIcons)).thenReturn(iconContainer)
         `when`(view.findViewById<OngoingPrivacyChip>(R.id.privacy_chip)).thenReturn(privacyChip)
         `when`(view.findViewById<Clock>(R.id.clock)).thenReturn(clock)
+        `when`(view.requireViewById<VariableDateView>(R.id.date)).thenReturn(variableDateView)
+        `when`(view.requireViewById<VariableDateView>(R.id.date_clock)).thenReturn(variableDateView)
     }
 
     private fun setPrivacyController(micCamera: Boolean, location: Boolean) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
index 72c7ddd..126b332 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
@@ -37,8 +37,8 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.keyguard.CarrierTextManager;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
 import com.android.systemui.util.CarrierConfigTracker;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index 4a1411a..8546a35 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -45,26 +45,23 @@
 import android.testing.TestableLooper;
 import android.text.TextUtils;
 import android.util.ArraySet;
-import android.util.FeatureFlagUtils;
 import android.view.View;
 
 import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
 
-import com.android.dx.mockito.inline.extended.ExtendedMockito;
 import com.android.internal.logging.InstanceId;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSIconView;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -74,8 +71,6 @@
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.mockito.MockitoSession;
-import org.mockito.quality.Strictness;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -114,7 +109,6 @@
     private PackageManager mPackageManager;
     @Mock
     private UserTracker mUserTracker;
-    @Mock private FeatureFlags mFeatureFlags;
     @Captor
     private ArgumentCaptor<List<TileQueryHelper.TileInfo>> mCaptor;
 
@@ -122,18 +116,10 @@
     private TileQueryHelper mTileQueryHelper;
     private FakeExecutor mMainExecutor;
     private FakeExecutor mBgExecutor;
-    MockitoSession mMockingSession = null;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        // TODO(b/174753536): Remove the mMockingSession when
-        // FeatureFlagUtils.SETTINGS_PROVIDER_MODEL is removed.
-        mMockingSession = ExtendedMockito.mockitoSession().strictness(Strictness.LENIENT)
-                .mockStatic(FeatureFlagUtils.class).startMocking();
-        ExtendedMockito.doReturn(false).when(() -> FeatureFlagUtils.isEnabled(mContext,
-                FeatureFlagUtils.SETTINGS_PROVIDER_MODEL));
-
         mContext.setMockPackageManager(mPackageManager);
 
         mState = new QSTile.State();
@@ -153,17 +139,10 @@
         mMainExecutor = new FakeExecutor(clock);
         mBgExecutor = new FakeExecutor(clock);
         mTileQueryHelper = new TileQueryHelper(
-                mContext, mUserTracker, mMainExecutor, mBgExecutor, mFeatureFlags);
+                mContext, mUserTracker, mMainExecutor, mBgExecutor);
         mTileQueryHelper.setListener(mListener);
     }
 
-    @After
-    public void tearDown() throws Exception {
-        if (mMockingSession != null) {
-            mMockingSession.finishMocking();
-        }
-    }
-
     @Test
     public void testIsFinished_falseBeforeQuerying() {
         assertFalse(mTileQueryHelper.isFinished());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
index 63ebe92..23e5168 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
@@ -18,6 +18,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -74,6 +75,24 @@
     }
 
     @Test
+    public void testMutateIconDrawable() {
+        SlashImageView iv = mock(SlashImageView.class);
+        Drawable originalDrawable = mock(Drawable.class);
+        Drawable otherDrawable = mock(Drawable.class);
+        State s = new State();
+        s.icon = mock(Icon.class);
+        when(s.icon.getInvisibleDrawable(eq(mContext))).thenReturn(originalDrawable);
+        when(s.icon.getDrawable(eq(mContext))).thenReturn(originalDrawable);
+        when(iv.isShown()).thenReturn(true);
+        when(originalDrawable.getConstantState()).thenReturn(fakeConstantState(otherDrawable));
+
+
+        mIconView.updateIcon(iv, s, /* allowAnimations= */true);
+
+        verify(iv).setState(any(), eq(otherDrawable));
+    }
+
+    @Test
     public void testNoFirstFade() {
         ImageView iv = mock(ImageView.class);
         State s = new State();
@@ -104,4 +123,18 @@
     public void testIconNotSet_toString() {
         assertFalse(mIconView.toString().contains("lastIcon"));
     }
+
+    private static Drawable.ConstantState fakeConstantState(Drawable otherDrawable) {
+        return new Drawable.ConstantState() {
+            @Override
+            public Drawable newDrawable() {
+                return otherDrawable;
+            }
+
+            @Override
+            public int getChangingConfigurations() {
+                return 1;
+            }
+        };
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index d44a526..e939411 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -17,6 +17,7 @@
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.TestCase.assertEquals;
 
+import static org.junit.Assert.assertFalse;
 import static org.mockito.ArgumentMatchers.same;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.mock;
@@ -327,4 +328,77 @@
         assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state);
         assertTrue(mCastTile.getState().secondaryLabel.toString().startsWith(connected.name));
     }
+
+    @Test
+    public void testExpandView_wifiNotConnected() {
+        mCastTile.refreshState();
+        mTestableLooper.processAllMessages();
+
+        assertFalse(mCastTile.getState().forceExpandIcon);
+    }
+
+    @Test
+    public void testExpandView_wifiEnabledNotCasting() {
+        enableWifiAndProcessMessages();
+
+        assertTrue(mCastTile.getState().forceExpandIcon);
+    }
+
+    @Test
+    public void testExpandView_casting_projection() {
+        CastController.CastDevice device = new CastController.CastDevice();
+        device.state = CastController.CastDevice.STATE_CONNECTED;
+        List<CastDevice> devices = new ArrayList<>();
+        devices.add(device);
+        when(mController.getCastDevices()).thenReturn(devices);
+
+        enableWifiAndProcessMessages();
+
+        assertFalse(mCastTile.getState().forceExpandIcon);
+    }
+
+    @Test
+    public void testExpandView_connecting_projection() {
+        CastController.CastDevice connecting = new CastController.CastDevice();
+        connecting.state = CastDevice.STATE_CONNECTING;
+        connecting.name = "Test Casting Device";
+
+        List<CastDevice> devices = new ArrayList<>();
+        devices.add(connecting);
+        when(mController.getCastDevices()).thenReturn(devices);
+
+        enableWifiAndProcessMessages();
+
+        assertFalse(mCastTile.getState().forceExpandIcon);
+    }
+
+    @Test
+    public void testExpandView_casting_mediaRoute() {
+        CastController.CastDevice device = new CastController.CastDevice();
+        device.state = CastDevice.STATE_CONNECTED;
+        device.tag = mock(MediaRouter.RouteInfo.class);
+        List<CastDevice> devices = new ArrayList<>();
+        devices.add(device);
+        when(mController.getCastDevices()).thenReturn(devices);
+
+        enableWifiAndProcessMessages();
+
+        assertTrue(mCastTile.getState().forceExpandIcon);
+    }
+
+    @Test
+    public void testExpandView_connecting_mediaRoute() {
+        CastController.CastDevice connecting = new CastController.CastDevice();
+        connecting.state = CastDevice.STATE_CONNECTING;
+        connecting.tag = mock(RouteInfo.class);
+        connecting.name = "Test Casting Device";
+
+        List<CastDevice> devices = new ArrayList<>();
+        devices.add(connecting);
+        when(mController.getCastDevices()).thenReturn(devices);
+
+        enableWifiAndProcessMessages();
+
+        assertTrue(mCastTile.getState().forceExpandIcon);
+    }
 }
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 a70c2be..8922b43 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
@@ -228,7 +228,9 @@
         mTile.handleClick(null /* view */);
         mTestableLooper.processAllMessages();
 
-        verify(mSpiedContext).startActivity(mIntentCaptor.capture());
+        verify(mActivityStarter).startActivity(mIntentCaptor.capture(), eq(true) /* dismissShade */,
+                (ActivityLaunchAnimator.Controller) eq(null),
+                eq(true) /* showOverLockscreenWhenLocked */);
 
         Intent nextStartedIntent = mIntentCaptor.getValue();
         String walletClassName = "com.android.systemui.wallet.ui.WalletActivity";
@@ -246,7 +248,8 @@
         mTestableLooper.processAllMessages();
 
         verify(mActivityStarter).startActivity(mIntentCaptor.capture(), eq(true) /* dismissShade */,
-                (ActivityLaunchAnimator.Controller) eq(null));
+                (ActivityLaunchAnimator.Controller) eq(null),
+                eq(true) /* showOverLockscreenWhenLocked */);
 
         Intent nextStartedIntent = mIntentCaptor.getValue();
         String walletClassName = "com.android.systemui.wallet.ui.WalletActivity";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
new file mode 100644
index 0000000..fa5f70c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
@@ -0,0 +1,127 @@
+package com.android.systemui.qs.tiles.dialog;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.settingslib.wifi.WifiUtils;
+import com.android.systemui.SysuiTestCase;
+import com.android.wifitrackerlib.WifiEntry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class InternetAdapterTest extends SysuiTestCase {
+
+    private static final String WIFI_TITLE = "Wi-Fi Title";
+    private static final String WIFI_SUMMARY = "Wi-Fi Summary";
+
+    @Mock
+    private WifiEntry mInternetWifiEntry;
+    @Mock
+    private List<WifiEntry> mWifiEntries;
+    @Mock
+    private WifiEntry mWifiEntry;
+    @Mock
+    private InternetDialogController mInternetDialogController;
+    @Mock
+    private WifiUtils.InternetIconInjector mWifiIconInjector;
+
+    private InternetAdapter mInternetAdapter;
+    private InternetAdapter.InternetViewHolder mViewHolder;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mInternetWifiEntry.getTitle()).thenReturn(WIFI_TITLE);
+        when(mInternetWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY);
+        when(mInternetWifiEntry.isDefaultNetwork()).thenReturn(true);
+        when(mInternetWifiEntry.hasInternetAccess()).thenReturn(true);
+        when(mWifiEntry.getTitle()).thenReturn(WIFI_TITLE);
+        when(mWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY);
+
+        mInternetAdapter = new InternetAdapter(mInternetDialogController);
+        mViewHolder = mInternetAdapter.onCreateViewHolder(new LinearLayout(mContext), 0);
+        mInternetAdapter.setWifiEntries(Arrays.asList(mWifiEntry), 1 /* wifiEntriesCount */);
+        mViewHolder.mWifiIconInjector = mWifiIconInjector;
+    }
+
+    @Test
+    public void getItemCount_returnWifiEntriesCount() {
+        for (int i = 0; i < InternetDialogController.MAX_WIFI_ENTRY_COUNT; i++) {
+            mInternetAdapter.setWifiEntries(mWifiEntries, i /* wifiEntriesCount */);
+
+            assertThat(mInternetAdapter.getItemCount()).isEqualTo(i);
+        }
+    }
+
+    @Test
+    public void onBindViewHolder_bindWithOpenWifiNetwork_verifyView() {
+        when(mWifiEntry.getSecurity()).thenReturn(WifiEntry.SECURITY_NONE);
+        mInternetAdapter.onBindViewHolder(mViewHolder, 0);
+
+        assertThat(mViewHolder.mWifiTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mWifiSummaryText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mWifiIcon.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mWifiLockedIcon.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void onBindViewHolder_bindWithSecurityWifiNetwork_verifyView() {
+        when(mWifiEntry.getSecurity()).thenReturn(WifiEntry.SECURITY_PSK);
+        mInternetAdapter.onBindViewHolder(mViewHolder, 0);
+
+        assertThat(mViewHolder.mWifiTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mWifiSummaryText.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mWifiIcon.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mWifiLockedIcon.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void onBindViewHolder_wifiLevelUnreachable_shouldNotGetWifiIcon() {
+        reset(mWifiIconInjector);
+        when(mWifiEntry.getLevel()).thenReturn(WifiEntry.WIFI_LEVEL_UNREACHABLE);
+
+        mInternetAdapter.onBindViewHolder(mViewHolder, 0);
+
+        verify(mWifiIconInjector, never()).getIcon(anyBoolean(), anyInt());
+    }
+
+    @Test
+    public void onBindViewHolder_shouldNotShowXLevelIcon_getIconWithInternet() {
+        when(mWifiEntry.shouldShowXLevelIcon()).thenReturn(false);
+
+        mInternetAdapter.onBindViewHolder(mViewHolder, 0);
+
+        verify(mWifiIconInjector).getIcon(eq(false) /* noInternet */, anyInt());
+    }
+
+    @Test
+    public void onBindViewHolder_shouldShowXLevelIcon_getIconWithNoInternet() {
+        when(mWifiEntry.shouldShowXLevelIcon()).thenReturn(true);
+
+        mInternetAdapter.onBindViewHolder(mViewHolder, 0);
+
+        verify(mWifiIconInjector).getIcon(eq(true) /* noInternet */, anyInt());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
new file mode 100644
index 0000000..cacc409
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
@@ -0,0 +1,491 @@
+package com.android.systemui.qs.tiles.dialog;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.text.TextUtils;
+
+import androidx.annotation.Nullable;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.settingslib.wifi.WifiUtils;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.settings.GlobalSettings;
+import com.android.systemui.util.time.FakeSystemClock;
+import com.android.wifitrackerlib.WifiEntry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class InternetDialogControllerTest extends SysuiTestCase {
+
+    private static final int SUB_ID = 1;
+    private static final String CONNECTED_TITLE = "Connected Wi-Fi Title";
+    private static final String CONNECTED_SUMMARY = "Connected Wi-Fi Summary";
+
+    @Mock
+    private WifiManager mWifiManager;
+    @Mock
+    private TelephonyManager mTelephonyManager;
+    @Mock
+    private SubscriptionManager mSubscriptionManager;
+    @Mock
+    private Handler mHandler;
+    @Mock
+    private ActivityStarter mActivityStarter;
+    @Mock
+    private GlobalSettings mGlobalSettings;
+    @Mock
+    private KeyguardStateController mKeyguardStateController;
+    @Mock
+    private NetworkController.AccessPointController mAccessPointController;
+    @Mock
+    private WifiEntry mConnectedEntry;
+    @Mock
+    private WifiEntry mWifiEntry1;
+    @Mock
+    private WifiEntry mWifiEntry2;
+    @Mock
+    private WifiEntry mWifiEntry3;
+    @Mock
+    private WifiEntry mWifiEntry4;
+    @Mock
+    private ServiceState mServiceState;
+    @Mock
+    private BroadcastDispatcher mBroadcastDispatcher;
+    @Mock
+    private WifiUtils.InternetIconInjector mWifiIconInjector;
+    @Mock
+    InternetDialogController.InternetDialogCallback mInternetDialogCallback;
+
+    private MockInternetDialogController mInternetDialogController;
+    private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+    private List<WifiEntry> mAccessPoints = new ArrayList<>();
+    private List<WifiEntry> mWifiEntries = new ArrayList<>();
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt());
+        when(mKeyguardStateController.isUnlocked()).thenReturn(true);
+        when(mConnectedEntry.isDefaultNetwork()).thenReturn(true);
+        when(mConnectedEntry.hasInternetAccess()).thenReturn(true);
+        when(mWifiEntry1.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_DISCONNECTED);
+        when(mWifiEntry2.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_DISCONNECTED);
+        when(mWifiEntry3.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_DISCONNECTED);
+        when(mWifiEntry4.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_DISCONNECTED);
+        mAccessPoints.add(mConnectedEntry);
+        mAccessPoints.add(mWifiEntry1);
+        when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[]{SUB_ID});
+
+        mInternetDialogController = new MockInternetDialogController(mContext,
+                mock(UiEventLogger.class), mock(ActivityStarter.class), mAccessPointController,
+                mSubscriptionManager, mTelephonyManager, mWifiManager,
+                mock(ConnectivityManager.class), mHandler, mExecutor, mBroadcastDispatcher,
+                mock(KeyguardUpdateMonitor.class), mGlobalSettings, mKeyguardStateController);
+        mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor,
+                mInternetDialogController.mOnSubscriptionsChangedListener);
+        mInternetDialogController.onStart(mInternetDialogCallback, true);
+        mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+        mInternetDialogController.mActivityStarter = mActivityStarter;
+        mInternetDialogController.mWifiIconInjector = mWifiIconInjector;
+    }
+
+    @Test
+    public void getDialogTitleText_withAirplaneModeOn_returnAirplaneMode() {
+        mInternetDialogController.setAirplaneModeEnabled(true);
+
+        assertTrue(TextUtils.equals(mInternetDialogController.getDialogTitleText(),
+                getResourcesString("airplane_mode")));
+    }
+
+    @Test
+    public void getDialogTitleText_withAirplaneModeOff_returnInternet() {
+        mInternetDialogController.setAirplaneModeEnabled(false);
+
+        assertTrue(TextUtils.equals(mInternetDialogController.getDialogTitleText(),
+                getResourcesString("quick_settings_internet_label")));
+    }
+
+    @Test
+    public void getSubtitleText_withAirplaneModeOn_returnNull() {
+        mInternetDialogController.setAirplaneModeEnabled(true);
+
+        assertThat(mInternetDialogController.getSubtitleText(false)).isNull();
+    }
+
+    @Test
+    public void getSubtitleText_withWifiOff_returnWifiIsOff() {
+        mInternetDialogController.setAirplaneModeEnabled(false);
+        when(mWifiManager.isWifiEnabled()).thenReturn(false);
+
+        assertThat(mInternetDialogController.getSubtitleText(false))
+                .isEqualTo(getResourcesString("wifi_is_off"));
+
+        // if the Wi-Fi disallow config, then don't return Wi-Fi related string.
+        mInternetDialogController.mCanConfigWifi = false;
+
+        assertThat(mInternetDialogController.getSubtitleText(false))
+                .isNotEqualTo(getResourcesString("wifi_is_off"));
+    }
+
+    @Test
+    public void getSubtitleText_withNoWifiEntry_returnSearchWifi() {
+        mInternetDialogController.setAirplaneModeEnabled(false);
+        when(mWifiManager.isWifiEnabled()).thenReturn(true);
+        mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
+
+        assertThat(mInternetDialogController.getSubtitleText(true))
+                .isEqualTo(getResourcesString("wifi_empty_list_wifi_on"));
+
+        // if the Wi-Fi disallow config, then don't return Wi-Fi related string.
+        mInternetDialogController.mCanConfigWifi = false;
+
+        assertThat(mInternetDialogController.getSubtitleText(true))
+                .isNotEqualTo(getResourcesString("wifi_empty_list_wifi_on"));
+    }
+
+    @Test
+    public void getSubtitleText_withWifiEntry_returnTapToConnect() {
+        // The preconditions WiFi Entries is already in setUp()
+        mInternetDialogController.setAirplaneModeEnabled(false);
+        when(mWifiManager.isWifiEnabled()).thenReturn(true);
+
+        assertThat(mInternetDialogController.getSubtitleText(false))
+                .isEqualTo(getResourcesString("tap_a_network_to_connect"));
+
+        // if the Wi-Fi disallow config, then don't return Wi-Fi related string.
+        mInternetDialogController.mCanConfigWifi = false;
+
+        assertThat(mInternetDialogController.getSubtitleText(false))
+                .isNotEqualTo(getResourcesString("tap_a_network_to_connect"));
+    }
+
+    @Test
+    public void getSubtitleText_deviceLockedWithWifiOn_returnUnlockToViewNetworks() {
+        mInternetDialogController.setAirplaneModeEnabled(false);
+        when(mWifiManager.isWifiEnabled()).thenReturn(true);
+        when(mKeyguardStateController.isUnlocked()).thenReturn(false);
+
+        assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false),
+                getResourcesString("unlock_to_view_networks")));
+    }
+
+    @Test
+    public void getSubtitleText_withNoService_returnNoNetworksAvailable() {
+        mInternetDialogController.setAirplaneModeEnabled(false);
+        when(mWifiManager.isWifiEnabled()).thenReturn(true);
+        mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
+
+        doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getState();
+        doReturn(mServiceState).when(mTelephonyManager).getServiceState();
+        doReturn(TelephonyManager.DATA_DISCONNECTED).when(mTelephonyManager).getDataState();
+
+        assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false),
+                getResourcesString("all_network_unavailable")));
+    }
+
+    @Test
+    public void getSubtitleText_withMobileDataDisabled_returnNoOtherAvailable() {
+        mInternetDialogController.setAirplaneModeEnabled(false);
+        when(mWifiManager.isWifiEnabled()).thenReturn(true);
+        mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
+
+        doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getState();
+        doReturn(mServiceState).when(mTelephonyManager).getServiceState();
+
+        when(mTelephonyManager.isDataEnabled()).thenReturn(false);
+
+        assertThat(mInternetDialogController.getSubtitleText(false))
+                .isEqualTo(getResourcesString("non_carrier_network_unavailable"));
+
+        // if the Wi-Fi disallow config, then don't return Wi-Fi related string.
+        mInternetDialogController.mCanConfigWifi = false;
+
+        assertThat(mInternetDialogController.getSubtitleText(false))
+                .isNotEqualTo(getResourcesString("non_carrier_network_unavailable"));
+    }
+
+    @Test
+    public void getWifiDetailsSettingsIntent_withNoConnectedEntry_returnNull() {
+        mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
+
+        assertThat(mInternetDialogController.getWifiDetailsSettingsIntent()).isNull();
+    }
+
+    @Test
+    public void getWifiDetailsSettingsIntent_withNoConnectedEntryKey_returnNull() {
+        when(mConnectedEntry.getKey()).thenReturn(null);
+
+        assertThat(mInternetDialogController.getWifiDetailsSettingsIntent()).isNull();
+    }
+
+    @Test
+    public void getWifiDetailsSettingsIntent_withConnectedEntryKey_returnIntent() {
+        when(mConnectedEntry.getKey()).thenReturn("test_key");
+
+        assertThat(mInternetDialogController.getWifiDetailsSettingsIntent()).isNotNull();
+    }
+
+    @Test
+    public void getWifiDrawable_withConnectedEntry_returnIntentIconWithCorrectColor() {
+        final Drawable drawable = mock(Drawable.class);
+        when(mWifiIconInjector.getIcon(anyBoolean(), anyInt())).thenReturn(drawable);
+
+        mInternetDialogController.getInternetWifiDrawable(mConnectedEntry);
+
+        verify(mWifiIconInjector).getIcon(eq(false), anyInt());
+        verify(drawable).setTint(mContext.getColor(R.color.connected_network_primary_color));
+    }
+
+    @Test
+    public void launchWifiNetworkDetailsSetting_withNoConnectedEntry_doNothing() {
+        mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
+
+        mInternetDialogController.launchWifiNetworkDetailsSetting();
+
+        verify(mActivityStarter, never())
+                .postStartActivityDismissingKeyguard(any(Intent.class), anyInt());
+    }
+
+    @Test
+    public void launchWifiNetworkDetailsSetting_withConnectedEntryKey_startActivity() {
+        when(mConnectedEntry.getKey()).thenReturn("test_key");
+
+        mInternetDialogController.launchWifiNetworkDetailsSetting();
+
+        verify(mActivityStarter).postStartActivityDismissingKeyguard(any(Intent.class), anyInt());
+    }
+
+    @Test
+    public void isDeviceLocked_keyguardIsUnlocked_returnFalse() {
+        when(mKeyguardStateController.isUnlocked()).thenReturn(true);
+
+        assertThat(mInternetDialogController.isDeviceLocked()).isFalse();
+    }
+
+    @Test
+    public void isDeviceLocked_keyguardIsLocked_returnTrue() {
+        when(mKeyguardStateController.isUnlocked()).thenReturn(false);
+
+        assertThat(mInternetDialogController.isDeviceLocked()).isTrue();
+    }
+
+    @Test
+    public void onAccessPointsChanged_canNotConfigWifi_doNothing() {
+        reset(mInternetDialogCallback);
+        mInternetDialogController.mCanConfigWifi = false;
+
+        mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
+
+        verify(mInternetDialogCallback, never()).onAccessPointsChanged(any(), any());
+    }
+
+    @Test
+    public void onAccessPointsChanged_nullAccessPoints_callbackBothNull() {
+        reset(mInternetDialogCallback);
+
+        mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
+
+        verify(mInternetDialogCallback)
+                .onAccessPointsChanged(null /* wifiEntries */, null /* connectedEntry */);
+    }
+
+    @Test
+    public void onAccessPointsChanged_oneConnectedEntry_callbackConnectedEntryOnly() {
+        reset(mInternetDialogCallback);
+        mInternetDialogController.setAirplaneModeEnabled(true);
+        mAccessPoints.clear();
+        mAccessPoints.add(mConnectedEntry);
+
+        mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+
+        mWifiEntries.clear();
+        verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
+    }
+
+    @Test
+    public void onAccessPointsChanged_noConnectedEntryAndOneOther_callbackWifiEntriesOnly() {
+        reset(mInternetDialogCallback);
+        mInternetDialogController.setAirplaneModeEnabled(true);
+        mAccessPoints.clear();
+        mAccessPoints.add(mWifiEntry1);
+
+        mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+
+        mWifiEntries.clear();
+        mWifiEntries.add(mWifiEntry1);
+        verify(mInternetDialogCallback)
+                .onAccessPointsChanged(mWifiEntries, null /* connectedEntry */);
+    }
+
+    @Test
+    public void onAccessPointsChanged_oneConnectedEntryAndOneOther_callbackCorrectly() {
+        reset(mInternetDialogCallback);
+        mInternetDialogController.setAirplaneModeEnabled(true);
+        mAccessPoints.clear();
+        mAccessPoints.add(mConnectedEntry);
+        mAccessPoints.add(mWifiEntry1);
+
+        mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+
+        mWifiEntries.clear();
+        mWifiEntries.add(mWifiEntry1);
+        verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
+    }
+
+    @Test
+    public void onAccessPointsChanged_oneConnectedEntryAndTwoOthers_callbackCorrectly() {
+        reset(mInternetDialogCallback);
+        mInternetDialogController.setAirplaneModeEnabled(true);
+        mAccessPoints.clear();
+        mAccessPoints.add(mConnectedEntry);
+        mAccessPoints.add(mWifiEntry1);
+        mAccessPoints.add(mWifiEntry2);
+
+        mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+
+        mWifiEntries.clear();
+        mWifiEntries.add(mWifiEntry1);
+        mWifiEntries.add(mWifiEntry2);
+        verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
+    }
+
+    @Test
+    public void onAccessPointsChanged_oneConnectedEntryAndThreeOthers_callbackCutMore() {
+        reset(mInternetDialogCallback);
+        mInternetDialogController.setAirplaneModeEnabled(true);
+        mAccessPoints.clear();
+        mAccessPoints.add(mConnectedEntry);
+        mAccessPoints.add(mWifiEntry1);
+        mAccessPoints.add(mWifiEntry2);
+        mAccessPoints.add(mWifiEntry3);
+
+        mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+
+        mWifiEntries.clear();
+        mWifiEntries.add(mWifiEntry1);
+        mWifiEntries.add(mWifiEntry2);
+        mWifiEntries.add(mWifiEntry3);
+        verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
+
+        // Turn off airplane mode to has carrier network, then Wi-Fi entries will cut last one.
+        reset(mInternetDialogCallback);
+        mInternetDialogController.setAirplaneModeEnabled(false);
+
+        mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+
+        mWifiEntries.remove(mWifiEntry3);
+        verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
+    }
+
+    @Test
+    public void onAccessPointsChanged_oneConnectedEntryAndFourOthers_callbackCutMore() {
+        reset(mInternetDialogCallback);
+        mInternetDialogController.setAirplaneModeEnabled(true);
+        mAccessPoints.clear();
+        mAccessPoints.add(mConnectedEntry);
+        mAccessPoints.add(mWifiEntry1);
+        mAccessPoints.add(mWifiEntry2);
+        mAccessPoints.add(mWifiEntry3);
+        mAccessPoints.add(mWifiEntry4);
+
+        mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+
+        mWifiEntries.clear();
+        mWifiEntries.add(mWifiEntry1);
+        mWifiEntries.add(mWifiEntry2);
+        mWifiEntries.add(mWifiEntry3);
+        verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
+
+        // Turn off airplane mode to has carrier network, then Wi-Fi entries will cut last one.
+        reset(mInternetDialogCallback);
+        mInternetDialogController.setAirplaneModeEnabled(false);
+
+        mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+
+        mWifiEntries.remove(mWifiEntry3);
+        verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry);
+    }
+
+    private String getResourcesString(String name) {
+        return mContext.getResources().getString(getResourcesId(name));
+    }
+
+    private int getResourcesId(String name) {
+        return mContext.getResources().getIdentifier(name, "string",
+                mContext.getPackageName());
+    }
+
+    private class MockInternetDialogController extends InternetDialogController {
+
+        private GlobalSettings mGlobalSettings;
+        private boolean mIsAirplaneModeOn;
+
+        MockInternetDialogController(Context context, UiEventLogger uiEventLogger,
+                ActivityStarter starter, AccessPointController accessPointController,
+                SubscriptionManager subscriptionManager, TelephonyManager telephonyManager,
+                @Nullable WifiManager wifiManager, ConnectivityManager connectivityManager,
+                @Main Handler handler, @Main Executor mainExecutor,
+                BroadcastDispatcher broadcastDispatcher,
+                KeyguardUpdateMonitor keyguardUpdateMonitor, GlobalSettings globalSettings,
+                KeyguardStateController keyguardStateController) {
+            super(context, uiEventLogger, starter, accessPointController, subscriptionManager,
+                    telephonyManager, wifiManager, connectivityManager, handler, mainExecutor,
+                    broadcastDispatcher, keyguardUpdateMonitor, globalSettings,
+                    keyguardStateController);
+            mGlobalSettings = globalSettings;
+        }
+
+        @Override
+        boolean isAirplaneModeEnabled() {
+            return mIsAirplaneModeOn;
+        }
+
+        public void setAirplaneModeEnabled(boolean enabled) {
+            mIsAirplaneModeOn = enabled;
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
new file mode 100644
index 0000000..fa9c053
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -0,0 +1,292 @@
+package com.android.systemui.qs.tiles.dialog;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.telephony.TelephonyManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.wifitrackerlib.WifiEntry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class InternetDialogTest extends SysuiTestCase {
+
+    private static final String MOBILE_NETWORK_TITLE = "Mobile Title";
+    private static final String MOBILE_NETWORK_SUMMARY = "Mobile Summary";
+    private static final String WIFI_TITLE = "Connected Wi-Fi Title";
+    private static final String WIFI_SUMMARY = "Connected Wi-Fi Summary";
+
+    @Mock
+    private Handler mHandler;
+    @Mock
+    private TelephonyManager mTelephonyManager;
+    @Mock
+    private WifiManager mWifiManager;
+    @Mock
+    private WifiEntry mInternetWifiEntry;
+    @Mock
+    private List<WifiEntry> mWifiEntries;
+    @Mock
+    private InternetAdapter mInternetAdapter;
+    @Mock
+    private InternetDialogController mInternetDialogController;
+
+    private InternetDialog mInternetDialog;
+    private View mDialogView;
+    private View mSubTitle;
+    private LinearLayout mMobileDataToggle;
+    private LinearLayout mWifiToggle;
+    private LinearLayout mConnectedWifi;
+    private RecyclerView mWifiList;
+    private LinearLayout mSeeAll;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt());
+        when(mWifiManager.isWifiEnabled()).thenReturn(true);
+        when(mInternetWifiEntry.getTitle()).thenReturn(WIFI_TITLE);
+        when(mInternetWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY);
+        when(mInternetWifiEntry.isDefaultNetwork()).thenReturn(true);
+        when(mInternetWifiEntry.hasInternetAccess()).thenReturn(true);
+        when(mWifiEntries.size()).thenReturn(1);
+
+        when(mInternetDialogController.getMobileNetworkTitle()).thenReturn(MOBILE_NETWORK_TITLE);
+        when(mInternetDialogController.getMobileNetworkSummary())
+                .thenReturn(MOBILE_NETWORK_SUMMARY);
+        when(mInternetDialogController.getWifiManager()).thenReturn(mWifiManager);
+
+        mInternetDialog = new InternetDialog(mContext, mock(InternetDialogFactory.class),
+                mInternetDialogController, true, true, true, mock(UiEventLogger.class), mHandler);
+        mInternetDialog.mAdapter = mInternetAdapter;
+        mInternetDialog.onAccessPointsChanged(mWifiEntries, mInternetWifiEntry);
+        mInternetDialog.show();
+
+        mDialogView = mInternetDialog.mDialogView;
+        mSubTitle = mDialogView.requireViewById(R.id.internet_dialog_subtitle);
+        mMobileDataToggle = mDialogView.requireViewById(R.id.mobile_network_layout);
+        mWifiToggle = mDialogView.requireViewById(R.id.turn_on_wifi_layout);
+        mConnectedWifi = mDialogView.requireViewById(R.id.wifi_connected_layout);
+        mWifiList = mDialogView.requireViewById(R.id.wifi_list_layout);
+        mSeeAll = mDialogView.requireViewById(R.id.see_all_layout);
+    }
+
+    @After
+    public void tearDown() {
+        mInternetDialog.dismissDialog();
+    }
+
+    @Test
+    public void hideWifiViews_WifiViewsGone() {
+        mInternetDialog.hideWifiViews();
+
+        assertThat(mInternetDialog.mIsProgressBarVisible).isFalse();
+        assertThat(mWifiToggle.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void updateDialog_withApmOn_internetDialogSubTitleGone() {
+        when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
+
+        mInternetDialog.updateDialog();
+
+        assertThat(mSubTitle.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void updateDialog_withApmOff_internetDialogSubTitleVisible() {
+        when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
+
+        mInternetDialog.updateDialog();
+
+        assertThat(mSubTitle.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void updateDialog_withApmOn_mobileDataLayoutGone() {
+        when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
+
+        mInternetDialog.updateDialog();
+
+        assertThat(mMobileDataToggle.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void updateDialog_wifiOnAndHasInternetWifi_showConnectedWifi() {
+        // The preconditions WiFi ON and Internet WiFi are already in setUp()
+        doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
+
+        mInternetDialog.updateDialog();
+
+        assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void updateDialog_wifiOnAndNoConnectedWifi_hideConnectedWifi() {
+        // The precondition WiFi ON is already in setUp()
+        mInternetDialog.onAccessPointsChanged(mWifiEntries, null /* connectedEntry*/);
+        doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
+
+        mInternetDialog.updateDialog();
+
+        assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void updateDialog_wifiOnAndNoWifiList_hideWifiListAndSeeAll() {
+        // The precondition WiFi ON is already in setUp()
+        mInternetDialog.onAccessPointsChanged(null /* wifiEntries */, mInternetWifiEntry);
+
+        mInternetDialog.updateDialog();
+
+        assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void updateDialog_wifiOnAndHasWifiList_showWifiListAndSeeAll() {
+        // The preconditions WiFi ON and WiFi entries are already in setUp()
+
+        mInternetDialog.updateDialog();
+
+        assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mSeeAll.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void updateDialog_deviceLockedAndHasInternetWifi_showHighlightWifiToggle() {
+        // The preconditions WiFi ON and Internet WiFi are already in setUp()
+        when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
+
+        mInternetDialog.updateDialog();
+
+        assertThat(mWifiToggle.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mWifiToggle.getBackground()).isNotNull();
+    }
+
+    @Test
+    public void updateDialog_deviceLockedAndHasInternetWifi_hideConnectedWifi() {
+        // The preconditions WiFi ON and Internet WiFi are already in setUp()
+        when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
+
+        mInternetDialog.updateDialog();
+
+        assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void updateDialog_deviceLockedAndHasWifiList_hideWifiListAndSeeAll() {
+        // The preconditions WiFi entries are already in setUp()
+        when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
+
+        mInternetDialog.updateDialog();
+
+        assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void onClickSeeMoreButton_clickSeeAll_verifyLaunchNetworkSetting() {
+        mSeeAll.performClick();
+
+        verify(mInternetDialogController).launchNetworkSetting();
+    }
+
+    @Test
+    public void showProgressBar_wifiDisabled_hideProgressBar() {
+        Mockito.reset(mHandler);
+        when(mWifiManager.isWifiEnabled()).thenReturn(false);
+
+        mInternetDialog.showProgressBar();
+
+        assertThat(mInternetDialog.mIsProgressBarVisible).isFalse();
+        verify(mHandler, never()).postDelayed(any(Runnable.class), anyLong());
+    }
+
+    @Test
+    public void showProgressBar_deviceLocked_hideProgressBar() {
+        Mockito.reset(mHandler);
+        when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
+
+        mInternetDialog.showProgressBar();
+
+        assertThat(mInternetDialog.mIsProgressBarVisible).isFalse();
+        verify(mHandler, never()).postDelayed(any(Runnable.class), anyLong());
+    }
+
+    @Test
+    public void showProgressBar_wifiEnabledWithWifiEntry_showProgressBarThenHide() {
+        Mockito.reset(mHandler);
+        when(mWifiManager.isWifiEnabled()).thenReturn(true);
+
+        mInternetDialog.showProgressBar();
+
+        // Show progress bar
+        assertThat(mInternetDialog.mIsProgressBarVisible).isTrue();
+
+        ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        verify(mHandler).postDelayed(runnableCaptor.capture(),
+                eq(InternetDialog.PROGRESS_DELAY_MS));
+        runnableCaptor.getValue().run();
+
+        // Then hide progress bar
+        assertThat(mInternetDialog.mIsProgressBarVisible).isFalse();
+    }
+
+    @Test
+    public void showProgressBar_wifiEnabledWithoutWifiEntries_showProgressBarThenHideSearch() {
+        Mockito.reset(mHandler);
+        when(mWifiManager.isWifiEnabled()).thenReturn(true);
+        mInternetDialog.onAccessPointsChanged(null /* wifiEntries */, null /* connectedEntry*/);
+
+        mInternetDialog.showProgressBar();
+
+        // Show progress bar
+        assertThat(mInternetDialog.mIsProgressBarVisible).isTrue();
+
+        ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        verify(mHandler).postDelayed(runnableCaptor.capture(),
+                eq(InternetDialog.PROGRESS_DELAY_MS));
+        runnableCaptor.getValue().run();
+
+        // Then hide searching sub-title only
+        assertThat(mInternetDialog.mIsProgressBarVisible).isTrue();
+        assertThat(mInternetDialog.mIsSearchingHidden).isTrue();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
index 3a4bc69..670a130 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
@@ -26,7 +26,6 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 
-import android.content.Context;
 import android.graphics.Rect;
 import android.os.RemoteException;
 import android.testing.AndroidTestingRunner;
@@ -36,7 +35,6 @@
 import android.view.ScrollCaptureResponse;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult;
@@ -83,7 +81,7 @@
                 /* taskId */ anyInt(), any(IScrollCaptureResponseListener.class));
 
         // Create client
-        ScrollCaptureClient client = new ScrollCaptureClient(mContext, mWm);
+        ScrollCaptureClient client = new ScrollCaptureClient(mWm, Runnable::run, mContext);
 
         // Request scroll capture
         ListenableFuture<ScrollCaptureResponse> requestFuture =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java
index 10c878a..6f081c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java
@@ -34,6 +34,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.logging.testing.UiEventLoggerFake;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.screenshot.ScrollCaptureClient.Session;
 
@@ -274,7 +275,8 @@
             when(client.start(/* response */ any(), /* maxPages */ anyFloat()))
                     .thenReturn(immediateFuture(session));
             return new ScrollCaptureController(context, context.getMainExecutor(),
-                    client, new ImageTileSet(context.getMainThreadHandler()));
+                    client, new ImageTileSet(context.getMainThreadHandler()),
+                    new UiEventLoggerFake());
         }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt
index e0187bd..bceb928 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt
@@ -108,15 +108,6 @@
     }
 
     @Test
-    fun testNullMirrorControllerNotTrackingTouch() {
-        mController.setMirrorControllerAndMirror(null)
-
-        verify(brightnessSliderView, never()).max
-        verify(brightnessSliderView, never()).value
-        verify(brightnessSliderView).setOnDispatchTouchEventListener(isNull())
-    }
-
-    @Test
     fun testNullMirrorNotTrackingTouch() {
         whenever(mirrorController.toggleSlider).thenReturn(null)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt
new file mode 100644
index 0000000..a9c6a53
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.shared.animation
+
+import android.graphics.Point
+import android.test.suitebuilder.annotation.SmallTest
+import android.testing.AndroidTestingRunner
+import android.view.Display
+import android.view.Surface.ROTATION_0
+import android.view.Surface.ROTATION_90
+import android.view.View
+import android.view.WindowManager
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.any
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.spy
+import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class UnfoldMoveFromCenterAnimatorTest : SysuiTestCase() {
+
+    @Mock
+    private lateinit var windowManager: WindowManager
+
+    @get:Rule
+    val mockito = MockitoJUnit.rule()
+
+    private lateinit var animator: UnfoldMoveFromCenterAnimator
+
+    @Before
+    fun before() {
+        animator = UnfoldMoveFromCenterAnimator(windowManager)
+    }
+
+    @Test
+    fun testRegisterViewOnTheLeftOfVerticalFold_halfProgress_viewTranslatedToTheRight() {
+        givenScreen(width = 100, height = 100, rotation = ROTATION_0)
+        val view = createView(x = 20)
+        animator.registerViewForAnimation(view)
+        animator.onTransitionStarted()
+
+        animator.onTransitionProgress(0.5f)
+
+        // Positive translationX -> translated to the right
+        assertThat(view.translationX).isWithin(0.1f).of(3.75f)
+    }
+
+    @Test
+    fun testRegisterViewOnTheLeftOfVerticalFold_zeroProgress_viewTranslatedToTheRight() {
+        givenScreen(width = 100, height = 100, rotation = ROTATION_0)
+        val view = createView(x = 20)
+        animator.registerViewForAnimation(view)
+        animator.onTransitionStarted()
+
+        animator.onTransitionProgress(0f)
+
+        // Positive translationX -> translated to the right
+        assertThat(view.translationX).isWithin(0.1f).of(7.5f)
+    }
+
+    @Test
+    fun testRegisterViewOnTheLeftOfVerticalFold_fullProgress_viewTranslatedToTheOriginalPosition() {
+        givenScreen(width = 100, height = 100, rotation = ROTATION_0)
+        val view = createView(x = 20)
+        animator.registerViewForAnimation(view)
+        animator.onTransitionStarted()
+
+        animator.onTransitionProgress(1f)
+
+        assertThat(view.translationX).isEqualTo(0f)
+    }
+
+    @Test
+    fun testRegisterViewAndUnregister_halfProgress_viewIsNotUpdated() {
+        givenScreen(width = 100, height = 100, rotation = ROTATION_0)
+        val view = createView(x = 20)
+        animator.registerViewForAnimation(view)
+        animator.onTransitionStarted()
+        animator.clearRegisteredViews()
+
+        animator.onTransitionProgress(0.5f)
+
+        assertThat(view.translationX).isEqualTo(0f)
+    }
+
+    @Test
+    fun testRegisterViewUpdateProgressAndUnregister_halfProgress_viewIsNotUpdated() {
+        givenScreen(width = 100, height = 100, rotation = ROTATION_0)
+        val view = createView(x = 20)
+        animator.registerViewForAnimation(view)
+        animator.onTransitionStarted()
+        animator.onTransitionProgress(0.2f)
+        animator.clearRegisteredViews()
+
+        animator.onTransitionProgress(0.5f)
+
+        assertThat(view.translationX).isEqualTo(0f)
+    }
+
+    @Test
+    fun testRegisterViewOnTheTopOfHorizontalFold_halfProgress_viewTranslatedToTheBottom() {
+        givenScreen(width = 100, height = 100, rotation = ROTATION_90)
+        val view = createView(y = 20)
+        animator.registerViewForAnimation(view)
+        animator.onTransitionStarted()
+
+        animator.onTransitionProgress(0.5f)
+
+        // Positive translationY -> translated to the bottom
+        assertThat(view.translationY).isWithin(0.1f).of(3.75f)
+    }
+
+    private fun createView(
+        x: Int = 0,
+        y: Int = 0,
+        width: Int = 10,
+        height: Int = 10,
+        translationX: Float = 0f,
+        translationY: Float = 0f
+    ): View {
+        val view = spy(View(context))
+        doAnswer {
+            val location = (it.arguments[0] as IntArray)
+            location[0] = x
+            location[1] = y
+            Unit
+        }.`when`(view).getLocationOnScreen(any())
+
+        whenever(view.width).thenReturn(width)
+        whenever(view.height).thenReturn(height)
+
+        return view.apply {
+            setTranslationX(translationX)
+            setTranslationY(translationY)
+        }
+    }
+
+    private fun givenScreen(width: Int = 100,
+                            height: Int = 100,
+                            rotation: Int = ROTATION_0) {
+        val display = mock(Display::class.java)
+        whenever(display.getSize(any())).thenAnswer {
+            val size = (it.arguments[0] as Point)
+            size.set(width, height)
+            Unit
+        }
+        whenever(display.rotation).thenReturn(rotation)
+        whenever(windowManager.defaultDisplay).thenReturn(display)
+
+        animator.updateDisplayProperties()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
index 325d540..790b4dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
@@ -15,7 +15,6 @@
 package com.android.systemui.shared.plugins;
 
 import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.Matchers.any;
@@ -32,33 +31,32 @@
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.ContextWrapper;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
-import android.os.HandlerThread;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import androidx.test.annotation.UiThreadTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.SysuiTestableContext;
 import com.android.systemui.plugins.Plugin;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.annotations.Requires;
-import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
 import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
+import org.mockito.stubbing.Answer;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -68,63 +66,55 @@
 @RunWith(AndroidJUnit4.class)
 public class PluginInstanceManagerTest extends SysuiTestCase {
 
-    private static final String WHITELISTED_PACKAGE = "com.android.systemui";
-    // Static since the plugin needs to be generated by the PluginInstanceManager using newInstance.
-    private static Plugin sMockPlugin;
+    private static final String PRIVILEGED_PACKAGE = "com.android.systemui.shared.plugins";
+    private TestPlugin mMockPlugin;
 
-    private HandlerThread mHandlerThread;
-    private Context mContextWrapper;
     private PackageManager mMockPm;
-    private PluginListener mMockListener;
-    private PluginInstanceManager mPluginInstanceManager;
-    private PluginManagerImpl mMockManager;
+    private PluginListener<Plugin> mMockListener;
+    private PluginInstanceManager<Plugin> mPluginInstanceManager;
     private VersionInfo mMockVersionInfo;
     private PluginEnabler mMockEnabler;
     ComponentName mTestPluginComponentName =
-            new ComponentName(WHITELISTED_PACKAGE, TestPlugin.class.getName());
+            new ComponentName(PRIVILEGED_PACKAGE, TestPlugin.class.getName());
+    private PluginInitializer mInitializer;
+    private final FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+    NotificationManager mNotificationManager;
+    private PluginInstanceManager.Factory mInstanceManagerFactory;
+    private final PluginInstanceManager.InstanceFactory<Plugin> mPluginInstanceFactory =
+            new PluginInstanceManager.InstanceFactory<Plugin>() {
+        @Override
+        Plugin create(Class cls) {
+            return mMockPlugin;
+        }
+    };
 
     @Before
     public void setup() throws Exception {
-        mHandlerThread = new HandlerThread("test_thread");
-        mHandlerThread.start();
-        mContextWrapper = new MyContextWrapper(getContext());
+        mContext = new MyContextWrapper(mContext);
         mMockPm = mock(PackageManager.class);
         mMockListener = mock(PluginListener.class);
-        mMockManager = mock(PluginManagerImpl.class);
-        when(mMockManager.getClassLoader(any())).thenReturn(getClass().getClassLoader());
         mMockEnabler = mock(PluginEnabler.class);
-        when(mMockManager.getPluginEnabler()).thenReturn(mMockEnabler);
         mMockVersionInfo = mock(VersionInfo.class);
-        mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
-                mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo,
-                mMockManager, true, new String[0]);
-        sMockPlugin = mock(Plugin.class);
-        when(sMockPlugin.getVersion()).thenReturn(1);
-    }
+        mInitializer = mock(PluginInitializer.class);
+        mNotificationManager = mock(NotificationManager.class);
+        mMockPlugin = mock(TestPlugin.class);
+        mInstanceManagerFactory = new PluginInstanceManager.Factory(getContext(), mMockPm,
+                mFakeExecutor, mFakeExecutor, mInitializer, mNotificationManager, mMockEnabler,
+                new ArrayList<>())
+                .setInstanceFactory(mPluginInstanceFactory);
 
-    @After
-    public void tearDown() throws Exception {
-        mHandlerThread.quit();
-        sMockPlugin = null;
-    }
-
-    @UiThreadTest
-    @Test
-    public void testGetPlugin() throws Exception {
-        setupFakePmQuery();
-        PluginInfo p = mPluginInstanceManager.getPlugin();
-        assertNotNull(p.mPlugin);
-        verify(sMockPlugin).onCreate(any(), any());
+        mPluginInstanceManager = mInstanceManagerFactory.create("myAction", mMockListener,
+                true, mMockVersionInfo, true);
+        when(mMockPlugin.getVersion()).thenReturn(1);
     }
 
     @Test
-    public void testNoPlugins() throws Exception {
+    public void testNoPlugins() {
         when(mMockPm.queryIntentServices(any(), anyInt())).thenReturn(
                 Collections.emptyList());
         mPluginInstanceManager.loadAll();
 
-        waitForIdleSync(mPluginInstanceManager.mPluginHandler);
-        waitForIdleSync(mPluginInstanceManager.mMainHandler);
+        mFakeExecutor.runAllReady();
 
         verify(mMockListener, never()).onPluginConnected(any(), any());
     }
@@ -134,7 +124,7 @@
         createPlugin();
 
         // Verify startup lifecycle
-        verify(sMockPlugin).onCreate(ArgumentCaptor.forClass(Context.class).capture(),
+        verify(mMockPlugin).onCreate(ArgumentCaptor.forClass(Context.class).capture(),
                 ArgumentCaptor.forClass(Context.class).capture());
         verify(mMockListener).onPluginConnected(any(), any());
     }
@@ -145,45 +135,41 @@
 
         mPluginInstanceManager.destroy();
 
-        waitForIdleSync(mPluginInstanceManager.mPluginHandler);
-        waitForIdleSync(mPluginInstanceManager.mMainHandler);
+        mFakeExecutor.runAllReady();
+
 
         // Verify shutdown lifecycle
         verify(mMockListener).onPluginDisconnected(ArgumentCaptor.forClass(Plugin.class).capture());
-        verify(sMockPlugin).onDestroy();
+        verify(mMockPlugin).onDestroy();
     }
 
     @Test
     public void testIncorrectVersion() throws Exception {
-        NotificationManager nm = mock(NotificationManager.class);
-        mContext.addMockSystemService(Context.NOTIFICATION_SERVICE, nm);
         setupFakePmQuery();
         doThrow(new InvalidVersionException("", false)).when(mMockVersionInfo).checkVersion(any());
 
         mPluginInstanceManager.loadAll();
 
-        waitForIdleSync(mPluginInstanceManager.mPluginHandler);
-        waitForIdleSync(mPluginInstanceManager.mMainHandler);
+        mFakeExecutor.runAllReady();
 
         // Plugin shouldn't be connected because it is the wrong version.
         verify(mMockListener, never()).onPluginConnected(any(), any());
-        verify(nm).notify(eq(SystemMessage.NOTE_PLUGIN), any());
+        verify(mNotificationManager).notify(eq(SystemMessage.NOTE_PLUGIN), any());
     }
 
     @Test
     public void testReloadOnChange() throws Exception {
         createPlugin(); // Get into valid created state.
 
-        mPluginInstanceManager.onPackageChange("com.android.systemui");
+        mPluginInstanceManager.onPackageChange(PRIVILEGED_PACKAGE);
 
-        waitForIdleSync(mPluginInstanceManager.mPluginHandler);
-        waitForIdleSync(mPluginInstanceManager.mMainHandler);
+        mFakeExecutor.runAllReady();
 
         // Verify the old one was destroyed.
         verify(mMockListener).onPluginDisconnected(ArgumentCaptor.forClass(Plugin.class).capture());
-        verify(sMockPlugin).onDestroy();
+        verify(mMockPlugin).onDestroy();
         // Also verify we got a second onCreate.
-        verify(sMockPlugin, Mockito.times(2)).onCreate(
+        verify(mMockPlugin, Mockito.times(2)).onCreate(
                 ArgumentCaptor.forClass(Context.class).capture(),
                 ArgumentCaptor.forClass(Context.class).capture());
         verify(mMockListener, Mockito.times(2)).onPluginConnected(any(), any());
@@ -192,35 +178,35 @@
     @Test
     public void testNonDebuggable() throws Exception {
         // Create a version that thinks the build is not debuggable.
-        mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
-                mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo,
-                mMockManager, false, new String[0]);
+        mPluginInstanceManager = mInstanceManagerFactory.create("myAction", mMockListener,
+                true, mMockVersionInfo, false);
         setupFakePmQuery();
 
         mPluginInstanceManager.loadAll();
 
-        waitForIdleSync(mPluginInstanceManager.mPluginHandler);
-        waitForIdleSync(mPluginInstanceManager.mMainHandler);;
+        mFakeExecutor.runAllReady();
 
         // Non-debuggable build should receive no plugins.
         verify(mMockListener, never()).onPluginConnected(any(), any());
     }
 
     @Test
-    public void testNonDebuggable_whitelist() throws Exception {
+    public void testNonDebuggable_privileged() throws Exception {
         // Create a version that thinks the build is not debuggable.
-        mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
-                mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo,
-                mMockManager, false, new String[] {WHITELISTED_PACKAGE});
+        PluginInstanceManager.Factory factory = new PluginInstanceManager.Factory(getContext(),
+                mMockPm, mFakeExecutor, mFakeExecutor, mInitializer, mNotificationManager,
+                mMockEnabler, Collections.singletonList(PRIVILEGED_PACKAGE));
+        factory.setInstanceFactory(mPluginInstanceFactory);
+        mPluginInstanceManager = factory.create("myAction", mMockListener,
+                true, mMockVersionInfo, false);
         setupFakePmQuery();
 
         mPluginInstanceManager.loadAll();
 
-        waitForIdleSync(mPluginInstanceManager.mPluginHandler);
-        waitForIdleSync(mPluginInstanceManager.mMainHandler);
+        mFakeExecutor.runAllReady();
 
         // Verify startup lifecycle
-        verify(sMockPlugin).onCreate(ArgumentCaptor.forClass(Context.class).capture(),
+        verify(mMockPlugin).onCreate(ArgumentCaptor.forClass(Context.class).capture(),
                 ArgumentCaptor.forClass(Context.class).capture());
         verify(mMockListener).onPluginConnected(any(), any());
     }
@@ -253,9 +239,13 @@
 
     @Test
     public void testDisableWhitelisted() throws Exception {
-        mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
-                mMockListener, true, mHandlerThread.getLooper(), mMockVersionInfo,
-                mMockManager, false, new String[] {WHITELISTED_PACKAGE});
+        PluginInstanceManager.Factory factory = new PluginInstanceManager.Factory(getContext(),
+                mMockPm, mFakeExecutor, mFakeExecutor, mInitializer, mNotificationManager,
+                mMockEnabler, Collections.singletonList(PRIVILEGED_PACKAGE));
+        factory.setInstanceFactory(mPluginInstanceFactory);
+        mPluginInstanceManager = factory.create("myAction", mMockListener,
+                true, mMockVersionInfo, false);
+
         createPlugin(); // Get into valid created state.
 
         mPluginInstanceManager.disableAll();
@@ -280,9 +270,12 @@
         when(mMockPm.checkPermission(Mockito.anyString(), Mockito.anyString())).thenReturn(
                 PackageManager.PERMISSION_GRANTED);
 
-        ApplicationInfo appInfo = getContext().getApplicationInfo();
-        when(mMockPm.getApplicationInfo(Mockito.anyString(), Mockito.anyInt())).thenReturn(
-                appInfo);
+        when(mMockPm.getApplicationInfo(Mockito.anyString(), anyInt())).thenAnswer(
+                (Answer<ApplicationInfo>) invocation -> {
+                    ApplicationInfo appInfo = getContext().getApplicationInfo();
+                    appInfo.packageName = invocation.getArgument(0);
+                    return appInfo;
+                });
         when(mMockEnabler.isEnabled(mTestPluginComponentName)).thenReturn(true);
     }
 
@@ -291,12 +284,11 @@
 
         mPluginInstanceManager.loadAll();
 
-        waitForIdleSync(mPluginInstanceManager.mPluginHandler);
-        waitForIdleSync(mPluginInstanceManager.mMainHandler);
+        mFakeExecutor.runAllReady();
     }
 
     // Real context with no registering/unregistering of receivers.
-    private static class MyContextWrapper extends ContextWrapper {
+    private static class MyContextWrapper extends SysuiTestableContext {
         public MyContextWrapper(Context base) {
             super(base);
         }
@@ -322,17 +314,15 @@
     public static class TestPlugin implements Plugin {
         @Override
         public int getVersion() {
-            return sMockPlugin.getVersion();
+            return 1;
         }
 
         @Override
         public void onCreate(Context sysuiContext, Context pluginContext) {
-            sMockPlugin.onCreate(sysuiContext, pluginContext);
         }
 
         @Override
         public void onDestroy() {
-            sMockPlugin.onDestroy();
         }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
index 536c043..4590dd8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
@@ -13,10 +13,8 @@
  */
 package com.android.systemui.shared.plugins;
 
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -30,19 +28,13 @@
 import android.net.Uri;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.Plugin;
-import com.android.systemui.plugins.PluginEnablerImpl;
-import com.android.systemui.plugins.PluginInitializerImpl;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.annotations.ProvidesInterface;
-import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
-import com.android.systemui.shared.plugins.PluginManagerImpl.PluginInstanceManagerFactory;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -51,19 +43,24 @@
 import org.mockito.Mockito;
 
 import java.lang.Thread.UncaughtExceptionHandler;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Optional;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper
 public class PluginManagerTest extends SysuiTestCase {
 
-    private static final String WHITELISTED_PACKAGE = "com.android.systemui";
+    private static final String PRIVILEGED_PACKAGE = "com.android.systemui";
 
-    private PluginInstanceManagerFactory mMockFactory;
-    private PluginInstanceManager mMockPluginInstance;
+    private PluginInstanceManager.Factory mMockFactory;
+    private PluginInstanceManager<Plugin> mMockPluginInstance;
     private PluginManagerImpl mPluginManager;
-    private PluginListener mMockListener;
+    private PluginListener<?> mMockListener;
     private PackageManager mMockPackageManager;
+    private PluginEnabler mPluginEnabler;
+    private PluginPrefs mPluginPrefs;
 
     private UncaughtExceptionHandler mRealExceptionHandler;
     private UncaughtExceptionHandler mMockExceptionHandler;
@@ -71,44 +68,25 @@
 
     @Before
     public void setup() throws Exception {
-        mDependency.injectTestDependency(Dependency.BG_LOOPER,
-                TestableLooper.get(this).getLooper());
         mRealExceptionHandler = Thread.getUncaughtExceptionPreHandler();
         mMockExceptionHandler = mock(UncaughtExceptionHandler.class);
-        mMockFactory = mock(PluginInstanceManagerFactory.class);
+        mMockFactory = mock(PluginInstanceManager.Factory.class);
         mMockPluginInstance = mock(PluginInstanceManager.class);
-        when(mMockFactory.createPluginInstanceManager(Mockito.any(), Mockito.any(), Mockito.any(),
-                Mockito.anyBoolean(), Mockito.any(), Mockito.any(), Mockito.any()))
+        mPluginEnabler = mock(PluginEnabler.class);
+        mPluginPrefs = mock(PluginPrefs.class);
+        when(mMockFactory.create(any(), any(), Mockito.anyBoolean(), any(), Mockito.anyBoolean()))
                 .thenReturn(mMockPluginInstance);
 
         mMockPackageManager = mock(PackageManager.class);
         mPluginManager = new PluginManagerImpl(
                 getContext(), mMockFactory, true,
-                mMockExceptionHandler, new PluginInitializerImpl() {
-                    @Override
-                    public String[] getWhitelistedPlugins(Context context) {
-                        return new String[0];
-                    }
+                Optional.of(mMockExceptionHandler), mPluginEnabler,
+                mPluginPrefs, new ArrayList<>());
 
-                    @Override
-                    public PluginEnabler getPluginEnabler(Context context) {
-                        return new PluginEnablerImpl(context, mMockPackageManager);
-                    }
-        });
         resetExceptionHandler();
         mMockListener = mock(PluginListener.class);
     }
 
-    @RunWithLooper(setAsMainLooper = true)
-    @Test
-    public void testOneShot() {
-        Plugin mockPlugin = mock(Plugin.class);
-        when(mMockPluginInstance.getPlugin()).thenReturn(new PluginInfo(null, null, mockPlugin,
-                null, null));
-        Plugin result = mPluginManager.getOneShotPlugin("myAction", TestPlugin.class);
-        assertSame(mockPlugin, result);
-    }
-
     @Test
     public void testAddListener() {
         mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
@@ -126,53 +104,49 @@
 
     @Test
     @RunWithLooper(setAsMainLooper = true)
-    public void testNonDebuggable_noWhitelist() {
-        mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, false,
-                mMockExceptionHandler, new PluginInitializerImpl() {
-            @Override
-            public String[] getWhitelistedPlugins(Context context) {
-                return new String[0];
-            }
-        });
+    public void testNonDebuggable_nonPrivileged() {
+        mPluginManager = new PluginManagerImpl(
+                getContext(), mMockFactory, false,
+                Optional.of(mMockExceptionHandler), mPluginEnabler,
+                mPluginPrefs, new ArrayList<>());
         resetExceptionHandler();
 
         String sourceDir = "myPlugin";
         ApplicationInfo applicationInfo = new ApplicationInfo();
         applicationInfo.sourceDir = sourceDir;
-        applicationInfo.packageName = WHITELISTED_PACKAGE;
+        applicationInfo.packageName = PRIVILEGED_PACKAGE;
         mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
-        assertNull(mPluginManager.getOneShotPlugin(sourceDir, TestPlugin.class));
-        assertNull(mPluginManager.getClassLoader(applicationInfo));
+        verify(mMockFactory).create(eq("myAction"), eq(mMockListener), eq(false),
+                any(VersionInfo.class), eq(false));
+        verify(mMockPluginInstance).loadAll();
     }
 
     @Test
     @RunWithLooper(setAsMainLooper = true)
-    public void testNonDebuggable_whitelistedPkg() {
-        mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, false,
-                mMockExceptionHandler, new PluginInitializerImpl() {
-            @Override
-            public String[] getWhitelistedPlugins(Context context) {
-                return new String[] {WHITELISTED_PACKAGE};
-            }
-        });
+    public void testNonDebuggable_privilegedPackage() {
+        mPluginManager = new PluginManagerImpl(
+                getContext(), mMockFactory, false,
+                Optional.of(mMockExceptionHandler), mPluginEnabler,
+                mPluginPrefs, Collections.singletonList(PRIVILEGED_PACKAGE));
         resetExceptionHandler();
 
         String sourceDir = "myPlugin";
-        ApplicationInfo whiteListedApplicationInfo = new ApplicationInfo();
-        whiteListedApplicationInfo.sourceDir = sourceDir;
-        whiteListedApplicationInfo.packageName = WHITELISTED_PACKAGE;
+        ApplicationInfo privilegedApplicationInfo = new ApplicationInfo();
+        privilegedApplicationInfo.sourceDir = sourceDir;
+        privilegedApplicationInfo.packageName = PRIVILEGED_PACKAGE;
         ApplicationInfo invalidApplicationInfo = new ApplicationInfo();
         invalidApplicationInfo.sourceDir = sourceDir;
         invalidApplicationInfo.packageName = "com.android.invalidpackage";
         mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
-        assertNotNull(mPluginManager.getClassLoader(whiteListedApplicationInfo));
-        assertNull(mPluginManager.getClassLoader(invalidApplicationInfo));
+        verify(mMockFactory).create(eq("myAction"), eq(mMockListener), eq(false),
+                any(VersionInfo.class), eq(false));
+        verify(mMockPluginInstance).loadAll();
     }
 
     @Test
     public void testExceptionHandler_foundPlugin() {
         mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
-        when(mMockPluginInstance.checkAndDisable(Mockito.any())).thenReturn(true);
+        when(mMockPluginInstance.checkAndDisable(any())).thenReturn(true);
 
         mPluginExceptionHandler.uncaughtException(Thread.currentThread(), new Throwable());
 
@@ -187,7 +161,7 @@
     @Test
     public void testExceptionHandler_noFoundPlugin() {
         mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
-        when(mMockPluginInstance.checkAndDisable(Mockito.any())).thenReturn(false);
+        when(mMockPluginInstance.checkAndDisable(any())).thenReturn(false);
 
         mPluginExceptionHandler.uncaughtException(Thread.currentThread(), new Throwable());
 
@@ -211,9 +185,7 @@
         intent.setData(Uri.parse("package://" + testComponent.flattenToString()));
         mPluginManager.onReceive(mContext, intent);
         verify(nm).cancel(eq(testComponent.getClassName()), eq(SystemMessage.NOTE_PLUGIN));
-        verify(mMockPackageManager).setComponentEnabledSetting(eq(testComponent),
-                eq(PackageManager.COMPONENT_ENABLED_STATE_DISABLED),
-                eq(PackageManager.DONT_KILL_APP));
+        verify(mPluginEnabler).setDisabled(testComponent, PluginEnabler.DISABLED_INVALID_VERSION);
     }
 
     private void resetExceptionHandler() {
@@ -223,8 +195,8 @@
     }
 
     @ProvidesInterface(action = TestPlugin.ACTION, version = TestPlugin.VERSION)
-    public static interface TestPlugin extends Plugin {
-        public static final String ACTION = "testAction";
-        public static final int VERSION = 1;
+    public interface TestPlugin extends Plugin {
+        String ACTION = "testAction";
+        int VERSION = 1;
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index f5ce673..4c90063 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -111,6 +111,7 @@
     private static final ComponentName DEVICE_OWNER_COMPONENT = new ComponentName("com.android.foo",
             "bar");
 
+    private String mKeyguardTryFingerprintMsg;
     private String mDisclosureWithOrganization;
     private String mDisclosureGeneric;
     private String mFinancedDisclosureWithOrganization;
@@ -182,6 +183,7 @@
         mContext.addMockSystemService(UserManager.class, mUserManager);
         mContext.addMockSystemService(Context.TRUST_SERVICE, mock(TrustManager.class));
         mContext.addMockSystemService(Context.FINGERPRINT_SERVICE, mock(FingerprintManager.class));
+        mKeyguardTryFingerprintMsg = mContext.getString(R.string.keyguard_try_fingerprint);
         mDisclosureWithOrganization = mContext.getString(R.string.do_disclosure_with_name,
                 ORGANIZATION_NAME);
         mDisclosureGeneric = mContext.getString(R.string.do_disclosure_generic);
@@ -677,6 +679,34 @@
         verifyTransientMessage(message);
     }
 
+    @Test
+    public void faceAuthMessageSuppressed() {
+        createController();
+        String faceHelpMsg = "Face auth help message";
+
+        // GIVEN state of showing message when keyguard screen is on
+        when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
+        when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
+        when(mKeyguardUpdateMonitor.isScreenOn()).thenReturn(true);
+
+        // GIVEN fingerprint is also running (not udfps)
+        when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
+        when(mKeyguardUpdateMonitor.isUdfpsAvailable()).thenReturn(false);
+
+        mController.setVisible(true);
+
+        // WHEN a face help message comes in
+        mController.getKeyguardCallback().onBiometricHelp(
+                KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED, faceHelpMsg,
+                BiometricSourceType.FACE);
+
+        // THEN "try fingerprint" message appears (and not the face help message)
+        verifyTransientMessage(mKeyguardTryFingerprintMsg);
+
+        // THEN the face help message is still announced for a11y
+        verify(mIndicationAreaBottom).announceForAccessibility(eq(faceHelpMsg));
+    }
+
     private void sendUpdateDisclosureBroadcast() {
         mBroadcastReceiver.onReceive(mContext, new Intent());
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt
new file mode 100644
index 0000000..97fe25d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 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
+
+import android.testing.AndroidTestingRunner
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.util.function.Consumer
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class LightRevealScrimTest : SysuiTestCase() {
+
+  private lateinit var scrim: LightRevealScrim
+  private var isOpaque = false
+
+  @Before
+  fun setUp() {
+    scrim = LightRevealScrim(context, null)
+    scrim.isScrimOpaqueChangedListener = Consumer { opaque ->
+      isOpaque = opaque
+    }
+    scrim.revealAmount = 0f
+    assertTrue("Scrim is not opaque in initial setup", scrim.isScrimOpaque)
+  }
+
+  @Test
+  fun testAlphaSetsOpaque() {
+    scrim.alpha = 0.5f
+    assertFalse("Scrim is opaque even though alpha is set", scrim.isScrimOpaque)
+  }
+
+  @Test
+  fun testVisibilitySetsOpaque() {
+    scrim.visibility = View.INVISIBLE
+    assertFalse("Scrim is opaque even though it's invisible", scrim.isScrimOpaque)
+    scrim.visibility = View.GONE
+    assertFalse("Scrim is opaque even though it's gone", scrim.isScrimOpaque)
+  }
+
+  @Test
+  fun testRevealSetsOpaque() {
+    scrim.revealAmount = 0.5f
+    assertFalse("Scrim is opaque even though it's revealed", scrim.isScrimOpaque)
+  }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index d23a9ce..a7b1446 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -69,7 +69,6 @@
     @Mock private lateinit var root: View
     @Mock private lateinit var viewRootImpl: ViewRootImpl
     @Mock private lateinit var windowToken: IBinder
-    @Mock private lateinit var shadeSpring: NotificationShadeDepthController.DepthAnimation
     @Mock private lateinit var shadeAnimation: NotificationShadeDepthController.DepthAnimation
     @Mock private lateinit var brightnessSpring: NotificationShadeDepthController.DepthAnimation
     @Mock private lateinit var listener: NotificationShadeDepthController.DepthListener
@@ -89,10 +88,10 @@
         `when`(root.isAttachedToWindow).thenReturn(true)
         `when`(statusBarStateController.state).then { statusBarState }
         `when`(blurUtils.blurRadiusOfRatio(anyFloat())).then { answer ->
-            (answer.arguments[0] as Float * maxBlur).toInt()
+            answer.arguments[0] as Float * maxBlur.toFloat()
         }
-        `when`(blurUtils.ratioOfBlurRadius(anyInt())).then { answer ->
-            answer.arguments[0] as Int / maxBlur.toFloat()
+        `when`(blurUtils.ratioOfBlurRadius(anyFloat())).then { answer ->
+            answer.arguments[0] as Float / maxBlur.toFloat()
         }
         `when`(blurUtils.supportsBlursOnWindows()).thenReturn(true)
         `when`(blurUtils.maxBlurRadius).thenReturn(maxBlur)
@@ -102,7 +101,6 @@
                 statusBarStateController, blurUtils, biometricUnlockController,
                 keyguardStateController, choreographer, wallpaperManager,
                 notificationShadeWindowController, dozeParameters, dumpManager)
-        notificationShadeDepthController.shadeSpring = shadeSpring
         notificationShadeDepthController.shadeAnimation = shadeAnimation
         notificationShadeDepthController.brightnessMirrorSpring = brightnessSpring
         notificationShadeDepthController.root = root
@@ -123,7 +121,6 @@
     fun onPanelExpansionChanged_apliesBlur_ifShade() {
         notificationShadeDepthController.onPanelExpansionChanged(1f /* expansion */,
                 false /* tracking */)
-        verify(shadeSpring).animateTo(eq(maxBlur), any())
         verify(shadeAnimation).animateTo(eq(maxBlur), any())
     }
 
@@ -172,20 +169,19 @@
     @Test
     fun onStateChanged_reevalutesBlurs_ifSameRadiusAndNewState() {
         onPanelExpansionChanged_apliesBlur_ifShade()
-        clearInvocations(shadeSpring)
-        clearInvocations(shadeAnimation)
+        clearInvocations(choreographer)
 
         statusBarState = StatusBarState.KEYGUARD
         statusBarStateListener.onStateChanged(statusBarState)
-        verify(shadeSpring).animateTo(eq(0), any())
         verify(shadeAnimation).animateTo(eq(0), any())
     }
 
     @Test
     fun setQsPanelExpansion_appliesBlur() {
         notificationShadeDepthController.qsPanelExpansion = 1f
+        notificationShadeDepthController.onPanelExpansionChanged(0.5f, tracking = false)
         notificationShadeDepthController.updateBlurCallback.doFrame(0)
-        verify(blurUtils).applyBlur(any(), eq(maxBlur), eq(false))
+        verify(blurUtils).applyBlur(any(), anyInt(), eq(false))
     }
 
     @Test
@@ -206,10 +202,10 @@
     fun setFullShadeTransition_appliesBlur_onlyIfSupported() {
         reset(blurUtils)
         `when`(blurUtils.blurRadiusOfRatio(anyFloat())).then { answer ->
-            (answer.arguments[0] as Float * maxBlur).toInt()
+            answer.arguments[0] as Float * maxBlur
         }
-        `when`(blurUtils.ratioOfBlurRadius(anyInt())).then { answer ->
-            answer.arguments[0] as Int / maxBlur.toFloat()
+        `when`(blurUtils.ratioOfBlurRadius(anyFloat())).then { answer ->
+            answer.arguments[0] as Float / maxBlur.toFloat()
         }
         `when`(blurUtils.maxBlurRadius).thenReturn(maxBlur)
         `when`(blurUtils.maxBlurRadius).thenReturn(maxBlur)
@@ -238,16 +234,16 @@
 
     @Test
     fun updateBlurCallback_setsBlur_whenExpanded() {
-        `when`(shadeSpring.radius).thenReturn(maxBlur)
-        `when`(shadeAnimation.radius).thenReturn(maxBlur)
+        notificationShadeDepthController.onPanelExpansionChanged(1f, false)
+        `when`(shadeAnimation.radius).thenReturn(maxBlur.toFloat())
         notificationShadeDepthController.updateBlurCallback.doFrame(0)
         verify(blurUtils).applyBlur(any(), eq(maxBlur), eq(false))
     }
 
     @Test
     fun updateBlurCallback_ignoreShadeBlurUntilHidden_overridesZoom() {
-        `when`(shadeSpring.radius).thenReturn(maxBlur)
-        `when`(shadeAnimation.radius).thenReturn(maxBlur)
+        notificationShadeDepthController.onPanelExpansionChanged(1f, false)
+        `when`(shadeAnimation.radius).thenReturn(maxBlur.toFloat())
         notificationShadeDepthController.blursDisabledForAppLaunch = true
         notificationShadeDepthController.updateBlurCallback.doFrame(0)
         verify(blurUtils).applyBlur(any(), eq(0), eq(false))
@@ -292,8 +288,8 @@
         // Brightness mirror is fully visible
         `when`(brightnessSpring.ratio).thenReturn(1f)
         // And shade is blurred
-        `when`(shadeSpring.radius).thenReturn(maxBlur)
-        `when`(shadeAnimation.radius).thenReturn(maxBlur)
+        notificationShadeDepthController.onPanelExpansionChanged(1f, false)
+        `when`(shadeAnimation.radius).thenReturn(maxBlur.toFloat())
 
         notificationShadeDepthController.updateBlurCallback.doFrame(0)
         verify(notificationShadeWindowController).setBackgroundBlurRadius(eq(0))
@@ -303,10 +299,8 @@
 
     @Test
     fun ignoreShadeBlurUntilHidden_whennNull_ignoresIfShadeHasNoBlur() {
-        `when`(shadeSpring.radius).thenReturn(0)
-        `when`(shadeAnimation.radius).thenReturn(0)
+        `when`(shadeAnimation.radius).thenReturn(0f)
         notificationShadeDepthController.blursDisabledForAppLaunch = true
-        verify(shadeSpring, never()).animateTo(anyInt(), any())
         verify(shadeAnimation, never()).animateTo(anyInt(), any())
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
index 85ec3fa..f2671b76 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
@@ -22,7 +22,7 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.statusbar.commandline.CommandRegistry
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.statusbar.policy.ConfigurationController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index 116f807..575e01cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -41,7 +41,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener
 import com.android.systemui.settings.UserTracker
-import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
 import com.android.systemui.util.concurrency.FakeExecution
@@ -51,7 +51,6 @@
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.settings.SecureSettings
 import com.android.systemui.util.time.FakeSystemClock
-import org.junit.Assert.assertEquals
 import org.junit.Before
 import org.junit.Test
 import org.mockito.ArgumentCaptor
@@ -114,6 +113,8 @@
     private lateinit var configChangeListener: ConfigurationListener
     private lateinit var statusBarStateListener: StateListener
 
+    private lateinit var smartspaceView: SmartspaceView
+
     private val clock = FakeSystemClock()
     private val executor = FakeExecutor(clock)
     private val execution = FakeExecution()
@@ -141,7 +142,7 @@
         `when`(secureSettings.getUriFor(PRIVATE_LOCKSCREEN_SETTING))
                 .thenReturn(fakePrivateLockscreenSettingUri)
         `when`(smartspaceManager.createSmartspaceSession(any())).thenReturn(smartspaceSession)
-        `when`(plugin.getView(any())).thenReturn(fakeSmartspaceView)
+        `when`(plugin.getView(any())).thenReturn(createSmartspaceView(), createSmartspaceView())
         `when`(userTracker.userProfiles).thenReturn(userList)
         `when`(statusBarStateController.dozeAmount).thenReturn(0.5f)
 
@@ -249,7 +250,7 @@
         configChangeListener.onThemeChanged()
 
         // We update the new text color to match the wallpaper color
-        verify(fakeSmartspaceView).setPrimaryTextColor(anyInt())
+        verify(smartspaceView).setPrimaryTextColor(anyInt())
     }
 
     @Test
@@ -261,7 +262,7 @@
         statusBarStateListener.onDozeAmountChanged(0.1f, 0.7f)
 
         // We pass that along to the view
-        verify(fakeSmartspaceView).setDozeAmount(0.7f)
+        verify(smartspaceView).setDozeAmount(0.7f)
     }
 
     @Test
@@ -393,40 +394,30 @@
     }
 
     @Test
-    fun testBuildViewIsIdempotent() {
-        // GIVEN a connected session
-        connectSession()
-        clearInvocations(plugin)
-
-        // WHEN we disconnect and then reconnect
-        controller.disconnect()
-        controller.buildAndConnectView(fakeParent)
-
-        // THEN the view is not rebuilt
-        verify(plugin, never()).getView(any())
-        assertEquals(fakeSmartspaceView, controller.view)
-    }
-
-    @Test
-    fun testDoubleConnectIsIgnored() {
+    fun testMultipleViewsUseSameSession() {
         // GIVEN a connected session
         connectSession()
         clearInvocations(smartspaceManager)
         clearInvocations(plugin)
 
-        // WHEN we're asked to connect a second time and add to a parent
+        // WHEN we're asked to connect a second time and add to a parent. If the same view
+        // was created the ViewGroup will throw an exception
         val view = controller.buildAndConnectView(fakeParent)
         fakeParent.addView(view)
+        val smartspaceView2 = view as SmartspaceView
 
-        // THEN the existing view and session are reused
+        // THEN the existing session is reused and views are registered
         verify(smartspaceManager, never()).createSmartspaceSession(any())
-        verify(plugin, never()).getView(any())
-        assertEquals(fakeSmartspaceView, controller.view)
+        verify(smartspaceView2).registerDataProvider(plugin)
     }
 
     private fun connectSession() {
-        controller.buildAndConnectView(fakeParent)
+        val view = controller.buildAndConnectView(fakeParent)
+        smartspaceView = view as SmartspaceView
 
+        controller.stateChangeListener.onViewAttachedToWindow(view)
+
+        verify(smartspaceView).registerDataProvider(plugin)
         verify(smartspaceSession)
                 .addOnTargetsAvailableListener(any(), capture(sessionListenerCaptor))
         sessionListener = sessionListenerCaptor.value
@@ -450,11 +441,11 @@
         verify(smartspaceSession).requestSmartspaceUpdate()
         clearInvocations(smartspaceSession)
 
-        verify(fakeSmartspaceView).setPrimaryTextColor(anyInt())
-        verify(fakeSmartspaceView).setDozeAmount(0.5f)
-        clearInvocations(fakeSmartspaceView)
+        verify(smartspaceView).setPrimaryTextColor(anyInt())
+        verify(smartspaceView).setDozeAmount(0.5f)
+        clearInvocations(view)
 
-        fakeParent.addView(fakeSmartspaceView)
+        fakeParent.addView(view)
     }
 
     private fun setActiveUser(userHandle: UserHandle) {
@@ -490,31 +481,33 @@
         ).thenReturn(if (value) 1 else 0)
     }
 
-    private val fakeSmartspaceView = spy(object : View(context), SmartspaceView {
-        override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) {
-        }
+    private fun createSmartspaceView(): SmartspaceView {
+        return spy(object : View(context), SmartspaceView {
+            override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) {
+            }
 
-        override fun setPrimaryTextColor(color: Int) {
-        }
+            override fun setPrimaryTextColor(color: Int) {
+            }
 
-        override fun setDozeAmount(amount: Float) {
-        }
+            override fun setDozeAmount(amount: Float) {
+            }
 
-        override fun setIntentStarter(intentStarter: BcSmartspaceDataPlugin.IntentStarter?) {
-        }
+            override fun setIntentStarter(intentStarter: BcSmartspaceDataPlugin.IntentStarter?) {
+            }
 
-        override fun setFalsingManager(falsingManager: FalsingManager?) {
-        }
+            override fun setFalsingManager(falsingManager: FalsingManager?) {
+            }
 
-        override fun setDnd(image: Drawable?, description: String?) {
-        }
+            override fun setDnd(image: Drawable?, description: String?) {
+            }
 
-        override fun setNextAlarm(image: Drawable?, description: String?) {
-        }
+            override fun setNextAlarm(image: Drawable?, description: String?) {
+            }
 
-        override fun setMediaTarget(target: SmartspaceTarget?) {
-        }
-    })
+            override fun setMediaTarget(target: SmartspaceTarget?) {
+            }
+        })
+    }
 }
 
 private const val PRIVATE_LOCKSCREEN_SETTING =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 1be14b6..8a32ce6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -64,7 +64,7 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLifetimeExtender;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationPresenter;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 9a5482c..39d794d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -67,7 +67,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.dump.LogBufferEulogizer;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.statusbar.RankingBuilder;
 import com.android.systemui.statusbar.notification.collection.NoManSimulator.NotifEvent;
 import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index 2ce22a6..3378003 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -802,13 +802,13 @@
                 .onBeforeTransformGroups(anyList());
         inOrder.verify(promoter, atLeastOnce())
                 .shouldPromoteToTopLevel(any(NotificationEntry.class));
+        inOrder.verify(mOnBeforeFinalizeFilterListener).onBeforeFinalizeFilter(anyList());
+        inOrder.verify(preRenderFilter, atLeastOnce())
+                .shouldFilterOut(any(NotificationEntry.class), anyLong());
         inOrder.verify(mOnBeforeSortListener).onBeforeSort(anyList());
         inOrder.verify(section, atLeastOnce()).isInSection(any(ListEntry.class));
         inOrder.verify(comparator, atLeastOnce())
                 .compare(any(ListEntry.class), any(ListEntry.class));
-        inOrder.verify(mOnBeforeFinalizeFilterListener).onBeforeFinalizeFilter(anyList());
-        inOrder.verify(preRenderFilter, atLeastOnce())
-                .shouldFilterOut(any(NotificationEntry.class), anyLong());
         inOrder.verify(mOnBeforeRenderListListener).onBeforeRenderList(anyList());
         inOrder.verify(mOnRenderListListener).onRenderList(anyList());
     }
@@ -1045,12 +1045,32 @@
         // because group changes aren't allowed by the stability manager
         verifyBuiltList(
                 notif(0),
+                notif(2),
                 group(
                         summary(3),
                         child(4),
                         child(5)
-                ),
-                notif(2)
+                )
+        );
+    }
+
+    @Test
+    public void testBrokenGroupNotificationOrdering() {
+        // GIVEN two group children with different sections & without a summary yet
+
+        addGroupChild(0, PACKAGE_2, GROUP_1);
+        addNotif(1, PACKAGE_1);
+        addGroupChild(2, PACKAGE_2, GROUP_1);
+        addGroupChild(3, PACKAGE_2, GROUP_1);
+
+        dispatchBuild();
+
+        // THEN all notifications are not grouped and posted in order by index
+        verifyBuiltList(
+                notif(0),
+                notif(1),
+                notif(2),
+                notif(3)
         );
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java
new file mode 100644
index 0000000..24a0ad3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.notification.row;
+
+import static android.view.DragEvent.ACTION_DRAG_STARTED;
+
+import android.content.Context;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.DragEvent;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class ExpandableNotificationRowDragControllerTest extends SysuiTestCase {
+
+    private ExpandableNotificationRow mRow;
+    private ExpandableNotificationRow mGroupRow;
+    private ExpandableNotificationRowDragController mController;
+    private NotificationTestHelper mNotificationTestHelper;
+
+    private NotificationGutsManager mGutsManager = mock(NotificationGutsManager.class);
+    private HeadsUpManager mHeadsUpManager = mock(HeadsUpManager.class);
+    private NotificationMenuRow mMenuRow = mock(NotificationMenuRow.class);
+    private NotificationMenuRowPlugin.MenuItem mMenuItem =
+            mock(NotificationMenuRowPlugin.MenuItem.class);
+
+    @Before
+    public void setUp() throws Exception {
+        allowTestableLooperAsMainThread();
+
+        mDependency.injectMockDependency(ShadeController.class);
+
+        mNotificationTestHelper = new NotificationTestHelper(
+                mContext,
+                mDependency,
+                TestableLooper.get(this));
+        mRow = mNotificationTestHelper.createRow();
+        mGroupRow = mNotificationTestHelper.createGroup(4);
+        when(mMenuRow.getLongpressMenuItem(any(Context.class))).thenReturn(mMenuItem);
+
+        mController = new ExpandableNotificationRowDragController(mContext, mHeadsUpManager);
+    }
+
+    @Test
+    public void testDoStartDragHeadsUpNotif_startDragAndDrop() throws Exception {
+        ExpandableNotificationRowDragController controller = createSpyController();
+        mRow.setDragController(controller);
+        mRow.setHeadsUp(true);
+        mRow.setPinned(true);
+
+        mRow.doLongClickCallback(0, 0);
+        mRow.doDragCallback(0, 0);
+        verify(controller).startDragAndDrop(mRow);
+
+        // Simulate the drag start
+        mRow.dispatchDragEvent(DragEvent.obtain(ACTION_DRAG_STARTED, 0, 0, 0, 0, null, null, null,
+                null, null, false));
+        verify(mHeadsUpManager, times(1)).releaseAllImmediately();
+    }
+
+    @Test
+    public void testDoStartDragNotif() throws Exception {
+        ExpandableNotificationRowDragController controller = createSpyController();
+        mRow.setDragController(controller);
+
+        mDependency.get(ShadeController.class).instantExpandNotificationsPanel();
+        mRow.doDragCallback(0, 0);
+        verify(controller).startDragAndDrop(mRow);
+
+        // Simulate the drag start
+        mRow.dispatchDragEvent(DragEvent.obtain(ACTION_DRAG_STARTED, 0, 0, 0, 0, null, null, null,
+                null, null, false));
+        verify(mDependency.get(ShadeController.class)).animateCollapsePanels(0, true);
+    }
+
+    private ExpandableNotificationRowDragController createSpyController() {
+        return spy(mController);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index cea49b7..4562e4f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -47,11 +47,11 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.media.MediaFeatureFlag;
 import com.android.systemui.media.dialog.MediaOutputDialogFactory;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationPresenter;
@@ -265,7 +265,8 @@
                                 new FalsingManagerFake(),
                                 new FalsingCollectorFake(),
                                 mPeopleNotificationIdentifier,
-                                Optional.of(mock(BubblesManager.class))
+                                Optional.of(mock(BubblesManager.class)),
+                                mock(ExpandableNotificationRowDragController.class)
                         ));
 
         when(mNotificationRowComponentBuilder.activatableNotificationView(any()))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index c56d085..07ebaea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -46,11 +46,11 @@
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.media.KeyguardMediaController;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index d02d77e..4e76b16 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -52,7 +52,6 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.EmptyShadeView;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.NotificationShelfController;
 import com.android.systemui.statusbar.StatusBarState;
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 b0d1fc1..5f46700 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
@@ -17,6 +17,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.mock;
@@ -37,9 +38,10 @@
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiBaseFragmentTest;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.OperatorNameViewController;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -71,6 +73,8 @@
 
     private final StatusBar mStatusBar = mock(StatusBar.class);
     private final CommandQueue mCommandQueue = mock(CommandQueue.class);
+    private OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
+    private OperatorNameViewController mOperatorNameViewController;
 
     public CollapsedStatusBarFragmentTest() {
         super(CollapsedStatusBarFragment.class);
@@ -241,6 +245,11 @@
         mNetworkController = mock(NetworkController.class);
         mStatusBarStateController = mock(StatusBarStateController.class);
         mKeyguardStateController = mock(KeyguardStateController.class);
+        mOperatorNameViewController = mock(OperatorNameViewController.class);
+        mOperatorNameViewControllerFactory = mock(OperatorNameViewController.Factory.class);
+        when(mOperatorNameViewControllerFactory.create(any()))
+                .thenReturn(mOperatorNameViewController);
+
         setUpNotificationIconAreaController();
         return new CollapsedStatusBarFragment(
                 mOngoingCallController,
@@ -253,7 +262,8 @@
                 mKeyguardStateController,
                 mNetworkController,
                 mStatusBarStateController,
-                mCommandQueue);
+                mCommandQueue,
+                mOperatorNameViewControllerFactory);
     }
 
     private void setUpNotificationIconAreaController() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
index 5bf1bb3..7a0b366 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
@@ -38,7 +38,7 @@
 import com.android.systemui.doze.AlwaysOnDisplayPolicy;
 import com.android.systemui.doze.DozeScreenState;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.tuner.TunerService;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index 6e0cbd9..bca1227 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -202,6 +202,5 @@
         verify(mPanelView).removeTrackingHeadsUpListener(any());
         verify(mPanelView).setHeadsUpAppearanceController(isNull());
         verify(mStackScrollerController).removeOnExpandedHeightChangedListener(any());
-        verify(mStackScrollerController).removeOnLayoutChangeListener(any());
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index 690b841..f34f21b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -31,42 +31,31 @@
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
-
     private static final int SCREEN_HEIGHT = 2000;
     private static final int EMPTY_MARGIN = 0;
     private static final int EMPTY_HEIGHT = 0;
     private static final float ZERO_DRAG = 0.f;
     private static final float OPAQUE = 1.f;
     private static final float TRANSPARENT = 0.f;
-    private static final boolean HAS_CUSTOM_CLOCK = false;
-    private static final boolean HAS_VISIBLE_NOTIFS = false;
-
     private KeyguardClockPositionAlgorithm mClockPositionAlgorithm;
     private KeyguardClockPositionAlgorithm.Result mClockPosition;
-    private int mNotificationStackHeight;
     private float mPanelExpansion;
     private int mKeyguardStatusHeight;
     private float mDark;
-    private boolean mHasCustomClock;
-    private boolean mHasVisibleNotifs;
     private float mQsExpansion;
-    private int mCutoutTopInset = 0; // in pixels
+    private int mCutoutTopInsetPx = 0;
     private boolean mIsSplitShade = false;
 
     @Before
     public void setUp() {
         mClockPositionAlgorithm = new KeyguardClockPositionAlgorithm();
         mClockPosition = new KeyguardClockPositionAlgorithm.Result();
-
-        mHasCustomClock = HAS_CUSTOM_CLOCK;
-        mHasVisibleNotifs = HAS_VISIBLE_NOTIFS;
     }
 
     @Test
     public void clockPositionTopOfScreenOnAOD() {
-        // GIVEN on AOD and both stack scroll and clock have 0 height
+        // GIVEN on AOD and clock has 0 height
         givenAOD();
-        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = EMPTY_HEIGHT;
         // WHEN the clock position algorithm is run
         positionClock();
@@ -79,11 +68,10 @@
 
     @Test
     public void clockPositionBelowCutout() {
-        // GIVEN on AOD and both stack scroll and clock have 0 height
+        // GIVEN on AOD and clock has 0 height
         givenAOD();
-        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = EMPTY_HEIGHT;
-        mCutoutTopInset = 300;
+        mCutoutTopInsetPx = 300;
         // WHEN the clock position algorithm is run
         positionClock();
         // THEN the clock Y position is below the cutout
@@ -97,7 +85,6 @@
     public void clockPositionAdjustsForKeyguardStatusOnAOD() {
         // GIVEN on AOD with a clock of height 100
         givenAOD();
-        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = 100;
         // WHEN the clock position algorithm is run
         positionClock();
@@ -112,7 +99,6 @@
     public void clockPositionLargeClockOnAOD() {
         // GIVEN on AOD with a full screen clock
         givenAOD();
-        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = SCREEN_HEIGHT;
         // WHEN the clock position algorithm is run
         positionClock();
@@ -125,9 +111,8 @@
 
     @Test
     public void clockPositionTopOfScreenOnLockScreen() {
-        // GIVEN on lock screen with stack scroll and clock of 0 height
+        // GIVEN on lock screen with clock of 0 height
         givenLockScreen();
-        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = EMPTY_HEIGHT;
         // WHEN the clock position algorithm is run
         positionClock();
@@ -138,24 +123,9 @@
     }
 
     @Test
-    public void clockPositionWithStackScrollExpandOnLockScreen() {
-        // GIVEN on lock screen with stack scroll of height 500
-        givenLockScreen();
-        mNotificationStackHeight = 500;
-        mKeyguardStatusHeight = EMPTY_HEIGHT;
-        // WHEN the clock position algorithm is run
-        positionClock();
-        // THEN the clock Y position stays to the top
-        assertThat(mClockPosition.clockY).isEqualTo(0);
-        // AND the clock is positioned on the left.
-        assertThat(mClockPosition.clockX).isEqualTo(0);
-    }
-
-    @Test
     public void clockPositionWithPartialDragOnLockScreen() {
         // GIVEN dragging up on lock screen
         givenLockScreen();
-        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = EMPTY_HEIGHT;
         mPanelExpansion = 0.5f;
         // WHEN the clock position algorithm is run
@@ -171,7 +141,6 @@
     public void clockPositionWithFullDragOnLockScreen() {
         // GIVEN the lock screen is dragged up
         givenLockScreen();
-        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = EMPTY_HEIGHT;
         mPanelExpansion = 0.f;
         // WHEN the clock position algorithm is run
@@ -184,7 +153,6 @@
     public void largeClockOnLockScreenIsTransparent() {
         // GIVEN on lock screen with a full screen clock
         givenLockScreen();
-        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = SCREEN_HEIGHT;
         // WHEN the clock position algorithm is run
         positionClock();
@@ -194,9 +162,8 @@
 
     @Test
     public void notifPositionTopOfScreenOnAOD() {
-        // GIVEN on AOD and both stack scroll and clock have 0 height
+        // GIVEN on AOD and clock has 0 height
         givenAOD();
-        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = EMPTY_HEIGHT;
         // WHEN the position algorithm is run
         positionClock();
@@ -208,7 +175,6 @@
     public void notifPositionIndependentOfKeyguardStatusHeightOnAOD() {
         // GIVEN on AOD and clock has a nonzero height
         givenAOD();
-        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = 100;
         // WHEN the position algorithm is run
         positionClock();
@@ -220,7 +186,6 @@
     public void notifPositionWithLargeClockOnAOD() {
         // GIVEN on AOD and clock has a nonzero height
         givenAOD();
-        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = SCREEN_HEIGHT;
         // WHEN the position algorithm is run
         positionClock();
@@ -230,9 +195,8 @@
 
     @Test
     public void notifPositionMiddleOfScreenOnLockScreen() {
-        // GIVEN on lock screen and both stack scroll and clock have 0 height
+        // GIVEN on lock screen and clock has 0 height
         givenLockScreen();
-        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = EMPTY_HEIGHT;
         // WHEN the position algorithm is run
         positionClock();
@@ -241,47 +205,20 @@
     }
 
     @Test
-    public void notifPositionAdjustsForStackHeightOnLockScreen() {
-        // GIVEN on lock screen and stack scroller has a nonzero height
-        givenLockScreen();
-        mNotificationStackHeight = 500;
-        mKeyguardStatusHeight = EMPTY_HEIGHT;
-        // WHEN the position algorithm is run
-        positionClock();
-        // THEN the notif padding adjusts for keyguard status height
-        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(0);
-    }
-
-    @Test
     public void notifPositionAdjustsForClockHeightOnLockScreen() {
         // GIVEN on lock screen and stack scroller has a nonzero height
         givenLockScreen();
-        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = 200;
         // WHEN the position algorithm is run
         positionClock();
-        // THEN the notif padding adjusts for both clock and notif stack.
-        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(200);
-    }
-
-    @Test
-    public void notifPositionAdjustsForStackHeightAndClockHeightOnLockScreen() {
-        // GIVEN on lock screen and stack scroller has a nonzero height
-        givenLockScreen();
-        mNotificationStackHeight = 500;
-        mKeyguardStatusHeight = 200;
-        // WHEN the position algorithm is run
-        positionClock();
-        // THEN the notifs are placed below the statusview
         assertThat(mClockPosition.stackScrollerPadding).isEqualTo(200);
     }
 
     @Test
     public void notifPositionAlignedWithClockInSplitShadeMode() {
-        // GIVEN on lock screen and split shade mode
         givenLockScreen();
         mIsSplitShade = true;
-        mHasCustomClock = true;
+        mKeyguardStatusHeight = 200;
         // WHEN the position algorithm is run
         positionClock();
         // THEN the notif padding DOESN'T adjust for keyguard status height.
@@ -292,7 +229,6 @@
     public void notifPositionWithLargeClockOnLockScreen() {
         // GIVEN on lock screen and clock has a nonzero height
         givenLockScreen();
-        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = SCREEN_HEIGHT;
         // WHEN the position algorithm is run
         positionClock();
@@ -304,7 +240,6 @@
     public void notifPositionWithFullDragOnLockScreen() {
         // GIVEN the lock screen is dragged up
         givenLockScreen();
-        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = EMPTY_HEIGHT;
         mPanelExpansion = 0.f;
         // WHEN the clock position algorithm is run
@@ -317,19 +252,17 @@
     public void notifPositionWithLargeClockFullDragOnLockScreen() {
         // GIVEN the lock screen is dragged up and a full screen clock
         givenLockScreen();
-        mNotificationStackHeight = EMPTY_HEIGHT;
         mKeyguardStatusHeight = SCREEN_HEIGHT;
         mPanelExpansion = 0.f;
         // WHEN the clock position algorithm is run
         positionClock();
-        // THEN the notif padding is zero.
         assertThat(mClockPosition.stackScrollerPadding).isEqualTo(
                 (int) (mKeyguardStatusHeight * .667f));
     }
 
     @Test
     public void clockHiddenWhenQsIsExpanded() {
-        // GIVEN on the lock screen with a custom clock and visible notifications
+        // GIVEN on the lock screen with visible notifications
         givenLockScreen();
         mQsExpansion = 1;
         // WHEN the clock position algorithm is run
@@ -349,12 +282,11 @@
     }
 
     private void positionClock() {
-        mClockPositionAlgorithm.setup(EMPTY_MARGIN, SCREEN_HEIGHT, mNotificationStackHeight,
-                mPanelExpansion, SCREEN_HEIGHT, mKeyguardStatusHeight,
+        mClockPositionAlgorithm.setup(EMPTY_MARGIN, mPanelExpansion, mKeyguardStatusHeight,
                 0 /* userSwitchHeight */, 0 /* userSwitchPreferredY */,
-                mHasCustomClock, mHasVisibleNotifs, mDark, ZERO_DRAG, false /* bypassEnabled */,
+                mDark, ZERO_DRAG, false /* bypassEnabled */,
                 0 /* unlockedStackScrollerPadding */, mQsExpansion,
-                mCutoutTopInset, mIsSplitShade);
+                mCutoutTopInsetPx, mIsSplitShade);
         mClockPositionAlgorithm.run(mClockPosition);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index 217a77d..f9e3707 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -17,35 +17,49 @@
 package com.android.systemui.statusbar.phone;
 
 
+import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+import static com.android.systemui.statusbar.StatusBarState.SHADE;
+
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.view.ViewGroup;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+import android.view.View;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.keyguard.CarrierTextController;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.battery.BatteryMeterViewController;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.UserInfoController;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
 public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
     @Mock
-    private KeyguardStatusBarView mKeyguardStatusBarView;
-    @Mock
-    private ViewGroup mViewGroup;
-    @Mock
     private CarrierTextController mCarrierTextController;
     @Mock
     private ConfigurationController mConfigurationController;
@@ -59,16 +73,36 @@
     private StatusBarIconController mStatusBarIconController;
     @Mock
     private FeatureFlags mFeatureFlags;
+    @Mock
+    private BatteryMeterViewController mBatteryMeterViewController;
+    @Mock
+    private KeyguardStateController mKeyguardStateController;
+    @Mock
+    private KeyguardBypassController mKeyguardBypassController;
+    @Mock
+    private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    @Mock
+    private BiometricUnlockController mBiometricUnlockController;
+    @Mock
+    private SysuiStatusBarStateController mStatusBarStateController;
 
+    private KeyguardStatusBarView mKeyguardStatusBarView;
     private KeyguardStatusBarViewController mController;
 
+    private float mAlpha = 0.5f;
+    private final KeyguardStatusBarViewController.ViewStateProvider mViewStateProvider =
+            () -> new KeyguardStatusBarViewController.ViewState(mAlpha);
+
     @Before
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
 
-        when(mKeyguardStatusBarView.getResources()).thenReturn(mContext.getResources());
-        when(mKeyguardStatusBarView.findViewById(R.id.statusIcons)).thenReturn(mViewGroup);
-        when(mViewGroup.getContext()).thenReturn(mContext);
+        allowTestableLooperAsMainThread();
+        TestableLooper.get(this).runWithLooper(() -> {
+            mKeyguardStatusBarView =
+                    (KeyguardStatusBarView) LayoutInflater.from(mContext)
+                            .inflate(R.layout.keyguard_status_bar, null);
+        });
 
         mController = new KeyguardStatusBarViewController(
                 mKeyguardStatusBarView,
@@ -78,7 +112,14 @@
                 mBatteryController,
                 mUserInfoController,
                 mStatusBarIconController,
-                new StatusBarIconController.TintedIconManager.Factory(mFeatureFlags)
+                new StatusBarIconController.TintedIconManager.Factory(mFeatureFlags),
+                mBatteryMeterViewController,
+                mViewStateProvider,
+                mKeyguardStateController,
+                mKeyguardBypassController,
+                mKeyguardUpdateMonitor,
+                mBiometricUnlockController,
+                mStatusBarStateController
         );
     }
 
@@ -129,4 +170,129 @@
 
         verify(mBatteryController).addCallback(any());
     }
+
+    @Test
+    public void updateTopClipping_viewClippingUpdated() {
+        int viewTop = 20;
+        mKeyguardStatusBarView.setTop(viewTop);
+        int notificationPanelTop = 30;
+
+        mController.updateTopClipping(notificationPanelTop);
+
+        assertThat(mKeyguardStatusBarView.getClipBounds().top).isEqualTo(
+                notificationPanelTop - viewTop);
+    }
+
+    @Test
+    public void setNotTopClipping_viewClippingUpdatedToZero() {
+        // Start out with some amount of top clipping.
+        mController.updateTopClipping(50);
+        assertThat(mKeyguardStatusBarView.getClipBounds().top).isGreaterThan(0);
+
+        mController.setNoTopClipping();
+
+        assertThat(mKeyguardStatusBarView.getClipBounds().top).isEqualTo(0);
+    }
+
+    @Test
+    public void updateViewState_alphaAndVisibilityGiven_viewUpdated() {
+        // Verify the initial values so we know the method triggers changes.
+        assertThat(mKeyguardStatusBarView.getAlpha()).isEqualTo(1f);
+        assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
+
+        float newAlpha = 0.5f;
+        int newVisibility = View.INVISIBLE;
+        mController.updateViewState(newAlpha, newVisibility);
+
+        assertThat(mKeyguardStatusBarView.getAlpha()).isEqualTo(newAlpha);
+        assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(newVisibility);
+    }
+
+    @Test
+    public void updateViewState_notKeyguardState_nothingUpdated() {
+        mAlpha = 0.255f;
+        mController.onViewAttached();
+        updateStateToNotKeyguard();
+
+        float oldAlpha = mKeyguardStatusBarView.getAlpha();
+
+        mController.updateViewState();
+
+        assertThat(mKeyguardStatusBarView.getAlpha()).isEqualTo(oldAlpha);
+    }
+
+    @Test
+    public void updateViewState_bypassEnabledAndShouldListenForFace_viewHidden() {
+        mController.onViewAttached();
+        updateStateToKeyguard();
+        assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
+
+        when(mKeyguardUpdateMonitor.shouldListenForFace()).thenReturn(true);
+        when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
+        onFinishedGoingToSleep();
+
+        mController.updateViewState();
+
+        assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.INVISIBLE);
+    }
+
+    @Test
+    public void updateViewState_bypassNotEnabled_viewShown() {
+        mController.onViewAttached();
+        updateStateToKeyguard();
+
+        when(mKeyguardUpdateMonitor.shouldListenForFace()).thenReturn(true);
+        when(mKeyguardBypassController.getBypassEnabled()).thenReturn(false);
+        onFinishedGoingToSleep();
+
+        mController.updateViewState();
+
+        assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void updateViewState_shouldNotListenForFace_viewShown() {
+        mController.onViewAttached();
+        updateStateToKeyguard();
+
+        when(mKeyguardUpdateMonitor.shouldListenForFace()).thenReturn(false);
+        when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
+        onFinishedGoingToSleep();
+
+        mController.updateViewState();
+
+        assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    // TODO(b/195442899): Add more tests for #updateViewState once CLs are finalized.
+
+    private void updateStateToNotKeyguard() {
+        updateStatusBarState(SHADE);
+    }
+
+    private void updateStateToKeyguard() {
+        updateStatusBarState(KEYGUARD);
+    }
+
+    private void updateStatusBarState(int state) {
+        ArgumentCaptor<StatusBarStateController.StateListener> statusBarStateListenerCaptor =
+                ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
+        verify(mStatusBarStateController).addCallback(statusBarStateListenerCaptor.capture());
+        StatusBarStateController.StateListener callback = statusBarStateListenerCaptor.getValue();
+
+        callback.onStateChanged(state);
+    }
+
+    /**
+     * Calls {@link com.android.keyguard.KeyguardUpdateMonitorCallback#onFinishedGoingToSleep(int)}
+     * to ensure values are updated properly.
+     */
+    private void onFinishedGoingToSleep() {
+        ArgumentCaptor<KeyguardUpdateMonitorCallback> keyguardUpdateCallbackCaptor =
+                ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
+        verify(mKeyguardUpdateMonitor).registerCallback(keyguardUpdateCallbackCaptor.capture());
+        KeyguardUpdateMonitorCallback callback = keyguardUpdateCallbackCaptor.getValue();
+
+        callback.onFinishedGoingToSleep(0);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java
new file mode 100644
index 0000000..3108ed9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.phone;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class KeyguardStatusBarViewTest extends SysuiTestCase {
+
+    private KeyguardStatusBarView mKeyguardStatusBarView;
+
+    @Before
+    public void setup() throws Exception {
+        allowTestableLooperAsMainThread();
+        TestableLooper.get(this).runWithLooper(() -> {
+            mKeyguardStatusBarView =
+                    (KeyguardStatusBarView) LayoutInflater.from(mContext)
+                            .inflate(R.layout.keyguard_status_bar, null);
+        });
+    }
+
+    @Test
+    public void setTopClipping_clippingUpdated() {
+        int topClipping = 40;
+
+        mKeyguardStatusBarView.setTopClipping(topClipping);
+
+        assertThat(mKeyguardStatusBarView.getClipBounds().top).isEqualTo(topClipping);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index cbaca3a..bef10ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -47,7 +47,6 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.ContentObserver;
-import android.hardware.biometrics.BiometricSourceType;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.PowerManager;
@@ -229,8 +228,6 @@
     @Mock
     private ConversationNotificationManager mConversationNotificationManager;
     @Mock
-    private BiometricUnlockController mBiometricUnlockController;
-    @Mock
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     @Mock
     private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
@@ -300,6 +297,8 @@
     private RecordingController mRecordingController;
     @Mock
     private ControlsComponent mControlsComponent;
+    @Mock
+    private LockscreenGestureLogger mLockscreenGestureLogger;
 
     private SysuiStatusBarStateController mStatusBarStateController;
     private NotificationPanelViewController mNotificationPanelViewController;
@@ -389,7 +388,7 @@
                 .thenReturn(mKeyguardClockSwitchController);
         when(mKeyguardStatusViewComponent.getKeyguardStatusViewController())
                 .thenReturn(mKeyguardStatusViewController);
-        when(mKeyguardStatusBarViewComponentFactory.build(any()))
+        when(mKeyguardStatusBarViewComponentFactory.build(any(), any()))
                 .thenReturn(mKeyguardStatusBarViewComponent);
         when(mKeyguardStatusBarViewComponent.getKeyguardStatusBarViewController())
                 .thenReturn(mKeyguardStatusBarViewController);
@@ -414,7 +413,7 @@
                 mMetricsLogger, mActivityManager, mConfigurationController,
                 () -> flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager,
                 mConversationNotificationManager, mMediaHiearchyManager,
-                mBiometricUnlockController, mStatusBarKeyguardViewManager,
+                mStatusBarKeyguardViewManager,
                 mNotificationStackScrollLayoutController,
                 mKeyguardStatusViewComponentFactory,
                 mKeyguardQsUserSwitchComponentFactory,
@@ -443,6 +442,7 @@
                 mSecureSettings,
                 mSplitShadeHeaderController,
                 mUnlockedScreenOffAnimationController,
+                mLockscreenGestureLogger,
                 mNotificationRemoteInputManager,
                 mControlsComponent);
         mNotificationPanelViewController.initDependencies(
@@ -509,20 +509,6 @@
     }
 
     @Test
-    public void testKeyguardStatusBarVisibility_hiddenForBypass() {
-        when(mUpdateMonitor.shouldListenForFace()).thenReturn(true);
-        mNotificationPanelViewController.mKeyguardUpdateCallback.onBiometricRunningStateChanged(
-                true, BiometricSourceType.FACE);
-        verify(mKeyguardStatusBar, never()).setVisibility(View.VISIBLE);
-
-        when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
-        mNotificationPanelViewController.mKeyguardUpdateCallback.onFinishedGoingToSleep(0);
-        mNotificationPanelViewController.mKeyguardUpdateCallback.onBiometricRunningStateChanged(
-                true, BiometricSourceType.FACE);
-        verify(mKeyguardStatusBar, never()).setVisibility(View.VISIBLE);
-    }
-
-    @Test
     public void testA11y_initializeNode() {
         AccessibilityNodeInfo nodeInfo = new AccessibilityNodeInfo();
         mAccessibiltyDelegate.onInitializeAccessibilityNodeInfo(mView, nodeInfo);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
index ddd7854..90b8a74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
@@ -146,7 +146,7 @@
         mNotificationShadeWindowController.attach();
 
         clearInvocations(mWindowManager);
-        mNotificationShadeWindowController.setLightRevealScrimAmount(0f);
+        mNotificationShadeWindowController.setLightRevealScrimOpaque(true);
         verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture());
         assertThat((mLayoutParameters.getValue().flags & FLAG_SHOW_WALLPAPER) == 0).isTrue();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
index 6c1a3c9..3d887dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
@@ -16,6 +16,11 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -41,7 +46,6 @@
 import com.android.systemui.statusbar.NotificationShadeDepthController;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -55,6 +59,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -85,12 +91,16 @@
     @Mock private NotificationPanelViewController mNotificationPanelViewController;
     @Mock private NotificationStackScrollLayout mNotificationStackScrollLayout;
     @Mock private NotificationShadeDepthController mNotificationShadeDepthController;
-    @Mock private SuperStatusBarViewFactory mStatusBarViewFactory;
+    @Mock private StatusBarWindowView mStatusBarWindowView;
     @Mock private NotificationShadeWindowController mNotificationShadeWindowController;
     @Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
     @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     @Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
 
+    @Captor private ArgumentCaptor<NotificationShadeWindowView.InteractionEventHandler>
+            mInteractionEventHandlerCaptor;
+    private NotificationShadeWindowView.InteractionEventHandler mInteractionEventHandler;
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -129,7 +139,7 @@
                 mNotificationShadeDepthController,
                 mView,
                 mNotificationPanelViewController,
-                mStatusBarViewFactory,
+                mStatusBarWindowView,
                 mNotificationStackScrollLayoutController,
                 mStatusBarKeyguardViewManager);
         mController.setupExpandedStatusBar();
@@ -147,4 +157,49 @@
         verify(mDragDownHelper).onTouchEvent(ev);
         ev.recycle();
     }
+
+    @Test
+    public void testInterceptTouchWhenShowingAltAuth() {
+        captureInteractionEventHandler();
+
+        // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
+        when(mStatusBarStateController.isDozing()).thenReturn(false);
+        when(mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()).thenReturn(true);
+        when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false);
+
+        // THEN we should intercept touch
+        assertTrue(mInteractionEventHandler.shouldInterceptTouchEvent(mock(MotionEvent.class)));
+    }
+
+    @Test
+    public void testNoInterceptTouch() {
+        captureInteractionEventHandler();
+
+        // WHEN not showing alt auth, not dozing, drag down helper doesn't want to intercept
+        when(mStatusBarStateController.isDozing()).thenReturn(false);
+        when(mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()).thenReturn(false);
+        when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false);
+
+        // THEN we shouldn't intercept touch
+        assertFalse(mInteractionEventHandler.shouldInterceptTouchEvent(mock(MotionEvent.class)));
+    }
+
+    @Test
+    public void testHandleTouchEventWhenShowingAltAuth() {
+        captureInteractionEventHandler();
+
+        // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
+        when(mStatusBarStateController.isDozing()).thenReturn(false);
+        when(mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()).thenReturn(true);
+        when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false);
+
+        // THEN we should handle the touch
+        assertTrue(mInteractionEventHandler.handleTouchEvent(mock(MotionEvent.class)));
+    }
+
+    private void captureInteractionEventHandler() {
+        verify(mView).setInteractionEventHandler(mInteractionEventHandlerCaptor.capture());
+        mInteractionEventHandler = mInteractionEventHandlerCaptor.getValue();
+
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
new file mode 100644
index 0000000..d63730d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.phone
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.CommandQueue
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+class PhoneStatusBarViewControllerTest : SysuiTestCase() {
+
+    @Mock
+    private lateinit var commandQueue: CommandQueue
+
+    private lateinit var view: PhoneStatusBarView
+    private lateinit var controller: PhoneStatusBarViewController
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        view = PhoneStatusBarView(mContext, null)
+        controller = PhoneStatusBarViewController(view, commandQueue)
+    }
+
+    @Test
+    fun constructor_setsPanelEnabledProviderOnView() {
+        var providerUsed = false
+        `when`(commandQueue.panelsEnabled()).then {
+            providerUsed = true
+            true
+        }
+
+        // If the constructor correctly set a [PanelEnabledProvider], then it should be used
+        // when [PhoneStatusBarView.panelEnabled] is called.
+        view.panelEnabled()
+
+        assertThat(providerUsed).isTrue()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
new file mode 100644
index 0000000..49ab6eb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.phone
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+
+@SmallTest
+class PhoneStatusBarViewTest : SysuiTestCase() {
+
+    private lateinit var view: PhoneStatusBarView
+
+    @Before
+    fun setUp() {
+        view = PhoneStatusBarView(mContext, null)
+    }
+
+    @Test
+    fun panelEnabled_providerReturnsTrue_returnsTrue() {
+        view.setPanelEnabledProvider { true }
+
+        assertThat(view.panelEnabled()).isTrue()
+    }
+
+    @Test
+    fun panelEnabled_providerReturnsFalse_returnsFalse() {
+        view.setPanelEnabledProvider { false }
+
+        assertThat(view.panelEnabled()).isFalse()
+    }
+
+    @Test
+    fun panelEnabled_noProvider_noCrash() {
+        view.panelEnabled()
+        // No assert needed, just testing no crash
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 2685b76..9d316b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -16,10 +16,13 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.android.systemui.statusbar.phone.ScrimController.KEYGUARD_SCRIM_ALPHA;
 import static com.android.systemui.statusbar.phone.ScrimController.OPAQUE;
 import static com.android.systemui.statusbar.phone.ScrimController.SEMI_TRANSPARENT;
 import static com.android.systemui.statusbar.phone.ScrimController.TRANSPARENT;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyFloat;
@@ -83,7 +86,6 @@
     private ScrimView mScrimBehind;
     private ScrimView mNotificationsScrim;
     private ScrimView mScrimInFront;
-    private ScrimView mScrimForBubble;
     private ScrimState mScrimState;
     private float mScrimBehindAlpha;
     private GradientColors mScrimInFrontColor;
@@ -167,7 +169,6 @@
         endAnimation(mNotificationsScrim);
         endAnimation(mScrimBehind);
         endAnimation(mScrimInFront);
-        endAnimation(mScrimForBubble);
 
         assertEquals("Animators did not finish",
                 mAnimatorListener.getNumStarts(), mAnimatorListener.getNumEnds());
@@ -190,7 +191,6 @@
 
         mScrimBehind = spy(new ScrimView(getContext()));
         mScrimInFront = new ScrimView(getContext());
-        mScrimForBubble = new ScrimView(getContext());
         mNotificationsScrim = new ScrimView(getContext());
         mAlwaysOnEnabled = true;
         mLooper = TestableLooper.get(this);
@@ -226,8 +226,7 @@
                 mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()),
                 mUnlockedScreenOffAnimationController);
         mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
-        mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront,
-                mScrimForBubble);
+        mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
         mScrimController.setAnimatorListener(mAnimatorListener);
 
         mScrimController.setHasBackdrop(false);
@@ -257,8 +256,8 @@
 
         assertScrimTinted(Map.of(
                 mScrimInFront, true,
-                mScrimBehind, true,
-                mScrimForBubble, false));
+                mScrimBehind, true
+        ));
     }
 
     @Test
@@ -274,8 +273,7 @@
 
         assertScrimTinted(Map.of(
                 mScrimInFront, false,
-                mScrimBehind, true,
-                mScrimForBubble, false
+                mScrimBehind, true
         ));
     }
 
@@ -293,8 +291,7 @@
 
         assertScrimTinted(Map.of(
                 mScrimInFront, false,
-                mScrimBehind, true,
-                mScrimForBubble, false
+                mScrimBehind, true
         ));
     }
 
@@ -309,8 +306,7 @@
 
         assertScrimTinted(Map.of(
                 mScrimInFront, true,
-                mScrimBehind, true,
-                mScrimForBubble, false
+                mScrimBehind, true
         ));
 
         assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
@@ -329,8 +325,7 @@
 
         assertScrimTinted(Map.of(
                 mScrimInFront, true,
-                mScrimBehind, true,
-                mScrimForBubble, false
+                mScrimBehind, true
         ));
     }
 
@@ -369,8 +364,7 @@
 
         assertScrimTinted(Map.of(
                 mScrimInFront, true,
-                mScrimBehind, true,
-                mScrimForBubble, false
+                mScrimBehind, true
         ));
     }
 
@@ -389,8 +383,7 @@
 
         assertScrimTinted(Map.of(
                 mScrimInFront, true,
-                mScrimBehind, true,
-                mScrimForBubble, false
+                mScrimBehind, true
         ));
     }
 
@@ -510,8 +503,7 @@
 
         assertScrimTinted(Map.of(
                 mScrimInFront, true,
-                mScrimBehind, true,
-                mScrimForBubble, false
+                mScrimBehind, true
         ));
 
         // ... and when ambient goes dark, front scrim should be semi-transparent
@@ -549,8 +541,7 @@
         assertScrimTinted(Map.of(
                 mScrimInFront, false,
                 mScrimBehind, false,
-                mNotificationsScrim, false,
-                mScrimForBubble, false
+                mNotificationsScrim, false
         ));
     }
 
@@ -570,8 +561,7 @@
         assertScrimTinted(Map.of(
                 mScrimInFront, false,
                 mScrimBehind, true,
-                mNotificationsScrim, false,
-                mScrimForBubble, false
+                mNotificationsScrim, false
         ));
     }
 
@@ -627,8 +617,7 @@
                 mScrimBehind, TRANSPARENT));
         assertScrimTinted(Map.of(
                 mScrimInFront, false,
-                mScrimBehind, false,
-                mScrimForBubble, false
+                mScrimBehind, false
         ));
     }
 
@@ -645,8 +634,7 @@
         assertScrimTinted(Map.of(
                 mNotificationsScrim, false,
                 mScrimInFront, false,
-                mScrimBehind, true,
-                mScrimForBubble, false
+                mScrimBehind, true
         ));
 
         // Back scrim should be visible after start dragging
@@ -657,27 +645,6 @@
                 mScrimBehind, SEMI_TRANSPARENT));
     }
 
-    @Test
-    public void transitionToBubbleExpanded() {
-        mScrimController.transitionTo(ScrimState.BUBBLE_EXPANDED);
-        finishAnimationsImmediately();
-
-        assertScrimTinted(Map.of(
-                mScrimInFront, false,
-                mScrimBehind, false,
-                mScrimForBubble, true
-        ));
-
-        // Front scrim should be transparent
-        assertEquals(ScrimController.TRANSPARENT,
-                mScrimInFront.getViewAlpha(), 0.0f);
-        // Back scrim should be visible
-        assertEquals(ScrimController.BUSY_SCRIM_ALPHA,
-                mScrimBehind.getViewAlpha(), 0.0f);
-        // Bubble scrim should be visible
-        assertEquals(ScrimController.BUBBLE_SCRIM_ALPHA,
-                mScrimForBubble.getViewAlpha(), 0.0f);
-    }
 
     @Test
     public void scrimStateCallback() {
@@ -777,6 +744,28 @@
     }
 
     @Test
+    public void transitionToUnlockedFromOff() {
+        // Simulate unlock with fingerprint without AOD
+        mScrimController.transitionTo(ScrimState.OFF);
+        mScrimController.setPanelExpansion(0f);
+        finishAnimationsImmediately();
+        mScrimController.transitionTo(ScrimState.UNLOCKED);
+
+        finishAnimationsImmediately();
+
+        // All scrims should be transparent at the end of fade transition.
+        assertScrimAlpha(Map.of(
+                mScrimInFront, TRANSPARENT,
+                mScrimBehind, TRANSPARENT));
+
+        // Make sure at the very end of the animation, we're reset to transparent
+        assertScrimTinted(Map.of(
+                mScrimInFront, false,
+                mScrimBehind, true
+        ));
+    }
+
+    @Test
     public void transitionToUnlockedFromAod() {
         // Simulate unlock with fingerprint
         mScrimController.transitionTo(ScrimState.AOD);
@@ -784,26 +773,17 @@
         finishAnimationsImmediately();
         mScrimController.transitionTo(ScrimState.UNLOCKED);
 
-        // Immediately tinted black after the transition starts
-        assertScrimTinted(Map.of(
-                mScrimInFront, true,
-                mScrimBehind, true,
-                mScrimForBubble, true
-        ));
-
         finishAnimationsImmediately();
 
         // All scrims should be transparent at the end of fade transition.
         assertScrimAlpha(Map.of(
                 mScrimInFront, TRANSPARENT,
-                mScrimBehind, TRANSPARENT,
-                mScrimForBubble, TRANSPARENT));
+                mScrimBehind, TRANSPARENT));
 
         // Make sure at the very end of the animation, we're reset to transparent
         assertScrimTinted(Map.of(
                 mScrimInFront, false,
-                mScrimBehind, true,
-                mScrimForBubble, false
+                mScrimBehind, true
         ));
     }
 
@@ -821,8 +801,7 @@
                                 + mScrimInFront.getViewAlpha(), mScrimInFront.getViewAlpha() > 0);
                         assertScrimTinted(Map.of(
                                 mScrimInFront, true,
-                                mScrimBehind, true,
-                                mScrimForBubble, true
+                                mScrimBehind, true
                         ));
                         Assert.assertSame("Scrim should be visible during transition.",
                                 mScrimVisibility, OPAQUE);
@@ -1075,8 +1054,6 @@
                 mScrimInFront.getDefaultFocusHighlightEnabled());
         Assert.assertFalse("Scrim shouldn't have focus highlight",
                 mScrimBehind.getDefaultFocusHighlightEnabled());
-        Assert.assertFalse("Scrim shouldn't have focus highlight",
-                mScrimForBubble.getDefaultFocusHighlightEnabled());
     }
 
     @Test
@@ -1086,7 +1063,7 @@
         HashSet<ScrimState> regularStates = new HashSet<>(Arrays.asList(
                 ScrimState.UNINITIALIZED, ScrimState.KEYGUARD, ScrimState.BOUNCER,
                 ScrimState.BOUNCER_SCRIMMED, ScrimState.BRIGHTNESS_MIRROR, ScrimState.UNLOCKED,
-                ScrimState.BUBBLE_EXPANDED, ScrimState.SHADE_LOCKED, ScrimState.AUTH_SCRIMMED));
+                ScrimState.SHADE_LOCKED, ScrimState.AUTH_SCRIMMED));
 
         for (ScrimState state : ScrimState.values()) {
             if (!lowPowerModeStates.contains(state) && !regularStates.contains(state)) {
@@ -1124,6 +1101,26 @@
     }
 
     @Test
+    public void testDoesntAnimate_whenUnlocking() {
+        // LightRevealScrim will animate the transition, we should only hide the keyguard scrims.
+        ScrimState.UNLOCKED.prepare(ScrimState.KEYGUARD);
+        assertThat(ScrimState.UNLOCKED.getAnimateChange()).isTrue();
+        ScrimState.UNLOCKED.prepare(ScrimState.PULSING);
+        assertThat(ScrimState.UNLOCKED.getAnimateChange()).isFalse();
+
+        ScrimState.UNLOCKED.prepare(ScrimState.KEYGUARD);
+        assertThat(ScrimState.UNLOCKED.getAnimateChange()).isTrue();
+        ScrimState.UNLOCKED.prepare(ScrimState.AOD);
+        assertThat(ScrimState.UNLOCKED.getAnimateChange()).isFalse();
+
+        // LightRevealScrim doesn't animate when AOD is disabled. We need to use the legacy anim.
+        ScrimState.UNLOCKED.prepare(ScrimState.KEYGUARD);
+        assertThat(ScrimState.UNLOCKED.getAnimateChange()).isTrue();
+        ScrimState.UNLOCKED.prepare(ScrimState.OFF);
+        assertThat(ScrimState.UNLOCKED.getAnimateChange()).isTrue();
+    }
+
+    @Test
     public void testScrimsVisible_whenShadeVisible_clippingQs() {
         mScrimController.setClipsQsScrim(true);
         mScrimController.transitionTo(ScrimState.UNLOCKED);
@@ -1181,6 +1178,21 @@
     }
 
     @Test
+    public void testNotificationTransparency_unnocclusion() {
+        mScrimController.transitionTo(ScrimState.KEYGUARD);
+        mScrimController.setUnocclusionAnimationRunning(true);
+
+        assertAlphaAfterExpansion(mNotificationsScrim, /* alpha */ KEYGUARD_SCRIM_ALPHA,
+                /* expansion */ 0.0f);
+        assertAlphaAfterExpansion(mNotificationsScrim, /* alpha */ KEYGUARD_SCRIM_ALPHA,
+                /* expansion */ 1.0f);
+
+        // Verify normal behavior after
+        mScrimController.setUnocclusionAnimationRunning(false);
+        assertAlphaAfterExpansion(mNotificationsScrim, /* alpha */ 0.2f, /* expansion */ 0.4f);
+    }
+
+    @Test
     public void testNotificationTransparency_inKeyguardState() {
         mScrimController.transitionTo(ScrimState.KEYGUARD);
 
@@ -1239,21 +1251,16 @@
             return "behind";
         } else if (scrim == mNotificationsScrim) {
             return "notifications";
-        } else if (scrim == mScrimForBubble) {
-            return "bubble";
         }
         return "unknown_scrim";
     }
 
     /**
-     * If {@link #mScrimForBubble} or {@link #mNotificationsScrim} is not passed in the map
+     * If {@link #mNotificationsScrim} is not passed in the map
      * we assume it must be transparent
      */
     private void assertScrimAlpha(Map<ScrimView, Integer> scrimToAlpha) {
         // Check single scrim visibility.
-        if (!scrimToAlpha.containsKey(mScrimForBubble)) {
-            assertScrimAlpha(mScrimForBubble, TRANSPARENT);
-        }
         if (!scrimToAlpha.containsKey(mNotificationsScrim)) {
             assertScrimAlpha(mNotificationsScrim, TRANSPARENT);
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java
new file mode 100644
index 0000000..52538c7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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.phone;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.StatusBarManager;
+import android.os.PowerManager;
+import android.os.Vibrator;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.testing.FakeMetricsLogger;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.StatusBarStateControllerImpl;
+import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
+import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
+
+import java.util.Optional;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class StatusBarCommandQueueCallbacksTest extends SysuiTestCase {
+    @Mock private StatusBar mStatusBar;
+    @Mock private ShadeController mShadeController;
+    @Mock private CommandQueue mCommandQueue;
+    @Mock private NotificationPanelViewController mNotificationPanelViewController;
+    @Mock private LegacySplitScreen mLegacySplitScreen;
+    @Mock private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
+    private final MetricsLogger mMetricsLogger = new FakeMetricsLogger();
+    @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    @Mock private KeyguardStateController mKeyguardStateController;
+    @Mock private HeadsUpManagerPhone mHeadsUpManager;
+    @Mock private WakefulnessLifecycle mWakefulnessLifecycle;
+    @Mock private DeviceProvisionedController mDeviceProvisionedController;
+    @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+    @Mock private AssistManager mAssistManager;
+    @Mock private DozeServiceHost mDozeServiceHost;
+    @Mock private StatusBarStateControllerImpl mStatusBarStateController;
+    @Mock private NotificationShadeWindowView mNotificationShadeWindowView;
+    @Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
+    @Mock private PowerManager mPowerManager;
+    @Mock private VibratorHelper mVibratorHelper;
+    @Mock private Vibrator mVibrator;
+    @Mock private LightBarController mLightBarController;
+
+    StatusBarCommandQueueCallbacks mSbcqCallbacks;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+
+        mSbcqCallbacks = new StatusBarCommandQueueCallbacks(
+                mStatusBar,
+                mContext,
+                mContext.getResources(),
+                mShadeController,
+                mCommandQueue,
+                mNotificationPanelViewController,
+                Optional.of(mLegacySplitScreen),
+                mRemoteInputQuickSettingsDisabler,
+                mMetricsLogger,
+                mKeyguardUpdateMonitor,
+                mKeyguardStateController,
+                mHeadsUpManager,
+                mWakefulnessLifecycle,
+                mDeviceProvisionedController,
+                mStatusBarKeyguardViewManager,
+                mAssistManager,
+                mDozeServiceHost,
+                mStatusBarStateController,
+                mNotificationShadeWindowView,
+                mNotificationStackScrollLayoutController,
+                mPowerManager,
+                mVibratorHelper,
+                Optional.of(mVibrator),
+                mLightBarController,
+                DEFAULT_DISPLAY);
+
+        when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
+        when(mRemoteInputQuickSettingsDisabler.adjustDisableFlags(anyInt()))
+                .thenAnswer((Answer<Integer>) invocation -> invocation.getArgument(0));
+    }
+
+    @Test
+    public void testDisableNotificationShade() {
+        when(mStatusBar.getDisabled1()).thenReturn(StatusBarManager.DISABLE_NONE);
+        when(mStatusBar.getDisabled2()).thenReturn(StatusBarManager.DISABLE_NONE);
+        when(mCommandQueue.panelsEnabled()).thenReturn(false);
+        mSbcqCallbacks.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
+                StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false);
+
+        verify(mStatusBar).updateQsExpansionEnabled();
+        verify(mShadeController).animateCollapsePanels();
+
+        // Trying to open it does nothing.
+        mSbcqCallbacks.animateExpandNotificationsPanel();
+        verify(mNotificationPanelViewController, never()).expandWithoutQs();
+        mSbcqCallbacks.animateExpandSettingsPanel(null);
+        verify(mNotificationPanelViewController, never()).expand(anyBoolean());
+    }
+
+    @Test
+    public void testEnableNotificationShade() {
+        when(mStatusBar.getDisabled1()).thenReturn(StatusBarManager.DISABLE_NONE);
+        when(mStatusBar.getDisabled2()).thenReturn(StatusBarManager.DISABLE2_NOTIFICATION_SHADE);
+        when(mCommandQueue.panelsEnabled()).thenReturn(true);
+        mSbcqCallbacks.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
+                StatusBarManager.DISABLE2_NONE, false);
+        verify(mStatusBar).updateQsExpansionEnabled();
+        verify(mShadeController, never()).animateCollapsePanels();
+
+        // Can now be opened.
+        mSbcqCallbacks.animateExpandNotificationsPanel();
+        verify(mNotificationPanelViewController).expandWithoutQs();
+        mSbcqCallbacks.animateExpandSettingsPanel(null);
+        verify(mNotificationPanelViewController).expandWithQs();
+    }
+
+    @Test
+    public void testSuppressAmbientDisplay_suppress() {
+        mSbcqCallbacks.suppressAmbientDisplay(true);
+        verify(mDozeServiceHost).setDozeSuppressed(true);
+    }
+
+    @Test
+    public void testSuppressAmbientDisplay_unsuppress() {
+        mSbcqCallbacks.suppressAmbientDisplay(false);
+        verify(mDozeServiceHost).setDozeSuppressed(false);
+    }
+
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
index 10eb71f..1503af8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
@@ -16,32 +16,54 @@
 
 package com.android.systemui.statusbar.phone
 
+import android.content.Context
+import android.content.res.Configuration
 import android.graphics.Rect
 import android.test.suitebuilder.annotation.SmallTest
+import android.view.Display
 import android.view.DisplayCutout
-import android.view.WindowMetrics
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.leak.RotationUtils
 import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE
 import com.android.systemui.util.leak.RotationUtils.ROTATION_NONE
 import com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE
 import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN
 import com.android.systemui.util.leak.RotationUtils.Rotation
+import com.google.common.truth.Truth.assertThat
 import junit.framework.Assert.assertTrue
 import org.junit.Before
 import org.junit.Test
+import org.mockito.ArgumentMatchers.any
 import org.mockito.Mock
 import org.mockito.Mockito.`when`
+import org.mockito.Mockito.mock
 import org.mockito.MockitoAnnotations
 
 @SmallTest
 class StatusBarContentInsetsProviderTest : SysuiTestCase() {
+
     @Mock private lateinit var dc: DisplayCutout
-    @Mock private lateinit var windowMetrics: WindowMetrics
+    @Mock private lateinit var contextMock: Context
+    @Mock private lateinit var display: Display
+    private lateinit var configurationController: ConfigurationController
+
+    private val configuration = Configuration()
 
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
+        `when`(contextMock.display).thenReturn(display)
+
+        context.ensureTestableResources()
+        `when`(contextMock.resources).thenReturn(context.resources)
+        `when`(contextMock.resources.configuration).thenReturn(configuration)
+        `when`(contextMock.createConfigurationContext(any())).thenAnswer {
+            context.createConfigurationContext(it.arguments[0] as Configuration)
+        }
+
+        configurationController = ConfigurationControllerImpl(contextMock)
     }
 
     @Test
@@ -55,15 +77,13 @@
         val chipWidth = 30
         val dotWidth = 10
 
-        `when`(windowMetrics.bounds).thenReturn(screenBounds)
-
         var isRtl = false
         var targetRotation = ROTATION_NONE
         var bounds = calculateInsetsForRotationWithRotatedResources(
                 currentRotation,
                 targetRotation,
                 null,
-                windowMetrics,
+                screenBounds,
                 sbHeightPortrait,
                 minLeftPadding,
                 minRightPadding)
@@ -92,7 +112,7 @@
                 currentRotation,
                 targetRotation,
                 dc,
-                windowMetrics,
+                screenBounds,
                 sbHeightLandscape,
                 minLeftPadding,
                 minRightPadding)
@@ -127,7 +147,6 @@
         val sbHeightLandscape = 60
         val currentRotation = ROTATION_NONE
 
-        `when`(windowMetrics.bounds).thenReturn(screenBounds)
         `when`(dc.boundingRects).thenReturn(listOf(dcBounds))
 
         // THEN rotations which share a short side should use the greater value between rounded
@@ -142,7 +161,7 @@
                 currentRotation,
                 targetRotation,
                 dc,
-                windowMetrics,
+                screenBounds,
                 sbHeightPortrait,
                 minLeftPadding,
                 minRightPadding)
@@ -159,7 +178,7 @@
                 currentRotation,
                 targetRotation,
                 dc,
-                windowMetrics,
+                screenBounds,
                 sbHeightLandscape,
                 minLeftPadding,
                 minRightPadding)
@@ -178,7 +197,7 @@
                 currentRotation,
                 targetRotation,
                 dc,
-                windowMetrics,
+                screenBounds,
                 sbHeightPortrait,
                 minLeftPadding,
                 minRightPadding)
@@ -196,7 +215,7 @@
                 currentRotation,
                 targetRotation,
                 dc,
-                windowMetrics,
+                screenBounds,
                 sbHeightLandscape,
                 minLeftPadding,
                 minRightPadding)
@@ -219,7 +238,6 @@
         val sbHeightLandscape = 60
         val currentRotation = ROTATION_NONE
 
-        `when`(windowMetrics.bounds).thenReturn(screenBounds)
         `when`(dc.boundingRects).thenReturn(listOf(dcBounds))
 
         // THEN only the landscape/seascape rotations should avoid the cutout area because of the
@@ -234,7 +252,7 @@
                 currentRotation,
                 targetRotation,
                 dc,
-                windowMetrics,
+                screenBounds,
                 sbHeightPortrait,
                 minLeftPadding,
                 minRightPadding)
@@ -251,7 +269,7 @@
                 currentRotation,
                 targetRotation,
                 dc,
-                windowMetrics,
+                screenBounds,
                 sbHeightLandscape,
                 minLeftPadding,
                 minRightPadding)
@@ -268,7 +286,7 @@
                 currentRotation,
                 targetRotation,
                 dc,
-                windowMetrics,
+                screenBounds,
                 sbHeightPortrait,
                 minLeftPadding,
                 minRightPadding)
@@ -285,7 +303,7 @@
                 currentRotation,
                 targetRotation,
                 dc,
-                windowMetrics,
+                screenBounds,
                 sbHeightLandscape,
                 minLeftPadding,
                 minRightPadding)
@@ -303,8 +321,6 @@
         val sbHeightPortrait = 100
         val sbHeightLandscape = 60
 
-        `when`(windowMetrics.bounds).thenReturn(screenBounds)
-
         // THEN content insets should only use rounded corner padding
         var targetRotation = ROTATION_NONE
         var expectedBounds = Rect(minLeftPadding,
@@ -316,7 +332,7 @@
                 currentRotation,
                 targetRotation,
                 null, /* no cutout */
-                windowMetrics,
+                screenBounds,
                 sbHeightPortrait,
                 minLeftPadding,
                 minRightPadding)
@@ -332,7 +348,7 @@
                 currentRotation,
                 targetRotation,
                 null, /* no cutout */
-                windowMetrics,
+                screenBounds,
                 sbHeightLandscape,
                 minLeftPadding,
                 minRightPadding)
@@ -348,7 +364,7 @@
                 currentRotation,
                 targetRotation,
                 null, /* no cutout */
-                windowMetrics,
+                screenBounds,
                 sbHeightPortrait,
                 minLeftPadding,
                 minRightPadding)
@@ -364,7 +380,7 @@
                 currentRotation,
                 targetRotation,
                 null, /* no cutout */
-                windowMetrics,
+                screenBounds,
                 sbHeightLandscape,
                 minLeftPadding,
                 minRightPadding)
@@ -382,7 +398,6 @@
         val sbHeightLandscape = 60
         val currentRotation = ROTATION_NONE
 
-        `when`(windowMetrics.bounds).thenReturn(screenBounds)
         `when`(dc.boundingRects).thenReturn(listOf(dcBounds))
 
         // THEN left should be set to the display cutout width, and right should use the minRight
@@ -396,7 +411,7 @@
                 currentRotation,
                 targetRotation,
                 dc,
-                windowMetrics,
+                screenBounds,
                 sbHeightPortrait,
                 minLeftPadding,
                 minRightPadding)
@@ -404,6 +419,67 @@
         assertRects(expectedBounds, bounds, currentRotation, targetRotation)
     }
 
+    @Test
+    fun testDisplayChanged_returnsUpdatedInsets() {
+        // GIVEN: get insets on the first display and switch to the second display
+        val provider = StatusBarContentInsetsProvider(contextMock, configurationController,
+            mock(DumpManager::class.java))
+
+        givenDisplay(
+            screenBounds = Rect(0, 0, 1080, 2160),
+            displayUniqueId = "1"
+        )
+        val firstDisplayInsets = provider.getStatusBarContentInsetsForRotation(ROTATION_NONE)
+        givenDisplay(
+            screenBounds = Rect(0, 0, 800, 600),
+            displayUniqueId = "2"
+        )
+        configurationController.onConfigurationChanged(configuration)
+
+        // WHEN: get insets on the second display
+        val secondDisplayInsets = provider.getStatusBarContentInsetsForRotation(ROTATION_NONE)
+
+        // THEN: insets are updated
+        assertThat(firstDisplayInsets).isNotEqualTo(secondDisplayInsets)
+    }
+
+    @Test
+    fun testDisplayChangedAndReturnedBack_returnsTheSameInsets() {
+        // GIVEN: get insets on the first display, switch to the second display,
+        // get insets and switch back
+        val provider = StatusBarContentInsetsProvider(contextMock, configurationController,
+            mock(DumpManager::class.java))
+        givenDisplay(
+            screenBounds = Rect(0, 0, 1080, 2160),
+            displayUniqueId = "1"
+        )
+        val firstDisplayInsetsFirstCall = provider
+            .getStatusBarContentInsetsForRotation(ROTATION_NONE)
+        givenDisplay(
+            screenBounds = Rect(0, 0, 800, 600),
+            displayUniqueId = "2"
+        )
+        configurationController.onConfigurationChanged(configuration)
+        provider.getStatusBarContentInsetsForRotation(ROTATION_NONE)
+        givenDisplay(
+            screenBounds = Rect(0, 0, 1080, 2160),
+            displayUniqueId = "1"
+        )
+        configurationController.onConfigurationChanged(configuration)
+
+        // WHEN: get insets on the first display again
+        val firstDisplayInsetsSecondCall = provider
+            .getStatusBarContentInsetsForRotation(ROTATION_NONE)
+
+        // THEN: insets for the first and second calls for the first display are the same
+        assertThat(firstDisplayInsetsFirstCall).isEqualTo(firstDisplayInsetsSecondCall)
+    }
+
+    private fun givenDisplay(screenBounds: Rect, displayUniqueId: String) {
+        `when`(display.uniqueId).thenReturn(displayUniqueId)
+        configuration.windowConfiguration.maxBounds = screenBounds
+    }
+
     private fun assertRects(
         expected: Rect,
         actual: Rect,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
index e3263d4..0f1c40b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
@@ -30,8 +30,8 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.statusbar.StatusBarIcon;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.StatusBarMobileView;
 import com.android.systemui.statusbar.StatusBarWifiView;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index c39a906..38f36bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -64,6 +64,8 @@
 
 import java.util.Optional;
 
+import dagger.Lazy;
+
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
@@ -103,6 +105,8 @@
     private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
     @Mock
     private KeyguardMessageArea mKeyguardMessageArea;
+    @Mock
+    private Lazy<ShadeController> mShadeController;
 
     private WakefulnessLifecycle mWakefulnessLifecycle;
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -133,7 +137,8 @@
                 mKeyguardBouncerFactory,
                 mWakefulnessLifecycle,
                 mUnlockedScreenOffAnimationController,
-                mKeyguardMessageAreaFactory);
+                mKeyguardMessageAreaFactory,
+                mShadeController);
         mStatusBarKeyguardViewManager.registerStatusBar(mStatusBar, mContainer,
                 mNotificationPanelView, mBiometrucUnlockController,
                 mNotificationContainer, mBypassController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 83bf96b..72a3d66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -53,10 +53,10 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.assist.AssistManager;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationClickNotifier;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationPresenter;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index fd85c44..c80c072 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -31,9 +31,9 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.logging.testing.FakeMetricsLogger;
+import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.ForegroundServiceNotificationListener;
 import com.android.systemui.InitController;
 import com.android.systemui.SysuiTestCase;
@@ -51,7 +51,6 @@
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -59,6 +58,7 @@
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import org.junit.Before;
@@ -66,14 +66,10 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 
-import java.util.ArrayList;
-
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper()
 public class StatusBarNotificationPresenterTest extends SysuiTestCase {
-
-
     private StatusBarNotificationPresenter mStatusBarNotificationPresenter;
     private NotificationInterruptStateProvider mNotificationInterruptStateProvider =
             mock(NotificationInterruptStateProvider.class);
@@ -86,27 +82,16 @@
 
     @Before
     public void setup() {
-        NotificationRemoteInputManager notificationRemoteInputManager =
-                mock(NotificationRemoteInputManager.class);
         mMetricsLogger = new FakeMetricsLogger();
-        mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
+        LockscreenGestureLogger lockscreenGestureLogger = new LockscreenGestureLogger(
+                mMetricsLogger);
         mCommandQueue = new CommandQueue(mContext);
         mDependency.injectTestDependency(StatusBarStateController.class,
                 mock(SysuiStatusBarStateController.class));
         mDependency.injectTestDependency(ShadeController.class, mShadeController);
-        mDependency.injectTestDependency(NotificationRemoteInputManager.class,
-                notificationRemoteInputManager);
-        mDependency.injectMockDependency(NotificationViewHierarchyManager.class);
         mDependency.injectMockDependency(NotificationRemoteInputManager.Callback.class);
-        mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
-        mDependency.injectMockDependency(NotificationMediaManager.class);
-        mDependency.injectMockDependency(VisualStabilityManager.class);
-        mDependency.injectMockDependency(NotificationGutsManager.class);
         mDependency.injectMockDependency(NotificationShadeWindowController.class);
         mDependency.injectMockDependency(ForegroundServiceNotificationListener.class);
-        NotificationEntryManager entryManager =
-                mDependency.injectMockDependency(NotificationEntryManager.class);
-        when(entryManager.getActiveNotificationsForCurrentUser()).thenReturn(new ArrayList<>());
 
         NotificationShadeWindowView notificationShadeWindowView =
                 mock(NotificationShadeWindowView.class);
@@ -126,8 +111,19 @@
                 mock(KeyguardStateController.class),
                 mock(KeyguardIndicationController.class), mStatusBar,
                 mock(ShadeControllerImpl.class), mock(LockscreenShadeTransitionController.class),
-                mCommandQueue, mInitController,
-                mNotificationInterruptStateProvider);
+                mCommandQueue,
+                mock(NotificationViewHierarchyManager.class),
+                mock(NotificationLockscreenUserManager.class),
+                mock(SysuiStatusBarStateController.class),
+                mock(NotificationEntryManager.class),
+                mock(NotificationMediaManager.class),
+                mock(NotificationGutsManager.class),
+                mock(KeyguardUpdateMonitor.class),
+                lockscreenGestureLogger,
+                mInitController,
+                mNotificationInterruptStateProvider,
+                mock(NotificationRemoteInputManager.class),
+                mock(ConfigurationController.class));
         mInitController.executePostInitTasks();
         ArgumentCaptor<NotificationInterruptSuppressor> suppressorCaptor =
                 ArgumentCaptor.forClass(NotificationInterruptSuppressor.class);
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 2a58f7c..2f7e39b3 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
@@ -18,7 +18,6 @@
 
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
-import static android.view.Display.DEFAULT_DISPLAY;
 
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
@@ -40,7 +39,7 @@
 
 import android.app.IWallpaperManager;
 import android.app.Notification;
-import android.app.StatusBarManager;
+import android.app.WallpaperManager;
 import android.app.trust.TrustManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -78,29 +77,25 @@
 import com.android.systemui.InitController;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.demomode.DemoModeController;
-import com.android.systemui.keyguard.DismissCallbackRegistry;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.navigationbar.NavigationBarController;
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
-import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.PluginDependencyProvider;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.recents.ScreenPinningRequest;
 import com.android.systemui.settings.brightness.BrightnessSlider;
 import com.android.systemui.shared.plugins.PluginManager;
-import com.android.unfold.config.UnfoldTransitionConfig;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.NotificationListener;
@@ -110,15 +105,12 @@
 import com.android.systemui.statusbar.NotificationShadeDepthController;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
+import com.android.systemui.statusbar.OperatorNameViewController;
 import com.android.systemui.statusbar.PulseExpansionHandler;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.StatusBarStateControllerImpl;
-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.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationFilter;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
@@ -143,11 +135,13 @@
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
 import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.tuner.TunerService;
 import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
+import com.android.systemui.unfold.config.UnfoldTransitionConfig;
 import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.concurrency.MessageRouterImpl;
 import com.android.systemui.util.time.FakeSystemClock;
 import com.android.systemui.volume.VolumeComponent;
 import com.android.systemui.wmshell.BubblesManager;
@@ -165,8 +159,6 @@
 import java.io.PrintWriter;
 import java.util.Optional;
 
-import javax.inject.Provider;
-
 import dagger.Lazy;
 
 @SmallTest
@@ -181,7 +173,6 @@
 
     @Mock private NotificationsController mNotificationsController;
     @Mock private LightBarController mLightBarController;
-    @Mock private StatusBarSignalPolicy mStatusBarSignalPolicy;
     @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     @Mock private KeyguardStateController mKeyguardStateController;
     @Mock private KeyguardIndicationController mKeyguardIndicationController;
@@ -206,7 +197,6 @@
     @Mock private BatteryController mBatteryController;
     @Mock private DeviceProvisionedController mDeviceProvisionedController;
     @Mock private StatusBarNotificationPresenter mNotificationPresenter;
-    @Mock private NotificationEntryListener mEntryListener;
     @Mock private NotificationFilter mNotificationFilter;
     @Mock private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
     @Mock private NotificationLogger.ExpansionStateLogger mExpansionStateLogger;
@@ -214,10 +204,10 @@
     @Mock private NotificationShadeWindowView mNotificationShadeWindowView;
     @Mock private BroadcastDispatcher mBroadcastDispatcher;
     @Mock private AssistManager mAssistManager;
+    @Mock private NotificationEntryManager mNotificationEntryManager;
     @Mock private NotificationGutsManager mNotificationGutsManager;
     @Mock private NotificationMediaManager mNotificationMediaManager;
     @Mock private NavigationBarController mNavigationBarController;
-    @Mock private AccessibilityFloatingMenuController mAccessibilityFloatingMenuController;
     @Mock private BypassHeadsUpNotifier mBypassHeadsUpNotifier;
     @Mock private SysuiColorExtractor mColorExtractor;
     @Mock private ColorExtractor.GradientColors mGradientColors;
@@ -229,7 +219,6 @@
     @Mock private NotificationViewHierarchyManager mNotificationViewHierarchyManager;
     @Mock private UserSwitcherController mUserSwitcherController;
     @Mock private NetworkController mNetworkController;
-    @Mock private VibratorHelper mVibratorHelper;
     @Mock private BubblesManager mBubblesManager;
     @Mock private Bubbles mBubbles;
     @Mock private NotificationShadeWindowController mNotificationShadeWindowController;
@@ -237,26 +226,23 @@
     @Mock private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
     @Mock private DozeParameters mDozeParameters;
     @Mock private Lazy<LockscreenWallpaper> mLockscreenWallpaperLazy;
+    @Mock private LockscreenGestureLogger mLockscreenGestureLogger;
     @Mock private LockscreenWallpaper mLockscreenWallpaper;
     @Mock private DozeServiceHost mDozeServiceHost;
     @Mock private ViewMediatorCallback mKeyguardVieMediatorCallback;
-    @Mock private KeyguardLiftController mKeyguardLiftController;
     @Mock private VolumeComponent mVolumeComponent;
     @Mock private CommandQueue mCommandQueue;
-    @Mock private Provider<StatusBarComponent.Builder> mStatusBarComponentBuilderProvider;
-    @Mock private StatusBarComponent.Builder mStatusBarComponentBuilder;
+    @Mock private StatusBarComponent.Factory mStatusBarComponentFactory;
     @Mock private StatusBarComponent mStatusBarComponent;
     @Mock private PluginManager mPluginManager;
     @Mock private LegacySplitScreen mLegacySplitScreen;
-    @Mock private SuperStatusBarViewFactory mSuperStatusBarViewFactory;
+    @Mock private StatusBarWindowView mStatusBarWindowView;
     @Mock private LightsOutNotifController mLightsOutNotifController;
     @Mock private ViewMediatorCallback mViewMediatorCallback;
-    @Mock private DismissCallbackRegistry mDismissCallbackRegistry;
     @Mock private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
     @Mock private ScreenPinningRequest mScreenPinningRequest;
     @Mock private StatusBarNotificationActivityStarter.Builder
             mStatusBarNotificationActivityStarterBuilder;
-    @Mock private DarkIconDispatcher mDarkIconDispatcher;
     @Mock private PluginDependencyProvider mPluginDependencyProvider;
     @Mock private KeyguardDismissUtil mKeyguardDismissUtil;
     @Mock private ExtensionController mExtensionController;
@@ -265,7 +251,6 @@
     @Mock private DemoModeController mDemoModeController;
     @Mock private Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy;
     @Mock private BrightnessSlider.Factory mBrightnessSliderFactory;
-    @Mock private WiredChargingRippleController mWiredChargingRippleController;
     @Mock private UnfoldTransitionConfig mUnfoldTransitionConfig;
     @Mock private Lazy<UnfoldLightRevealOverlayAnimation> mUnfoldLightRevealOverlayAnimationLazy;
     @Mock private OngoingCallController mOngoingCallController;
@@ -274,12 +259,18 @@
     @Mock private StatusBarIconController mIconController;
     @Mock private LockscreenShadeTransitionController mLockscreenTransitionController;
     @Mock private FeatureFlags mFeatureFlags;
-    @Mock private IWallpaperManager mWallpaperManager;
+    @Mock private WallpaperManager mWallpaperManager;
+    @Mock private IWallpaperManager mIWallpaperManager;
     @Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
     @Mock private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
+    @Mock private TunerService mTunerService;
     @Mock private StartingSurface mStartingSurface;
+    @Mock private OperatorNameViewController mOperatorNameViewController;
+    @Mock private OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
     private ShadeController mShadeController;
-    private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
+    private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
+    private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
+    private FakeExecutor mUiBgExecutor = new FakeExecutor(mFakeSystemClock);
     private InitController mInitController = new InitController();
 
     @Before
@@ -335,7 +326,7 @@
         }).when(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable(any());
 
         WakefulnessLifecycle wakefulnessLifecycle =
-                new WakefulnessLifecycle(mContext, mWallpaperManager);
+                new WakefulnessLifecycle(mContext, mIWallpaperManager);
         wakefulnessLifecycle.dispatchStartedWakingUp(PowerManager.WAKE_REASON_UNKNOWN);
         wakefulnessLifecycle.dispatchFinishedWakingUp();
 
@@ -346,8 +337,7 @@
         when(mLockscreenWallpaperLazy.get()).thenReturn(mLockscreenWallpaper);
         when(mBiometricUnlockControllerLazy.get()).thenReturn(mBiometricUnlockController);
 
-        when(mStatusBarComponentBuilderProvider.get()).thenReturn(mStatusBarComponentBuilder);
-        when(mStatusBarComponentBuilder.build()).thenReturn(mStatusBarComponent);
+        when(mStatusBarComponentFactory.create()).thenReturn(mStatusBarComponent);
         when(mStatusBarComponent.getNotificationShadeWindowViewController()).thenReturn(
                 mNotificationShadeWindowViewController);
 
@@ -356,13 +346,15 @@
                 mStatusBarKeyguardViewManager, mContext.getSystemService(WindowManager.class),
                 () -> Optional.of(mStatusBar), () -> mAssistManager, Optional.of(mBubbles));
 
+        when(mOperatorNameViewControllerFactory.create(any()))
+                .thenReturn(mOperatorNameViewController);
+
         mStatusBar = new StatusBar(
                 mContext,
                 mNotificationsController,
                 mLightBarController,
                 mAutoHideController,
                 mKeyguardUpdateMonitor,
-                mStatusBarSignalPolicy,
                 mPulseExpansionHandler,
                 mNotificationWakeUpCoordinator,
                 mKeyguardBypassController,
@@ -373,11 +365,7 @@
                 new FalsingManagerFake(),
                 new FalsingCollectorFake(),
                 mBroadcastDispatcher,
-                new RemoteInputQuickSettingsDisabler(
-                        mContext,
-                        configurationController,
-                        mCommandQueue
-                ),
+                mNotificationEntryManager,
                 mNotificationGutsManager,
                 notificationLogger,
                 mNotificationInterruptStateProvider,
@@ -396,33 +384,31 @@
                 new ScreenLifecycle(),
                 wakefulnessLifecycle,
                 mStatusBarStateController,
-                mVibratorHelper,
                 Optional.of(mBubblesManager),
                 Optional.of(mBubbles),
                 mVisualStabilityManager,
                 mDeviceProvisionedController,
                 mNavigationBarController,
-                mAccessibilityFloatingMenuController,
                 () -> mAssistManager,
                 configurationController,
                 mNotificationShadeWindowController,
                 mDozeParameters,
                 mScrimController,
-                mKeyguardLiftController,
                 mLockscreenWallpaperLazy,
+                mLockscreenGestureLogger,
                 mBiometricUnlockControllerLazy,
                 mDozeServiceHost,
                 mPowerManager, mScreenPinningRequest,
                 mDozeScrimController,
                 mVolumeComponent,
                 mCommandQueue,
-                mStatusBarComponentBuilderProvider,
+                mStatusBarComponentFactory,
                 mPluginManager,
                 Optional.of(mLegacySplitScreen),
                 mLightsOutNotifController,
                 mStatusBarNotificationActivityStarterBuilder,
                 mShadeController,
-                mSuperStatusBarViewFactory,
+                mStatusBarWindowView,
                 mStatusBarKeyguardViewManager,
                 mViewMediatorCallback,
                 mInitController,
@@ -431,15 +417,14 @@
                 mKeyguardDismissUtil,
                 mExtensionController,
                 mUserInfoControllerImpl,
+                mOperatorNameViewControllerFactory,
                 mPhoneStatusBarPolicy,
                 mKeyguardIndicationController,
-                mDismissCallbackRegistry,
                 mDemoModeController,
                 mNotificationShadeDepthControllerLazy,
                 mStatusBarTouchableRegionManager,
                 mNotificationIconAreaController,
                 mBrightnessSliderFactory,
-                mWiredChargingRippleController,
                 mUnfoldTransitionConfig,
                 mUnfoldLightRevealOverlayAnimationLazy,
                 mOngoingCallController,
@@ -449,8 +434,13 @@
                 mLockscreenTransitionController,
                 mFeatureFlags,
                 mKeyguardUnlockAnimationController,
+                new Handler(TestableLooper.get(this).getLooper()),
+                mMainExecutor,
+                new MessageRouterImpl(mMainExecutor),
+                mWallpaperManager,
                 mUnlockedScreenOffAnimationController,
-                Optional.of(mStartingSurface));
+                Optional.of(mStartingSurface),
+                mTunerService);
         when(mKeyguardViewMediator.registerStatusBar(any(StatusBar.class), any(ViewGroup.class),
                 any(NotificationPanelViewController.class), any(BiometricUnlockController.class),
                 any(ViewGroup.class), any(KeyguardBypassController.class)))
@@ -713,7 +703,7 @@
         } catch (RemoteException e) {
             fail();
         }
-        TestableLooper.get(this).processAllMessages();
+        mMainExecutor.runAllReady();
     }
 
     @Test
@@ -732,7 +722,7 @@
         } catch (RemoteException e) {
             fail();
         }
-        TestableLooper.get(this).processAllMessages();
+        mMainExecutor.runAllReady();
     }
 
     @Test
@@ -750,32 +740,7 @@
         } catch (RemoteException e) {
             fail();
         }
-        TestableLooper.get(this).processAllMessages();
-    }
-
-    @Test
-    public void testDisableExpandStatusBar() {
-        mStatusBar.setBarStateForTest(StatusBarState.SHADE);
-        mStatusBar.setUserSetupForTest(true);
-        when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
-
-        when(mCommandQueue.panelsEnabled()).thenReturn(false);
-        mStatusBar.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
-                StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false);
-        verify(mNotificationPanelViewController).setQsExpansionEnabledPolicy(false);
-        mStatusBar.animateExpandNotificationsPanel();
-        verify(mNotificationPanelViewController, never()).expand(anyBoolean());
-        mStatusBar.animateExpandSettingsPanel(null);
-        verify(mNotificationPanelViewController, never()).expand(anyBoolean());
-
-        when(mCommandQueue.panelsEnabled()).thenReturn(true);
-        mStatusBar.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
-                StatusBarManager.DISABLE2_NONE, false);
-        verify(mNotificationPanelViewController).setQsExpansionEnabledPolicy(true);
-        mStatusBar.animateExpandNotificationsPanel();
-        verify(mNotificationPanelViewController).expandWithoutQs();
-        mStatusBar.animateExpandSettingsPanel(null);
-        verify(mNotificationPanelViewController).expandWithQs();
+        mMainExecutor.runAllReady();
     }
 
     @Test
@@ -790,15 +755,6 @@
     }
 
     @Test
-    @RunWithLooper(setAsMainLooper = true)
-    public void testUpdateKeyguardState_DoesNotCrash() {
-        mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
-        when(mLockscreenUserManager.getCurrentProfiles()).thenReturn(
-                new SparseArray<>());
-        mStatusBar.onStateChanged(StatusBarState.SHADE);
-    }
-
-    @Test
     public void testFingerprintNotification_UpdatesScrims() {
         mStatusBar.notifyBiometricAuthModeChanged();
         verify(mScrimController).transitionTo(any(), any());
@@ -814,6 +770,30 @@
     }
 
     @Test
+    public void testTransitionLaunch_goesToUnlocked() {
+        mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
+        mStatusBar.showKeyguardImpl();
+
+        // Starting a pulse should change the scrim controller to the pulsing state
+        when(mNotificationPanelViewController.isLaunchTransitionRunning()).thenReturn(true);
+        when(mNotificationPanelViewController.isLaunchingAffordanceWithPreview()).thenReturn(true);
+        mStatusBar.updateScrimController();
+        verify(mScrimController).transitionTo(eq(ScrimState.UNLOCKED), any());
+    }
+
+    @Test
+    public void testTransitionLaunch_noPreview_doesntGoUnlocked() {
+        mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
+        mStatusBar.showKeyguardImpl();
+
+        // Starting a pulse should change the scrim controller to the pulsing state
+        when(mNotificationPanelViewController.isLaunchTransitionRunning()).thenReturn(true);
+        when(mNotificationPanelViewController.isLaunchingAffordanceWithPreview()).thenReturn(false);
+        mStatusBar.updateScrimController();
+        verify(mScrimController).transitionTo(eq(ScrimState.KEYGUARD));
+    }
+
+    @Test
     public void testSetOccluded_propagatesToScrimController() {
         mStatusBar.setOccluded(true);
         verify(mScrimController).setKeyguardOccluded(eq(true));
@@ -905,18 +885,6 @@
     }
 
     @Test
-    public void testSuppressAmbientDisplay_suppress() {
-        mStatusBar.suppressAmbientDisplay(true);
-        verify(mDozeServiceHost).setDozeSuppressed(true);
-    }
-
-    @Test
-    public void testSuppressAmbientDisplay_unsuppress() {
-        mStatusBar.suppressAmbientDisplay(false);
-        verify(mDozeServiceHost).setDozeSuppressed(false);
-    }
-
-    @Test
     public void testUpdateResources_updatesBouncer() {
         mStatusBar.updateResources();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index d36cb0b..4476fd8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -34,7 +34,7 @@
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
@@ -184,6 +184,21 @@
                 .isGreaterThan(0)
     }
 
+    /** Regression test for b/194731244. */
+    @Test
+    fun onEntryUpdated_calledManyTimes_uidObserverUnregisteredManyTimes() {
+        val numCalls = 4
+
+        for (i in 0 until numCalls) {
+            // Re-create the notification each time so that it's considered a different object and
+            // observers will get re-registered (and hopefully unregistered).
+            notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+        }
+
+        // There should be 1 observer still registered, so we should unregister n-1 times.
+        verify(mockIActivityManager, times(numCalls - 1)).unregisterUidObserver(any())
+    }
+
     /**
      * If a call notification is never added before #onEntryRemoved is called, then the listener
      * should never be notified.
@@ -223,7 +238,6 @@
         verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean())
     }
 
-
     @Test
     fun onEntryRemoved_notifKeyDoesNotMatchOngoingCallNotif_listenerNotNotified() {
         notifCollectionListener.onEntryAdded(createOngoingCallNotifEntry())
@@ -349,7 +363,7 @@
         // Update the process to visible.
         uidObserver.onUidStateChanged(CALL_UID, PROC_STATE_VISIBLE, 0, 0)
         mainExecutor.advanceClockToLast()
-        mainExecutor.runAllReady();
+        mainExecutor.runAllReady()
 
         verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean())
     }
@@ -370,7 +384,7 @@
         // Update the process to invisible.
         uidObserver.onUidStateChanged(CALL_UID, PROC_STATE_INVISIBLE, 0, 0)
         mainExecutor.advanceClockToLast()
-        mainExecutor.runAllReady();
+        mainExecutor.runAllReady()
 
         verify(mockOngoingCallListener).onOngoingCallStateChanged(anyBoolean())
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
new file mode 100644
index 0000000..30717f4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
@@ -0,0 +1,239 @@
+/*
+ * 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.policy;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+
+import android.hardware.devicestate.DeviceStateManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableResources;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.view.RotationPolicy;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.settings.FakeSettings;
+import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.util.wrapper.RotationPolicyWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase {
+
+    private static final String[] DEFAULT_SETTINGS = new String[]{
+            "0:0",
+            "1:2"
+    };
+
+    private final FakeSettings mFakeSettings = new FakeSettings();
+    private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
+    private final FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock);
+    @Mock DeviceStateManager mDeviceStateManager;
+    RotationPolicyWrapper mFakeRotationPolicy = new FakeRotationPolicy();
+    DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController;
+    private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(/* testClass= */ this);
+        TestableResources resources = mContext.getOrCreateTestableResources();
+
+        ArgumentCaptor<DeviceStateManager.DeviceStateCallback> deviceStateCallbackArgumentCaptor =
+                ArgumentCaptor.forClass(
+                        DeviceStateManager.DeviceStateCallback.class);
+
+        mDeviceStateRotationLockSettingController = new DeviceStateRotationLockSettingController(
+                mFakeSettings,
+                mFakeRotationPolicy,
+                mDeviceStateManager,
+                mFakeExecutor,
+                DEFAULT_SETTINGS
+        );
+
+        mDeviceStateRotationLockSettingController.setListening(true);
+        verify(mDeviceStateManager).registerCallback(any(),
+                deviceStateCallbackArgumentCaptor.capture());
+        mDeviceStateCallback = deviceStateCallbackArgumentCaptor.getValue();
+    }
+
+    @Test
+    public void whenSavedSettingsEmpty_defaultsLoadedAndSaved() {
+        mFakeSettings.putStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK, "",
+                UserHandle.USER_CURRENT);
+
+        mDeviceStateRotationLockSettingController.initialize();
+
+        assertThat(mFakeSettings
+                .getStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                        UserHandle.USER_CURRENT))
+                .isEqualTo("0:0:1:2");
+    }
+
+    @Test
+    public void whenNoSavedValueForDeviceState_assumeIgnored() {
+        mFakeSettings.putStringForUser(
+                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                /* value= */"0:2:1:2",
+                UserHandle.USER_CURRENT);
+        mFakeRotationPolicy.setRotationLock(true);
+        mDeviceStateRotationLockSettingController.initialize();
+
+        mDeviceStateCallback.onStateChanged(1);
+        assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
+
+        // Settings only exist for state 0 and 1
+        mDeviceStateCallback.onStateChanged(2);
+
+        assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
+    }
+
+    @Test
+    public void whenDeviceStateSwitched_loadCorrectSetting() {
+        mFakeSettings.putStringForUser(
+                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                /* value= */"0:2:1:1",
+                UserHandle.USER_CURRENT);
+        mFakeRotationPolicy.setRotationLock(true);
+        mDeviceStateRotationLockSettingController.initialize();
+
+        mDeviceStateCallback.onStateChanged(0);
+        assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
+
+        mDeviceStateCallback.onStateChanged(1);
+        assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
+
+    }
+
+    @Test
+    public void whenUserChangesSetting_saveSettingForCurrentState() {
+        mFakeSettings.putStringForUser(
+                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                /* value= */"0:1:1:2",
+                UserHandle.USER_CURRENT);
+        mFakeRotationPolicy.setRotationLock(true);
+        mDeviceStateRotationLockSettingController.initialize();
+
+        mDeviceStateCallback.onStateChanged(0);
+        assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
+
+        mDeviceStateRotationLockSettingController
+                .onRotationLockStateChanged(/* rotationLocked= */false,
+                        /* affordanceVisible= */ true);
+
+        assertThat(mFakeSettings
+                .getStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                        UserHandle.USER_CURRENT))
+                .isEqualTo("0:2:1:2");
+    }
+
+
+    @Test
+    public void whenDeviceStateSwitchedToIgnoredState_usePreviousSetting() {
+        mFakeSettings.putStringForUser(
+                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                /* value= */"0:0:1:2",
+                UserHandle.USER_CURRENT);
+        mFakeRotationPolicy.setRotationLock(true);
+        mDeviceStateRotationLockSettingController.initialize();
+
+        mDeviceStateCallback.onStateChanged(1);
+        assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
+
+        mDeviceStateCallback.onStateChanged(0);
+        assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
+    }
+
+    @Test
+    public void whenDeviceStateSwitchedToIgnoredState_newSettingsSaveForPreviousState() {
+        mFakeSettings.putStringForUser(
+                Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                /* value= */"0:0:1:2",
+                UserHandle.USER_CURRENT);
+        mFakeRotationPolicy.setRotationLock(true);
+        mDeviceStateRotationLockSettingController.initialize();
+
+        mDeviceStateCallback.onStateChanged(1);
+        assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
+
+        mDeviceStateCallback.onStateChanged(0);
+        assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
+
+        mDeviceStateRotationLockSettingController
+                .onRotationLockStateChanged(/* rotationLocked= */true,
+                        /* affordanceVisible= */ true);
+
+        assertThat(mFakeSettings
+                .getStringForUser(Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                        UserHandle.USER_CURRENT))
+                .isEqualTo("0:0:1:1");
+    }
+
+    private static class FakeRotationPolicy implements RotationPolicyWrapper {
+
+        private boolean mRotationLock;
+
+        @Override
+        public void setRotationLock(boolean enabled) {
+            mRotationLock = enabled;
+        }
+
+        @Override
+        public void setRotationLockAtAngle(boolean enabled, int rotation) {
+            mRotationLock = enabled;
+        }
+
+        @Override
+        public int getRotationLockOrientation() {
+            throw new AssertionError("Not implemented");
+        }
+
+        @Override
+        public boolean isRotationLockToggleVisible() {
+            throw new AssertionError("Not implemented");
+        }
+
+        @Override
+        public boolean isRotationLocked() {
+            return mRotationLock;
+        }
+
+        @Override
+        public void registerRotationPolicyListener(RotationPolicy.RotationPolicyListener listener,
+                int userHandle) {
+            throw new AssertionError("Not implemented");
+        }
+
+        @Override
+        public void unregisterRotationPolicyListener(
+                RotationPolicy.RotationPolicyListener listener) {
+            throw new AssertionError("Not implemented");
+        }
+    }
+}
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 f2de26c..c488ee9 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
@@ -70,7 +70,8 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.demomode.DemoModeController;
-import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
 import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
@@ -241,7 +242,9 @@
                 mMockBd,
                 mDemoModeController,
                 mCarrierConfigTracker,
-                mFeatureFlags);
+                mFeatureFlags,
+                mock(DumpManager.class)
+        );
         setupNetworkController();
 
         // Trigger blank callbacks to always get the current state (some tests don't trigger
@@ -309,7 +312,8 @@
                         mock(AccessPointControllerImpl.class),
                         mock(DataUsageController.class), mMockSubDefaults,
                         mock(DeviceProvisionedController.class), mMockBd, mDemoModeController,
-                        mCarrierConfigTracker, mFeatureFlags);
+                        mCarrierConfigTracker, mFeatureFlags,
+                        mock(DumpManager.class));
 
         setupNetworkController();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index bc4c2b6..3433a14 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -21,6 +21,7 @@
 
 import com.android.settingslib.mobile.TelephonyIcons;
 import com.android.settingslib.net.DataUsageController;
+import com.android.systemui.dump.DumpManager;
 import com.android.systemui.util.CarrierConfigTracker;
 
 import org.junit.Test;
@@ -113,7 +114,7 @@
                 mock(AccessPointControllerImpl.class),
                 mock(DataUsageController.class), mMockSubDefaults,
                 mock(DeviceProvisionedController.class), mMockBd, mDemoModeController,
-                mock(CarrierConfigTracker.class), mFeatureFlags);
+                mock(CarrierConfigTracker.class), mFeatureFlags, mock(DumpManager.class));
         setupNetworkController();
 
         setupDefaultSignal();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
index 5090b0d..4ff1301 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
@@ -41,6 +41,7 @@
 import com.android.settingslib.mobile.TelephonyIcons;
 import com.android.settingslib.net.DataUsageController;
 import com.android.systemui.R;
+import com.android.systemui.dump.DumpManager;
 import com.android.systemui.util.CarrierConfigTracker;
 
 import org.junit.Test;
@@ -67,7 +68,8 @@
                 Looper.getMainLooper(), mFakeExecutor, mCallbackHandler,
                 mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
                 mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd,
-                mDemoModeController, mock(CarrierConfigTracker.class), mFeatureFlags);
+                mDemoModeController, mock(CarrierConfigTracker.class), mFeatureFlags,
+                mock(DumpManager.class));
         setupNetworkController();
 
         verifyLastMobileDataIndicators(false, -1, 0);
@@ -87,7 +89,8 @@
                 Looper.getMainLooper(), mFakeExecutor, mCallbackHandler,
                 mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
                 mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd,
-                mDemoModeController, mock(CarrierConfigTracker.class), mFeatureFlags);
+                mDemoModeController, mock(CarrierConfigTracker.class), mFeatureFlags,
+                mock(DumpManager.class));
         mNetworkController.registerListeners();
 
         // Wait for the main looper to execute the previous command
@@ -155,7 +158,8 @@
                 Looper.getMainLooper(), mFakeExecutor, mCallbackHandler,
                 mock(AccessPointControllerImpl.class), mock(DataUsageController.class),
                 mMockSubDefaults, mock(DeviceProvisionedController.class), mMockBd,
-                mDemoModeController, mock(CarrierConfigTracker.class), mFeatureFlags);
+                mDemoModeController, mock(CarrierConfigTracker.class), mFeatureFlags,
+                mock(DumpManager.class));
         setupNetworkController();
 
         // No Subscriptions.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index 57198db..4a5770d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -238,7 +238,7 @@
         mNetworkController.setNoNetworksAvailable(false);
         setWifiStateForVcn(true, testSsid);
         setWifiLevelForVcn(0);
-        verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[0][0]);
+        verifyLastMobileDataIndicatorsForVcn(true, 0, TelephonyIcons.ICON_CWF, false);
 
         mNetworkController.setNoNetworksAvailable(true);
         for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
@@ -246,11 +246,11 @@
 
             setConnectivityViaCallbackInNetworkControllerForVcn(
                     NetworkCapabilities.TRANSPORT_CELLULAR, true, true, mVcnTransportInfo);
-            verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
+            verifyLastMobileDataIndicatorsForVcn(true, testLevel, TelephonyIcons.ICON_CWF, true);
 
             setConnectivityViaCallbackInNetworkControllerForVcn(
                     NetworkCapabilities.TRANSPORT_CELLULAR, false, true, mVcnTransportInfo);
-            verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[0][testLevel]);
+            verifyLastMobileDataIndicatorsForVcn(true, testLevel, TelephonyIcons.ICON_CWF, false);
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java
new file mode 100644
index 0000000..0581264
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RotationLockControllerImplTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.policy;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableResources;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.view.RotationPolicy;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.wrapper.RotationPolicyWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class RotationLockControllerImplTest extends SysuiTestCase {
+
+    private static final String[] DEFAULT_SETTINGS = new String[]{
+            "0:0",
+            "1:2"
+    };
+
+    @Mock RotationPolicyWrapper mRotationPolicyWrapper;
+    @Mock DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController;
+
+    private TestableResources mResources;
+    private ArgumentCaptor<RotationPolicy.RotationPolicyListener>
+            mRotationPolicyListenerCaptor;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(/* testClass= */ this);
+        mResources = mContext.getOrCreateTestableResources();
+
+        mRotationPolicyListenerCaptor = ArgumentCaptor.forClass(
+                RotationPolicy.RotationPolicyListener.class);
+    }
+
+    @Test
+    public void whenFlagOff_doesntInteractWithDeviceStateRotationController() {
+        createRotationLockController(new String[0]);
+
+        verifyZeroInteractions(mDeviceStateRotationLockSettingController);
+    }
+
+    @Test
+    public void whenFlagOn_setListeningSetsListeningOnDeviceStateRotationController() {
+        createRotationLockController();
+
+        verify(mDeviceStateRotationLockSettingController).setListening(/* listening= */ true);
+    }
+
+    @Test
+    public void whenFlagOn_initializesDeviceStateRotationController() {
+        createRotationLockController();
+
+        verify(mDeviceStateRotationLockSettingController).initialize();
+    }
+
+    @Test
+    public void whenFlagOn_dviceStateRotationControllerAddedToCallbacks() {
+        createRotationLockController();
+        captureRotationPolicyListener().onChange();
+
+        verify(mDeviceStateRotationLockSettingController)
+                .onRotationLockStateChanged(anyBoolean(), anyBoolean());
+    }
+
+    private RotationPolicy.RotationPolicyListener captureRotationPolicyListener() {
+        verify(mRotationPolicyWrapper)
+                .registerRotationPolicyListener(mRotationPolicyListenerCaptor.capture(), anyInt());
+        return mRotationPolicyListenerCaptor.getValue();
+    }
+
+    private void createRotationLockController() {
+        createRotationLockController(DEFAULT_SETTINGS);
+    }
+    private void createRotationLockController(String[] deviceStateRotationLockDefaults) {
+        new RotationLockControllerImpl(
+                mRotationPolicyWrapper,
+                mDeviceStateRotationLockSettingController,
+                deviceStateRotationLockDefaults
+        );
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
index ace2c71..3431a9d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.policy
 
 import android.app.IActivityTaskManager
+import android.app.admin.DevicePolicyManager
 import android.content.Context
 import android.content.DialogInterface
 import android.content.Intent
@@ -63,6 +64,8 @@
 @SmallTest
 class UserSwitcherControllerTest : SysuiTestCase() {
     @Mock private lateinit var keyguardStateController: KeyguardStateController
+    @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
+    @Mock private lateinit var devicePolicyManager: DevicePolicyManager
     @Mock private lateinit var handler: Handler
     @Mock private lateinit var userTracker: UserTracker
     @Mock private lateinit var userManager: UserManager
@@ -107,6 +110,8 @@
                 userManager,
                 userTracker,
                 keyguardStateController,
+                deviceProvisionedController,
+                devicePolicyManager,
                 handler,
                 activityStarter,
                 broadcastDispatcher,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt
new file mode 100644
index 0000000..871a48c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt
@@ -0,0 +1,176 @@
+/*
+ * 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.policy
+
+import android.os.Handler
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.util.Date
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+@SmallTest
+class VariableDateViewControllerTest : SysuiTestCase() {
+
+    companion object {
+        private const val TIME_STAMP = 1_500_000_000_000
+        private const val LONG_PATTERN = "EEEMMMd"
+        private const val SHORT_PATTERN = "MMMd"
+        private const val CHAR_WIDTH = 10f
+    }
+
+    @Mock
+    private lateinit var broadcastDispatcher: BroadcastDispatcher
+    @Mock
+    private lateinit var view: VariableDateView
+    @Captor
+    private lateinit var onMeasureListenerCaptor: ArgumentCaptor<VariableDateView.OnMeasureListener>
+
+    private var lastText: String? = null
+
+    private lateinit var systemClock: FakeSystemClock
+    private lateinit var testableLooper: TestableLooper
+    private lateinit var testableHandler: Handler
+    private lateinit var controller: VariableDateViewController
+
+    private lateinit var longText: String
+    private lateinit var shortText: String
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        testableLooper = TestableLooper.get(this)
+        testableHandler = Handler(testableLooper.looper)
+
+        systemClock = FakeSystemClock()
+        systemClock.setCurrentTimeMillis(TIME_STAMP)
+
+        `when`(view.longerPattern).thenReturn(LONG_PATTERN)
+        `when`(view.shorterPattern).thenReturn(SHORT_PATTERN)
+        `when`(view.handler).thenReturn(testableHandler)
+
+        `when`(view.setText(anyString())).thenAnswer {
+            lastText = it.arguments[0] as? String
+            Unit
+        }
+        `when`(view.isAttachedToWindow).thenReturn(true)
+
+        val date = Date(TIME_STAMP)
+        longText = getTextForFormat(date, getFormatFromPattern(LONG_PATTERN))
+        shortText = getTextForFormat(date, getFormatFromPattern(SHORT_PATTERN))
+
+        // Assume some sizes for the text, the controller doesn't need to know if these sizes are
+        // the true ones
+        `when`(view.getDesiredWidthForText(any())).thenAnswer {
+            getTextLength(it.arguments[0] as CharSequence)
+        }
+
+        controller = VariableDateViewController(
+                systemClock,
+                broadcastDispatcher,
+                testableHandler,
+                view
+        )
+
+        controller.init()
+        testableLooper.processAllMessages()
+
+        verify(view).onAttach(capture(onMeasureListenerCaptor))
+    }
+
+    @Test
+    fun testViewStartsWithLongText() {
+        assertThat(lastText).isEqualTo(longText)
+    }
+
+    @Test
+    fun testListenerNotNull() {
+        assertThat(onMeasureListenerCaptor.value).isNotNull()
+    }
+
+    @Test
+    fun testLotsOfSpaceUseLongText() {
+        onMeasureListenerCaptor.value.onMeasureAction(10000)
+
+        testableLooper.processAllMessages()
+        assertThat(lastText).isEqualTo(longText)
+    }
+
+    @Test
+    fun testSmallSpaceUseEmpty() {
+        onMeasureListenerCaptor.value.onMeasureAction(1)
+        testableLooper.processAllMessages()
+
+        assertThat(lastText).isEmpty()
+    }
+
+    @Test
+    fun testSpaceInBetweenUseShortText() {
+        val average = ((getTextLength(longText) + getTextLength(shortText)) / 2).toInt()
+
+        onMeasureListenerCaptor.value.onMeasureAction(average)
+        testableLooper.processAllMessages()
+
+        assertThat(lastText).isEqualTo(shortText)
+    }
+
+    @Test
+    fun testSwitchBackToLonger() {
+        onMeasureListenerCaptor.value.onMeasureAction(1)
+        testableLooper.processAllMessages()
+
+        onMeasureListenerCaptor.value.onMeasureAction(10000)
+        testableLooper.processAllMessages()
+
+        assertThat(lastText).isEqualTo(longText)
+    }
+
+    @Test
+    fun testNoSwitchingWhenFrozen() {
+        `when`(view.freezeSwitching).thenReturn(true)
+
+        val average = ((getTextLength(longText) + getTextLength(shortText)) / 2).toInt()
+        onMeasureListenerCaptor.value.onMeasureAction(average)
+        testableLooper.processAllMessages()
+        assertThat(lastText).isEqualTo(longText)
+
+        onMeasureListenerCaptor.value.onMeasureAction(1)
+        testableLooper.processAllMessages()
+        assertThat(lastText).isEqualTo(longText)
+    }
+
+    private fun getTextLength(text: CharSequence): Float {
+        return text.length * CHAR_WIDTH
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 07d3fc2..f6a54936 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -52,9 +52,9 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
 import com.android.systemui.util.settings.SecureSettings;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
index 5efe05f..84e6df2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
@@ -60,9 +60,9 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FeatureFlags;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt
new file mode 100644
index 0000000..eebcbe6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt
@@ -0,0 +1,89 @@
+/*
+ * 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.usb
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.hardware.usb.IUsbSerialReader
+import android.hardware.usb.UsbAccessory
+import android.hardware.usb.UsbManager
+import android.testing.AndroidTestingRunner
+import android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS
+import androidx.test.filters.SmallTest
+import androidx.test.rule.ActivityTestRule
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.lang.Exception
+
+/**
+ * UsbPermissionActivityTest
+ */
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class UsbPermissionActivityTest : SysuiTestCase() {
+
+    class UsbPermissionActivityTestable : UsbPermissionActivity()
+
+    @Rule
+    @JvmField
+    var activityRule = ActivityTestRule<UsbPermissionActivityTestable>(
+            UsbPermissionActivityTestable::class.java, false, false)
+
+    private val activityIntent = Intent(mContext, UsbPermissionActivityTestable::class.java)
+            .apply {
+                flags = Intent.FLAG_ACTIVITY_NEW_TASK
+                putExtra(UsbManager.EXTRA_PACKAGE, "com.android.systemui")
+                putExtra(Intent.EXTRA_INTENT, PendingIntent.getBroadcast(
+                        mContext,
+                        334,
+                        Intent("NO_ACTION"),
+                        PendingIntent.FLAG_MUTABLE))
+                putExtra(UsbManager.EXTRA_ACCESSORY, UsbAccessory(
+                        "manufacturer",
+                        "model",
+                        "description",
+                        "version",
+                        "uri",
+                        object : IUsbSerialReader.Stub() {
+                            override fun getSerial(packageName: String): String {
+                                return "serial"
+                            }
+                        }))
+            }
+
+    @Before
+    fun setUp() {
+        activityRule.launchActivity(activityIntent)
+    }
+
+    @After
+    fun tearDown() {
+        activityRule.finishActivity()
+    }
+
+    @Test
+    @Throws(Exception::class)
+    fun testHideNonSystemOverlay() {
+        assertThat(activityRule.activity.window.attributes.privateFlags and
+                SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS)
+                .isEqualTo(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java
index 8f07545..0e9d96c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorDualTest.java
@@ -16,8 +16,11 @@
 
 package com.android.systemui.util.sensors;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
@@ -60,6 +63,85 @@
     }
 
     @Test
+    public void testInitiallyAbovePrimary() {
+
+        TestableListener listener = new TestableListener();
+
+        mProximitySensor.register(listener);
+        assertTrue(mProximitySensor.isRegistered());
+        assertFalse(mThresholdSensorPrimary.isPaused());
+        assertTrue(mThresholdSensorSecondary.isPaused());
+        assertNull(listener.mLastEvent);
+        assertEquals(0, listener.mCallCount);
+
+        mThresholdSensorPrimary.triggerEvent(false, 0);
+        assertNotNull(listener.mLastEvent);
+        assertFalse(listener.mLastEvent.getBelow());
+        assertEquals(1, listener.mCallCount);
+    }
+
+    @Test
+    public void testInitiallyBelowPrimaryAboveSecondary() {
+
+        TestableListener listener = new TestableListener();
+
+        mProximitySensor.register(listener);
+        assertTrue(mProximitySensor.isRegistered());
+        assertFalse(mThresholdSensorPrimary.isPaused());
+        assertTrue(mThresholdSensorSecondary.isPaused());
+        assertNull(listener.mLastEvent);
+        assertEquals(0, listener.mCallCount);
+
+        mThresholdSensorPrimary.triggerEvent(true, 0);
+        assertNull(listener.mLastEvent);
+        assertEquals(0, listener.mCallCount);
+
+        mThresholdSensorSecondary.triggerEvent(false, 1);
+        assertNotNull(listener.mLastEvent);
+        assertFalse(listener.mLastEvent.getBelow());
+        assertEquals(1, listener.mCallCount);
+    }
+
+    @Test
+    public void testInitiallyBelowPrimaryAndSecondary() {
+
+        TestableListener listener = new TestableListener();
+
+        mProximitySensor.register(listener);
+        assertTrue(mProximitySensor.isRegistered());
+        assertFalse(mThresholdSensorPrimary.isPaused());
+        assertTrue(mThresholdSensorSecondary.isPaused());
+        assertNull(listener.mLastEvent);
+        assertEquals(0, listener.mCallCount);
+
+        mThresholdSensorPrimary.triggerEvent(true, 0);
+        assertNull(listener.mLastEvent);
+        assertEquals(0, listener.mCallCount);
+
+        mThresholdSensorSecondary.triggerEvent(true, 1);
+        assertNotNull(listener.mLastEvent);
+        assertTrue(listener.mLastEvent.getBelow());
+        assertEquals(1, listener.mCallCount);
+    }
+
+    @Test
+    public void testPrimaryBelowDoesNotInvokeSecondary() {
+        TestableListener listener = new TestableListener();
+
+        mProximitySensor.register(listener);
+        assertTrue(mProximitySensor.isRegistered());
+        assertFalse(mThresholdSensorPrimary.isPaused());
+        assertTrue(mThresholdSensorSecondary.isPaused());
+        assertNull(listener.mLastEvent);
+        assertEquals(0, listener.mCallCount);
+
+        // Trigger primary sensor. Our secondary sensor is not registered.
+        mThresholdSensorPrimary.triggerEvent(false, 0);
+        assertFalse(mThresholdSensorPrimary.isPaused());
+        assertTrue(mThresholdSensorSecondary.isPaused());
+    }
+
+    @Test
     public void testSingleListener() {
         TestableListener listener = new TestableListener();
 
@@ -256,66 +338,27 @@
     }
 
     @Test
-    public void testPrimaryCancelsSecondary() {
-        TestableListener listener = new TestableListener();
-
-        mProximitySensor.register(listener);
-        assertFalse(mThresholdSensorPrimary.isPaused());
-        assertTrue(mThresholdSensorSecondary.isPaused());
-        assertNull(listener.mLastEvent);
-        assertEquals(0, listener.mCallCount);
-
-        mThresholdSensorPrimary.triggerEvent(true, 0);
-        assertNull(listener.mLastEvent);
-        assertEquals(0, listener.mCallCount);
-        mThresholdSensorSecondary.triggerEvent(true, 0);
-        assertTrue(listener.mLastEvent.getBelow());
-        assertEquals(1, listener.mCallCount);
-
-        // When the primary reports false, the secondary is no longer needed. We get an immediate
-        // report.
-        mThresholdSensorPrimary.triggerEvent(false, 1);
-        assertFalse(listener.mLastEvent.getBelow());
-        assertEquals(2, listener.mCallCount);
-
-        // The secondary is now ignored. No more work is scheduled.
-        mFakeExecutor.advanceClockToNext();
-        mFakeExecutor.runNextReady();
-        mThresholdSensorSecondary.triggerEvent(true, 0);
-        assertFalse(listener.mLastEvent.getBelow());
-        assertEquals(2, listener.mCallCount);
-        assertEquals(0, mFakeExecutor.numPending());
-
-        mProximitySensor.unregister(listener);
-    }
-
-    @Test
     public void testSecondaryCancelsSecondary() {
         TestableListener listener = new TestableListener();
-        ThresholdSensor.Listener cancelingListener = new ThresholdSensor.Listener() {
-            @Override
-            public void onThresholdCrossed(ThresholdSensor.ThresholdSensorEvent event) {
-                mProximitySensor.pause();
-            }
-        };
+        ThresholdSensor.Listener cancelingListener = event -> mProximitySensor.pause();
 
         mProximitySensor.register(listener);
         mProximitySensor.register(cancelingListener);
-        assertNull(listener.mLastEvent);
-        assertEquals(0, listener.mCallCount);
+        assertThat(listener.mLastEvent).isNull();
+        assertThat(listener.mCallCount).isEqualTo(0);
 
         mThresholdSensorPrimary.triggerEvent(true, 0);
-        assertNull(listener.mLastEvent);
-        assertEquals(0, listener.mCallCount);
+        assertThat(listener.mLastEvent).isNull();
+        assertThat(listener.mCallCount).isEqualTo(0);
         mThresholdSensorSecondary.triggerEvent(true, 0);
-        assertTrue(listener.mLastEvent.getBelow());
-        assertEquals(1, listener.mCallCount);
+        assertThat(listener.mLastEvent.getBelow()).isTrue();
+        assertThat(listener.mCallCount).isEqualTo(1);
 
         // The proximity sensor should now be canceled. Advancing the clock should do nothing.
-        assertEquals(0, mFakeExecutor.numPending());
+        assertThat(mFakeExecutor.numPending()).isEqualTo(0);
         mThresholdSensorSecondary.triggerEvent(false, 1);
-        assertTrue(listener.mLastEvent.getBelow());
-        assertEquals(1, listener.mCallCount);
+        assertThat(listener.mLastEvent.getBelow()).isTrue();
+        assertThat(listener.mCallCount).isEqualTo(1);
 
         mProximitySensor.unregister(listener);
     }
@@ -326,33 +369,66 @@
 
         TestableListener listener = new TestableListener();
 
-        // WE immediately register the secondary sensor.
+        // We immediately register the secondary sensor.
         mProximitySensor.register(listener);
-        assertFalse(mThresholdSensorPrimary.isPaused());
-        assertFalse(mThresholdSensorSecondary.isPaused());
-        assertNull(listener.mLastEvent);
-        assertEquals(0, listener.mCallCount);
+        assertThat(mThresholdSensorPrimary.isPaused()).isTrue();
+        assertThat(mThresholdSensorSecondary.isPaused()).isFalse();
+        assertThat(listener.mLastEvent).isNull();
+        assertThat(listener.mCallCount).isEqualTo(0);
 
         mThresholdSensorPrimary.triggerEvent(true, 0);
-        assertNull(listener.mLastEvent);
-        assertEquals(0, listener.mCallCount);
+        assertThat(listener.mLastEvent).isNull();
+        assertThat(listener.mCallCount).isEqualTo(0);
         mThresholdSensorSecondary.triggerEvent(true, 0);
-        assertTrue(listener.mLastEvent.getBelow());
-        assertEquals(1, listener.mCallCount);
+        assertThat(listener.mLastEvent.getBelow()).isTrue();
+        assertThat(listener.mCallCount).isEqualTo(1);
 
         // The secondary sensor should now remain resumed indefinitely.
-        assertFalse(mThresholdSensorSecondary.isPaused());
-        mThresholdSensorPrimary.triggerEvent(false, 1);
-        assertFalse(listener.mLastEvent.getBelow());
-        assertEquals(2, listener.mCallCount);
+        assertThat(mThresholdSensorSecondary.isPaused()).isFalse();
+        mThresholdSensorSecondary.triggerEvent(false, 1);
+        assertThat(listener.mLastEvent.getBelow()).isFalse();
+        assertThat(listener.mCallCount).isEqualTo(2);
 
         // The secondary is still running, and not polling with the executor.
-        assertFalse(mThresholdSensorSecondary.isPaused());
-        assertEquals(0, mFakeExecutor.numPending());
+        assertThat(mThresholdSensorSecondary.isPaused()).isFalse();
+        assertThat(mFakeExecutor.numPending()).isEqualTo(0);
 
         mProximitySensor.unregister(listener);
     }
 
+    @Test
+    public void testSecondaryPausesPrimary() {
+        TestableListener listener = new TestableListener();
+
+        mProximitySensor.register(listener);
+
+        assertThat(mThresholdSensorPrimary.isPaused()).isFalse();
+        assertThat(mThresholdSensorSecondary.isPaused()).isTrue();
+
+        mProximitySensor.setSecondarySafe(true);
+
+        assertThat(mThresholdSensorPrimary.isPaused()).isTrue();
+        assertThat(mThresholdSensorSecondary.isPaused()).isFalse();
+    }
+
+    @Test
+    public void testSecondaryResumesPrimary() {
+        mProximitySensor.setSecondarySafe(true);
+
+        TestableListener listener = new TestableListener();
+        mProximitySensor.register(listener);
+
+        assertThat(mThresholdSensorPrimary.isPaused()).isTrue();
+        assertThat(mThresholdSensorSecondary.isPaused()).isFalse();
+
+        mProximitySensor.setSecondarySafe(false);
+
+        assertThat(mThresholdSensorPrimary.isPaused()).isFalse();
+        assertThat(mThresholdSensorSecondary.isPaused()).isTrue();
+
+
+    }
+
     private static class TestableListener implements ThresholdSensor.Listener {
         ThresholdSensor.ThresholdSensorEvent mLastEvent;
         int mCallCount = 0;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java
index 6976422..7bb2674 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettings.java
@@ -31,6 +31,7 @@
     private final Map<SettingsKey, String> mValues = new HashMap<>();
     private final Map<SettingsKey, List<ContentObserver>> mContentObservers =
             new HashMap<>();
+    private final Map<String, List<ContentObserver>> mContentObserversAllUsers = new HashMap<>();
 
     public static final Uri CONTENT_URI = Uri.parse("content://settings/fake");
 
@@ -55,9 +56,15 @@
     @Override
     public void registerContentObserverForUser(Uri uri, boolean notifyDescendents,
             ContentObserver settingsObserver, int userHandle) {
-        SettingsKey key = new SettingsKey(userHandle, uri.toString());
-        mContentObservers.putIfAbsent(key, new ArrayList<>());
-        List<ContentObserver> observers = mContentObservers.get(key);
+        List<ContentObserver> observers;
+        if (userHandle == UserHandle.USER_ALL) {
+            mContentObserversAllUsers.putIfAbsent(uri.toString(), new ArrayList<>());
+            observers = mContentObserversAllUsers.get(uri.toString());
+        } else {
+            SettingsKey key = new SettingsKey(userHandle, uri.toString());
+            mContentObservers.putIfAbsent(key, new ArrayList<>());
+            observers = mContentObservers.get(key);
+        }
         observers.add(settingsObserver);
     }
 
@@ -67,6 +74,10 @@
             List<ContentObserver> observers = mContentObservers.get(key);
             observers.remove(settingsObserver);
         }
+        for (String key : mContentObserversAllUsers.keySet()) {
+            List<ContentObserver> observers = mContentObserversAllUsers.get(key);
+            observers.remove(settingsObserver);
+        }
     }
 
     @Override
@@ -114,6 +125,10 @@
         for (ContentObserver observer : mContentObservers.getOrDefault(key, new ArrayList<>())) {
             observer.dispatchChange(false, List.of(uri), userHandle);
         }
+        for (ContentObserver observer :
+                mContentObserversAllUsers.getOrDefault(uri.toString(), new ArrayList<>())) {
+            observer.dispatchChange(false, List.of(uri), userHandle);
+        }
         return true;
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
index 0d560f2..34cae58 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.Mockito.verify;
 
 import android.database.ContentObserver;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
 
@@ -89,6 +90,16 @@
     }
 
     @Test
+    public void testRegisterContentObserverAllUsers() {
+        mFakeSettings.registerContentObserverForUser(
+                mFakeSettings.getUriFor("cat"), false, mContentObserver, UserHandle.USER_ALL);
+
+        mFakeSettings.putString("cat", "hat");
+
+        verify(mContentObserver).dispatchChange(anyBoolean(), any(Collection.class), anyInt());
+    }
+
+    @Test
     public void testUnregisterContentObserver() {
         mFakeSettings.registerContentObserver("cat", mContentObserver);
         mFakeSettings.unregisterContentObserver(mContentObserver);
@@ -98,4 +109,16 @@
         verify(mContentObserver, never()).dispatchChange(
                 anyBoolean(), any(Collection.class), anyInt());
     }
+
+    @Test
+    public void testUnregisterContentObserverAllUsers() {
+        mFakeSettings.registerContentObserverForUser(
+                mFakeSettings.getUriFor("cat"), false, mContentObserver, UserHandle.USER_ALL);
+        mFakeSettings.unregisterContentObserver(mContentObserver);
+
+        mFakeSettings.putString("cat", "hat");
+
+        verify(mContentObserver, never()).dispatchChange(
+                anyBoolean(), any(Collection.class), anyInt());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
index 6d1e6ce..8e1c0f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
@@ -22,7 +22,7 @@
 
 public class FakePluginManager implements PluginManager {
 
-    private final BaseLeakChecker<PluginListener> mLeakChecker;
+    private final BaseLeakChecker<PluginListener<?>> mLeakChecker;
 
     public FakePluginManager(LeakCheck test) {
         mLeakChecker = new BaseLeakChecker<>(test, "Plugin");
@@ -30,7 +30,7 @@
 
     @Override
     public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
-            Class cls, boolean allowMultiple) {
+            Class<?> cls, boolean allowMultiple) {
         mLeakChecker.addCallback(listener);
     }
 
@@ -62,17 +62,7 @@
     }
 
     @Override
-    public String[] getWhitelistedPlugins() {
+    public String[] getPrivilegedPlugins() {
         return new String[0];
     }
-
-    @Override
-    public <T extends Plugin> T getOneShotPlugin(Class<T> cls) {
-        return null;
-    }
-
-    @Override
-    public <T extends Plugin> T getOneShotPlugin(String action, Class<?> cls) {
-        return null;
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index dd4830e..9493456 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -41,9 +41,13 @@
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.media.dialog.MediaOutputDialogFactory;
+import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.VolumeDialogController;
 import com.android.systemui.plugins.VolumeDialogController.State;
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -68,23 +72,34 @@
     View mDrawerNormal;
 
     @Mock
-    VolumeDialogController mController;
-
+    VolumeDialogController mVolumeDialogController;
     @Mock
     KeyguardManager mKeyguard;
-
     @Mock
     AccessibilityManagerWrapper mAccessibilityMgr;
+    @Mock
+    DeviceProvisionedController mDeviceProvisionedController;
+    @Mock
+    ConfigurationController mConfigurationController;
+    @Mock
+    MediaOutputDialogFactory mMediaOutputDialogFactory;
+    @Mock
+    ActivityStarter mActivityStarter;
 
     @Before
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
 
-        mController = mDependency.injectMockDependency(VolumeDialogController.class);
-        mAccessibilityMgr = mDependency.injectMockDependency(AccessibilityManagerWrapper.class);
         getContext().addMockSystemService(KeyguardManager.class, mKeyguard);
 
-        mDialog = new VolumeDialogImpl(getContext());
+        mDialog = new VolumeDialogImpl(
+                getContext(),
+                mVolumeDialogController,
+                mAccessibilityMgr,
+                mDeviceProvisionedController,
+                mConfigurationController,
+                mMediaOutputDialogFactory,
+                mActivityStarter);
         mDialog.init(0, null);
         State state = createShellState();
         mDialog.onStateChangedH(state);
@@ -170,7 +185,7 @@
         Mockito.reset(mAccessibilityMgr);
         ArgumentCaptor<VolumeDialogController.Callbacks> controllerCallbackCapture =
                 ArgumentCaptor.forClass(VolumeDialogController.Callbacks.class);
-        verify(mController).addCallback(controllerCallbackCapture.capture(), any());
+        verify(mVolumeDialogController).addCallback(controllerCallbackCapture.capture(), any());
         VolumeDialogController.Callbacks callbacks = controllerCallbackCapture.getValue();
         callbacks.onShowSafetyWarning(AudioManager.FLAG_SHOW_UI);
         verify(mAccessibilityMgr).getRecommendedTimeoutMillis(
@@ -201,13 +216,13 @@
         mDialog.onStateChangedH(initialSilentState);
 
         // expected: shouldn't call vibrate yet
-        verify(mController, never()).vibrate(any());
+        verify(mVolumeDialogController, never()).vibrate(any());
 
         // changed ringer to vibrate
         mDialog.onStateChangedH(vibrateState);
 
         // expected: vibrate device
-        verify(mController).vibrate(any());
+        verify(mVolumeDialogController).vibrate(any());
     }
 
     @Test
@@ -225,7 +240,7 @@
         mDialog.onStateChangedH(vibrateState);
 
         // shouldn't call vibrate
-        verify(mController, never()).vibrate(any());
+        verify(mVolumeDialogController, never()).vibrate(any());
     }
 
     @Test
@@ -238,7 +253,7 @@
         mDrawerVibrate.performClick();
 
         // Make sure we've actually changed the ringer mode.
-        verify(mController, times(1)).setRingerMode(
+        verify(mVolumeDialogController, times(1)).setRingerMode(
                 AudioManager.RINGER_MODE_VIBRATE, false);
     }
 
@@ -252,7 +267,7 @@
         mDrawerMute.performClick();
 
         // Make sure we've actually changed the ringer mode.
-        verify(mController, times(1)).setRingerMode(
+        verify(mVolumeDialogController, times(1)).setRingerMode(
                 AudioManager.RINGER_MODE_SILENT, false);
     }
 
@@ -266,7 +281,7 @@
         mDrawerNormal.performClick();
 
         // Make sure we've actually changed the ringer mode.
-        verify(mController, times(1)).setRingerMode(
+        verify(mVolumeDialogController, times(1)).setRingerMode(
                 AudioManager.RINGER_MODE_NORMAL, false);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index a0b93ad..9f755f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -76,11 +76,11 @@
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationRemoveInterceptor;
 import com.android.systemui.statusbar.RankingBuilder;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
index b0dd73a..a3bbb26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
@@ -64,10 +64,10 @@
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.RankingBuilder;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index 561d079..b0893cc 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -970,14 +970,14 @@
         }
 
         @Override
-        public boolean submit(Request request, Callback callback) {
+        public int submit(Request request, Callback callback) {
             ArrayList<Request> requests = new ArrayList<>();
             requests.add(request);
             return submit(requests, callback);
         }
 
         @Override
-        public boolean submit(List<Request> requests, Callback callback) {
+        public int submit(List<Request> requests, Callback callback) {
             ArrayList<android.hardware.camera2.extension.Request> captureRequests =
                     new ArrayList<>();
             int requestId = 0;
@@ -992,11 +992,11 @@
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed to submit request due to remote exception!");
             }
-            return false;
+            return -1;
         }
 
         @Override
-        public boolean setRepeating(Request request, Callback callback) {
+        public int setRepeating(Request request, Callback callback) {
             try {
                 ArrayList<Request> requests = new ArrayList<>();
                 requests.add(request);
@@ -1007,7 +1007,7 @@
                 Log.e(TAG, "Failed to submit repeating request due to remote exception!");
             }
 
-            return false;
+            return -1;
         }
 
         @Override
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index b05dffe..244f357 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -499,6 +499,9 @@
             if (oldWindow.displayId != newWindow.displayId) {
                 return true;
             }
+            if (oldWindow.taskId != newWindow.taskId) {
+                return true;
+            }
             return false;
         }
 
@@ -699,6 +702,7 @@
             reportedWindow.setAnchorId(window.accessibilityIdOfAnchor);
             reportedWindow.setPictureInPicture(window.inPictureInPicture);
             reportedWindow.setDisplayId(window.displayId);
+            reportedWindow.setTaskId(window.taskId);
 
             final int parentId = findWindowIdLocked(userId, window.parentToken);
             if (parentId >= 0) {
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
index cd332a6..4946ad4 100644
--- a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
@@ -125,7 +125,7 @@
             // connect with remote AppPredictionService instead for dark launch
             usesPeopleService = false;
         }
-        final boolean serviceExists = resolveService(sessionId, false,
+        final boolean serviceExists = resolveService(sessionId, true,
                 usesPeopleService, s -> s.onCreatePredictionSession(context, sessionId));
         if (serviceExists && !mSessionInfos.containsKey(sessionId)) {
             final AppPredictionSessionInfo sessionInfo = new AppPredictionSessionInfo(
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 5aec6aa..c32543a 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -3285,6 +3285,58 @@
         }
     }
 
+    private void applyResourceOverlaysToWidgetsLocked(Set<String> packageNames, int userId,
+            boolean updateFrameworkRes) {
+        for (int i = 0, N = mProviders.size(); i < N; i++) {
+            Provider provider = mProviders.get(i);
+            if (provider.getUserId() != userId) {
+                continue;
+            }
+
+            final String packageName = provider.id.componentName.getPackageName();
+            if (!updateFrameworkRes && !packageNames.contains(packageName)) {
+                continue;
+            }
+
+            ApplicationInfo newAppInfo = null;
+            try {
+                newAppInfo = mPackageManager.getApplicationInfo(packageName,
+                        PackageManager.GET_SHARED_LIBRARY_FILES, userId);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to retrieve app info for " + packageName
+                        + " userId=" + userId, e);
+            }
+            if (newAppInfo == null || provider.info == null
+                    || provider.info.providerInfo == null) {
+                continue;
+            }
+            ApplicationInfo oldAppInfo = provider.info.providerInfo.applicationInfo;
+            if (oldAppInfo == null || !newAppInfo.sourceDir.equals(oldAppInfo.sourceDir)) {
+                // Overlay paths are generated against a particular version of an application.
+                // The overlays paths of a newly upgraded application are incompatible with the
+                // old version of the application.
+                continue;
+            }
+
+            // Isolate the changes relating to RROs. The app info must be copied to prevent
+            // affecting other parts of system server that may have cached this app info.
+            oldAppInfo = new ApplicationInfo(oldAppInfo);
+            oldAppInfo.overlayPaths = newAppInfo.overlayPaths.clone();
+            oldAppInfo.resourceDirs = newAppInfo.resourceDirs.clone();
+            provider.info.providerInfo.applicationInfo = oldAppInfo;
+
+            for (int j = 0, M = provider.widgets.size(); j < M; j++) {
+                Widget widget = provider.widgets.get(j);
+                if (widget.views != null) {
+                    widget.views.updateAppInfo(oldAppInfo);
+                }
+                if (widget.maskedViews != null) {
+                    widget.maskedViews.updateAppInfo(oldAppInfo);
+                }
+            }
+        }
+    }
+
     /**
      * Updates all providers with the specified package names, and records any providers that were
      * pruned.
@@ -4875,5 +4927,14 @@
         public void unlockUser(int userId) {
             handleUserUnlocked(userId);
         }
+
+        @Override
+        public void applyResourceOverlaysToWidgets(Set<String> packageNames, int userId,
+                boolean updateFrameworkRes) {
+            synchronized (mLock) {
+                applyResourceOverlaysToWidgetsLocked(new HashSet<>(packageNames), userId,
+                        updateFrameworkRes);
+            }
+        }
     }
 }
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 078d908..df269d7 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -96,6 +96,7 @@
 import android.util.SparseArray;
 import android.util.TimeUtils;
 import android.view.KeyEvent;
+import android.view.accessibility.AccessibilityManager;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillManager.SmartSuggestionMode;
@@ -346,6 +347,8 @@
      */
     private final AssistDataReceiverImpl mAssistReceiver = new AssistDataReceiverImpl();
 
+    private final AccessibilityManager mAccessibilityManager;
+
     void onSwitchInputMethodLocked() {
         // One caveat is that for the case where the focus is on a field for which regular autofill
         // returns null, and augmented autofill is triggered,  and then the user switches the input
@@ -440,6 +443,7 @@
                     if (!mWaitForInlineRequest || mPendingInlineSuggestionsRequest != null) {
                         return;
                     }
+                    mWaitForInlineRequest = inlineSuggestionsRequest != null;
                     mPendingInlineSuggestionsRequest = inlineSuggestionsRequest;
                     maybeRequestFillLocked();
                     viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
@@ -457,9 +461,14 @@
                     return;
                 }
 
-                mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
-                        mPendingFillRequest.getFillContexts(), mPendingFillRequest.getClientState(),
-                        mPendingFillRequest.getFlags(), mPendingInlineSuggestionsRequest);
+                // If a11y touch exploration is enabled, then we do not send an inline fill request
+                // to the regular af service, because dropdown UI is easier to use.
+                if (!mAccessibilityManager.isTouchExplorationEnabled()) {
+                    mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
+                            mPendingFillRequest.getFillContexts(),
+                            mPendingFillRequest.getClientState(),
+                            mPendingFillRequest.getFlags(), mPendingInlineSuggestionsRequest);
+                }
             }
 
             mRemoteFillService.onFillRequest(mPendingFillRequest);
@@ -888,6 +897,7 @@
         mRemoteFillService = serviceComponentName == null ? null
                 : new RemoteFillService(context, serviceComponentName, userId, this,
                         bindInstantServiceAllowed);
+        mAccessibilityManager = AccessibilityManager.getInstance(context);
         mActivityToken = activityToken;
         mHasCallback = hasCallback;
         mUiLatencyHistory = uiLatencyHistory;
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index b7feb63..30de4b4 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -17,6 +17,7 @@
 
 package com.android.server.companion;
 
+import static android.Manifest.permission.BIND_COMPANION_DEVICE_SERVICE;
 import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES;
 import static android.bluetooth.le.ScanSettings.SCAN_MODE_BALANCED;
 import static android.content.Context.BIND_IMPORTANT;
@@ -1197,6 +1198,12 @@
                     + " has " + packageResolveInfos.size());
             return new ServiceConnector.NoOp<>();
         }
+        String servicePermission = packageResolveInfos.get(0).serviceInfo.permission;
+        if (!BIND_COMPANION_DEVICE_SERVICE.equals(servicePermission)) {
+            Slog.w(LOG_TAG, "Binding CompanionDeviceService must have "
+                    + BIND_COMPANION_DEVICE_SERVICE + " permission.");
+            return new ServiceConnector.NoOp<>();
+        }
         ComponentName componentName = packageResolveInfos.get(0).serviceInfo.getComponentName();
         Slog.i(LOG_TAG, "Initializing CompanionDeviceService binding for " + componentName);
         return new ServiceConnector.Impl<ICompanionDeviceService>(getContext(),
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 25ea12b..8a42ddf 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -70,6 +70,7 @@
 import android.service.contentcapture.ActivityEvent.ActivityEventType;
 import android.service.contentcapture.IDataShareCallback;
 import android.service.contentcapture.IDataShareReadAdapter;
+import android.service.voice.VoiceInteractionManagerInternal;
 import android.util.ArraySet;
 import android.util.LocalLog;
 import android.util.Pair;
@@ -302,6 +303,37 @@
                 || super.isDisabledLocked(userId);
     }
 
+    @Override
+    protected void assertCalledByPackageOwner(@NonNull String packageName) {
+        try {
+            super.assertCalledByPackageOwner(packageName);
+        } catch (SecurityException e) {
+            final int callingUid = Binder.getCallingUid();
+
+            VoiceInteractionManagerInternal.HotwordDetectionServiceIdentity
+                    hotwordDetectionServiceIdentity =
+                    LocalServices.getService(VoiceInteractionManagerInternal.class)
+                            .getHotwordDetectionServiceIdentity();
+
+            if (callingUid != hotwordDetectionServiceIdentity.getIsolatedUid()) {
+                super.assertCalledByPackageOwner(packageName);
+                return;
+            }
+
+            final String[] packages =
+                    getContext()
+                            .getPackageManager()
+                            .getPackagesForUid(hotwordDetectionServiceIdentity.getOwnerUid());
+            if (packages != null) {
+                for (String candidate : packages) {
+                    if (packageName.equals(candidate)) return; // Found it
+                }
+            }
+
+            throw e;
+        }
+    }
+
     private boolean isDisabledBySettingsLocked(@UserIdInt int userId) {
         return mDisabledBySettings != null && mDisabledBySettings.get(userId);
     }
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 225a8d4..904def0 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -57,6 +57,7 @@
 import android.service.contentcapture.IContentCaptureServiceCallback;
 import android.service.contentcapture.IDataShareCallback;
 import android.service.contentcapture.SnapshotData;
+import android.service.voice.VoiceInteractionManagerInternal;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
@@ -415,12 +416,25 @@
         }
         if (callingUid != packageUid && !LocalServices.getService(ActivityManagerInternal.class)
                 .hasRunningActivity(callingUid, packageName)) {
-            final String[] packages = pm.getPackagesForUid(callingUid);
-            final String callingPackage = packages != null ? packages[0] : "uid-" + callingUid;
-            Slog.w(TAG, "App (package=" + callingPackage + ", UID=" + callingUid
-                    + ") passed package (" + packageName + ") owned by UID " + packageUid);
 
-            throw new SecurityException("Invalid package: " + packageName);
+            VoiceInteractionManagerInternal.HotwordDetectionServiceIdentity
+                    hotwordDetectionServiceIdentity =
+                    LocalServices.getService(VoiceInteractionManagerInternal.class)
+                            .getHotwordDetectionServiceIdentity();
+
+            boolean isHotwordDetectionServiceCall =
+                    hotwordDetectionServiceIdentity != null
+                            && callingUid == hotwordDetectionServiceIdentity.getIsolatedUid()
+                            && packageUid == hotwordDetectionServiceIdentity.getOwnerUid();
+
+            if (!isHotwordDetectionServiceCall) {
+                final String[] packages = pm.getPackagesForUid(callingUid);
+                final String callingPackage = packages != null ? packages[0] : "uid-" + callingUid;
+                Slog.w(TAG, "App (package=" + callingPackage + ", UID=" + callingUid
+                        + ") passed package (" + packageName + ") owned by UID " + packageUid);
+
+                throw new SecurityException("Invalid package: " + packageName);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 85ff2be..b7c61a0d 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -2505,6 +2505,16 @@
         mContext.sendBroadcastAsUser(intent, UserHandle.ALL, null, getTempAllowlistBroadcastOptions());
     }
 
+    private boolean isBleState(int state) {
+        switch (state) {
+            case BluetoothAdapter.STATE_BLE_ON:
+            case BluetoothAdapter.STATE_BLE_TURNING_ON:
+            case BluetoothAdapter.STATE_BLE_TURNING_OFF:
+                return true;
+        }
+        return false;
+    }
+
     @RequiresPermission(allOf = {
             android.Manifest.permission.BLUETOOTH_CONNECT,
             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
@@ -2527,8 +2537,15 @@
                 sendBluetoothServiceDownCallback();
                 unbindAndFinish();
                 sendBleStateChanged(prevState, newState);
-                // Don't broadcast as it has already been broadcast before
-                isStandardBroadcast = false;
+
+                /* Currently, the OFF intent is broadcasted externally only when we transition
+                 * from TURNING_OFF to BLE_ON state. So if the previous state is a BLE state,
+                 * we are guaranteed that the OFF intent has been broadcasted earlier and we
+                 * can safely skip it.
+                 * Conversely, if the previous state is not a BLE state, it indicates that some
+                 * sort of crash has occurred, moving us directly to STATE_OFF without ever
+                 * passing through BLE_ON. We should broadcast the OFF intent in this case. */
+                isStandardBroadcast = !isBleState(prevState);
 
             } else if (!intermediate_off) {
                 // connect to GattService
@@ -2581,6 +2598,11 @@
                 // Show prevState of BLE_ON as OFF to standard users
                 prevState = BluetoothAdapter.STATE_OFF;
             }
+            if (DBG) {
+                Slog.d(TAG,
+                        "Sending State Change: " + BluetoothAdapter.nameForState(prevState) + " > "
+                                + BluetoothAdapter.nameForState(newState));
+            }
             Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
             intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
             intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index 019e4ea..91b2440 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -21,8 +21,6 @@
 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
 import static android.app.ActivityManager.RunningServiceInfo;
 import static android.app.ActivityManager.RunningTaskInfo;
-import static android.app.ActivityManager.getCurrentUser;
-import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.MODE_IGNORED;
 import static android.app.AppOpsManager.OP_CAMERA;
 import static android.app.AppOpsManager.OP_PHONE_CALL_CAMERA;
@@ -383,17 +381,9 @@
 
             int sensor;
             if (result == MODE_IGNORED) {
-                if (code == OP_RECORD_AUDIO) {
+                if (code == OP_RECORD_AUDIO || code == OP_PHONE_CALL_MICROPHONE) {
                     sensor = MICROPHONE;
-                } else if (code == OP_CAMERA) {
-                    sensor = CAMERA;
-                } else {
-                    return;
-                }
-            } else if (result == MODE_ALLOWED) {
-                if (code == OP_PHONE_CALL_MICROPHONE) {
-                    sensor = MICROPHONE;
-                } else if (code == OP_PHONE_CALL_CAMERA) {
+                } else if (code == OP_CAMERA || code == OP_PHONE_CALL_CAMERA) {
                     sensor = CAMERA;
                 } else {
                     return;
@@ -423,6 +413,12 @@
                 return;
             }
 
+            if (uid == Process.SYSTEM_UID) {
+                // If the system uid is being blamed for sensor access, the ui must be shown
+                // explicitly using SensorPrivacyManager#showSensorUseDialog
+                return;
+            }
+
             synchronized (mLock) {
                 if (mSuppressReminders.containsKey(new Pair<>(sensor, user))) {
                     Log.d(TAG,
@@ -431,11 +427,6 @@
                 }
             }
 
-            if (uid == Process.SYSTEM_UID) {
-                enqueueSensorUseReminderDialogAsync(-1, user, packageName, sensor);
-                return;
-            }
-
             // TODO: Handle reminders with multiple sensors
 
             // - If we have a likely activity that triggered the sensor use overlay a dialog over
@@ -718,6 +709,9 @@
         public void setIndividualSensorPrivacy(@UserIdInt int userId,
                 @SensorPrivacyManager.Sources.Source int source, int sensor, boolean enable) {
             enforceManageSensorPrivacyPermission();
+            if (userId == UserHandle.USER_CURRENT) {
+                userId = mCurrentUser;
+            }
             if (!canChangeIndividualSensorPrivacy(userId, sensor)) {
                 return;
             }
@@ -843,6 +837,9 @@
         public void setIndividualSensorPrivacyForProfileGroup(@UserIdInt int userId,
                 @SensorPrivacyManager.Sources.Source int source, int sensor, boolean enable) {
             enforceManageSensorPrivacyPermission();
+            if (userId == UserHandle.USER_CURRENT) {
+                userId = mCurrentUser;
+            }
             int parentId = mUserManagerInternal.getProfileParentId(userId);
             forAllUsers(userId2 -> {
                 if (parentId == mUserManagerInternal.getProfileParentId(userId2)) {
@@ -896,6 +893,9 @@
         @Override
         public boolean isIndividualSensorPrivacyEnabled(@UserIdInt int userId, int sensor) {
             enforceObserveSensorPrivacyPermission();
+            if (userId == UserHandle.USER_CURRENT) {
+                userId = mCurrentUser;
+            }
             synchronized (mLock) {
                 return isIndividualSensorPrivacyEnabledLocked(userId, sensor);
             }
@@ -1021,7 +1021,15 @@
                 }
             }
 
-            return upgradeAndInit(version, map);
+            try {
+                return upgradeAndInit(version, map);
+            } catch (Exception e) {
+                Log.wtf(TAG, "Failed to upgrade and set sensor privacy state,"
+                        + " resetting to default.", e);
+                mEnabled = new SparseBooleanArray();
+                mIndividualEnabled = new SparseArray<>();
+                return true;
+            }
         }
 
         private boolean upgradeAndInit(int version, SparseArray map) {
@@ -1037,22 +1045,22 @@
             final int[] users = getLocalService(UserManagerInternal.class).getUserIds();
             if (version == 0) {
                 final boolean enabled = (boolean) map.get(VER0_ENABLED);
-                final SparseBooleanArray individualEnabled =
-                        (SparseBooleanArray) map.get(VER0_INDIVIDUAL_ENABLED);
+                final SparseArray<SensorState> individualEnabled =
+                        (SparseArray<SensorState>) map.get(VER0_INDIVIDUAL_ENABLED);
 
                 final SparseBooleanArray perUserEnabled = new SparseBooleanArray();
-                final SparseArray<SparseBooleanArray> perUserIndividualEnabled =
+                final SparseArray<SparseArray<SensorState>> perUserIndividualEnabled =
                         new SparseArray<>();
 
                 // Copy global state to each user
                 for (int i = 0; i < users.length; i++) {
                     int user = users[i];
                     perUserEnabled.put(user, enabled);
-                    SparseBooleanArray userIndividualSensorEnabled = new SparseBooleanArray();
+                    SparseArray<SensorState> userIndividualSensorEnabled = new SparseArray<>();
                     perUserIndividualEnabled.put(user, userIndividualSensorEnabled);
                     for (int j = 0; j < individualEnabled.size(); j++) {
                         final int sensor = individualEnabled.keyAt(j);
-                        final boolean isSensorEnabled = individualEnabled.valueAt(j);
+                        final SensorState isSensorEnabled = individualEnabled.valueAt(j);
                         userIndividualSensorEnabled.put(sensor, isSensorEnabled);
                     }
                 }
@@ -1213,6 +1221,9 @@
         public void suppressIndividualSensorPrivacyReminders(int userId, int sensor,
                 IBinder token, boolean suppress) {
             enforceManageSensorPrivacyPermission();
+            if (userId == UserHandle.USER_CURRENT) {
+                userId = mCurrentUser;
+            }
             Objects.requireNonNull(token);
 
             Pair<Integer, UserHandle> key = new Pair<>(sensor, UserHandle.of(userId));
@@ -1239,6 +1250,18 @@
             }
         }
 
+        @Override
+        public void showSensorUseDialog(int sensor) {
+            if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+                throw new SecurityException("Can only be called by the system uid");
+            }
+            if (!isIndividualSensorPrivacyEnabled(mCurrentUser, sensor)) {
+                return;
+            }
+            enqueueSensorUseReminderDialogAsync(
+                    -1, UserHandle.of(mCurrentUser), "android", sensor);
+        }
+
         private void userSwitching(int from, int to) {
             boolean micState;
             boolean camState;
@@ -1265,10 +1288,14 @@
                 case MICROPHONE:
                     mAppOpsManagerInternal.setGlobalRestriction(OP_RECORD_AUDIO, enabled,
                             mAppOpsRestrictionToken);
+                    mAppOpsManagerInternal.setGlobalRestriction(OP_PHONE_CALL_MICROPHONE, enabled,
+                            mAppOpsRestrictionToken);
                     break;
                 case CAMERA:
                     mAppOpsManagerInternal.setGlobalRestriction(OP_CAMERA, enabled,
                             mAppOpsRestrictionToken);
+                    mAppOpsManagerInternal.setGlobalRestriction(OP_PHONE_CALL_CAMERA, enabled,
+                            mAppOpsRestrictionToken);
                     break;
             }
         }
@@ -1898,9 +1925,9 @@
                 if (!mIsInEmergencyCall) {
                     mIsInEmergencyCall = true;
                     if (mSensorPrivacyServiceImpl
-                            .isIndividualSensorPrivacyEnabled(getCurrentUser(), MICROPHONE)) {
+                            .isIndividualSensorPrivacyEnabled(mCurrentUser, MICROPHONE)) {
                         mSensorPrivacyServiceImpl.setIndividualSensorPrivacyUnchecked(
-                                getCurrentUser(), OTHER, MICROPHONE, false);
+                                mCurrentUser, OTHER, MICROPHONE, false);
                         mMicUnmutedForEmergencyCall = true;
                     } else {
                         mMicUnmutedForEmergencyCall = false;
@@ -1915,7 +1942,7 @@
                     mIsInEmergencyCall = false;
                     if (mMicUnmutedForEmergencyCall) {
                         mSensorPrivacyServiceImpl.setIndividualSensorPrivacyUnchecked(
-                                getCurrentUser(), OTHER, MICROPHONE, true);
+                                mCurrentUser, OTHER, MICROPHONE, true);
                         mMicUnmutedForEmergencyCall = false;
                     }
                 }
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 85eadf5..1007130 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -1168,8 +1168,8 @@
 
     private boolean doesPackageHaveCallingUid(@NonNull String packageName) {
         try {
-            return getContext().getPackageManager().getPackageUid(packageName, 0)
-                    == mInjector.getCallingUid();
+            return getContext().getPackageManager().getPackageUidAsUser(packageName,
+                    UserHandle.getCallingUserId()) == mInjector.getCallingUid();
         } catch (PackageManager.NameNotFoundException e) {
             return false;
         }
diff --git a/services/core/java/com/android/server/VpnManagerService.java b/services/core/java/com/android/server/VpnManagerService.java
index d483f18..a03425c 100644
--- a/services/core/java/com/android/server/VpnManagerService.java
+++ b/services/core/java/com/android/server/VpnManagerService.java
@@ -26,6 +26,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.net.ConnectivityManager;
 import android.net.INetd;
 import android.net.IVpnManager;
@@ -312,6 +314,26 @@
         }
     }
 
+    // TODO : Move to a static lib to factorize with Vpn.java
+    private int getAppUid(final String app, final int userId) {
+        final PackageManager pm = mContext.getPackageManager();
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return pm.getPackageUidAsUser(app, userId);
+        } catch (NameNotFoundException e) {
+            return -1;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    private void verifyCallingUidAndPackage(String packageName, int callingUid) {
+        final int userId = UserHandle.getUserId(callingUid);
+        if (getAppUid(packageName, userId) != callingUid) {
+            throw new SecurityException(packageName + " does not belong to uid " + callingUid);
+        }
+    }
+
     /**
      * Starts the VPN based on the stored profile for the given package
      *
@@ -323,7 +345,9 @@
      */
     @Override
     public void startVpnProfile(@NonNull String packageName) {
-        final int user = UserHandle.getUserId(mDeps.getCallingUid());
+        final int callingUid = Binder.getCallingUid();
+        verifyCallingUidAndPackage(packageName, callingUid);
+        final int user = UserHandle.getUserId(callingUid);
         synchronized (mVpns) {
             throwIfLockdownEnabled();
             mVpns.get(user).startVpnProfile(packageName);
@@ -340,7 +364,9 @@
      */
     @Override
     public void stopVpnProfile(@NonNull String packageName) {
-        final int user = UserHandle.getUserId(mDeps.getCallingUid());
+        final int callingUid = Binder.getCallingUid();
+        verifyCallingUidAndPackage(packageName, callingUid);
+        final int user = UserHandle.getUserId(callingUid);
         synchronized (mVpns) {
             mVpns.get(user).stopVpnProfile(packageName);
         }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 07847f1..a2fec27 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -102,6 +102,7 @@
 import android.appwidget.AppWidgetManagerInternal;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledSince;
+import android.compat.annotation.Overridable;
 import android.content.ComponentName;
 import android.content.ComponentName.WithComponentName;
 import android.content.Context;
@@ -307,6 +308,7 @@
      */
     @ChangeId
     @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.S)
+    @Overridable
     static final long FGS_BG_START_RESTRICTION_CHANGE_ID = 170668199L;
 
     /**
@@ -805,10 +807,12 @@
 
         if (fgRequired) {
             // We are now effectively running a foreground service.
-            ServiceState stracker = r.getTracker();
-            if (stracker != null) {
-                stracker.setForeground(true, mAm.mProcessStats.getMemFactorLocked(),
-                        r.lastActivity);
+            synchronized (mAm.mProcessStats.mLock) {
+                final ServiceState stracker = r.getTracker();
+                if (stracker != null) {
+                    stracker.setForeground(true, mAm.mProcessStats.getMemFactorLocked(),
+                            r.lastActivity);
+                }
             }
             mAm.mAppOpsService.startOperation(AppOpsManager.getToken(mAm.mAppOpsService),
                     AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null,
@@ -1080,9 +1084,11 @@
 
     ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
             boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
-        ServiceState stracker = r.getTracker();
-        if (stracker != null) {
-            stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
+        synchronized (mAm.mProcessStats.mLock) {
+            final ServiceState stracker = r.getTracker();
+            if (stracker != null) {
+                stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
+            }
         }
         r.callStart = false;
 
@@ -1142,8 +1148,10 @@
         mAm.mBatteryStatsService.noteServiceStopRunning(uid, packageName, serviceName);
         service.startRequested = false;
         if (service.tracker != null) {
-            service.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
-                    SystemClock.uptimeMillis());
+            synchronized (mAm.mProcessStats.mLock) {
+                service.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
+                        SystemClock.uptimeMillis());
+            }
         }
         service.callStart = false;
 
@@ -1318,8 +1326,10 @@
             mAm.mBatteryStatsService.noteServiceStopRunning(uid, packageName, serviceName);
             r.startRequested = false;
             if (r.tracker != null) {
-                r.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
-                        SystemClock.uptimeMillis());
+                synchronized (mAm.mProcessStats.mLock) {
+                    r.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
+                            SystemClock.uptimeMillis());
+                }
             }
             r.callStart = false;
             final long origId = Binder.clearCallingIdentity();
@@ -1875,10 +1885,12 @@
                         r.mStartForegroundCount++;
                         r.mFgsEnterTime = SystemClock.uptimeMillis();
                         if (!stopProcStatsOp) {
-                            ServiceState stracker = r.getTracker();
-                            if (stracker != null) {
-                                stracker.setForeground(true,
-                                        mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
+                            synchronized (mAm.mProcessStats.mLock) {
+                                final ServiceState stracker = r.getTracker();
+                                if (stracker != null) {
+                                    stracker.setForeground(true,
+                                            mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
+                                }
                             }
                         } else {
                             stopProcStatsOp = false;
@@ -1913,10 +1925,12 @@
                 if (stopProcStatsOp) {
                     // We got through to this point with it actively being started foreground,
                     // and never decided we wanted to keep it like that, so drop it.
-                    ServiceState stracker = r.getTracker();
-                    if (stracker != null) {
-                        stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
-                                SystemClock.uptimeMillis());
+                    synchronized (mAm.mProcessStats.mLock) {
+                        final ServiceState stracker = r.getTracker();
+                        if (stracker != null) {
+                            stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
+                                    SystemClock.uptimeMillis());
+                        }
                     }
                 }
                 if (alreadyStartedOp) {
@@ -1958,10 +1972,12 @@
 
                 r.isForeground = false;
                 r.mFgsExitTime = SystemClock.uptimeMillis();
-                ServiceState stracker = r.getTracker();
-                if (stracker != null) {
-                    stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
-                            SystemClock.uptimeMillis());
+                synchronized (mAm.mProcessStats.mLock) {
+                    final ServiceState stracker = r.getTracker();
+                    if (stracker != null) {
+                        stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(),
+                                SystemClock.uptimeMillis());
+                    }
                 }
                 mAm.mAppOpsService.finishOperation(
                         AppOpsManager.getToken(mAm.mAppOpsService),
@@ -2736,10 +2752,12 @@
                 s.lastActivity = SystemClock.uptimeMillis();
                 if (!s.hasAutoCreateConnections()) {
                     // This is the first binding, let the tracker know.
-                    ServiceState stracker = s.getTracker();
-                    if (stracker != null) {
-                        stracker.setBound(true, mAm.mProcessStats.getMemFactorLocked(),
-                                s.lastActivity);
+                    synchronized (mAm.mProcessStats.mLock) {
+                        final ServiceState stracker = s.getTracker();
+                        if (stracker != null) {
+                            stracker.setBound(true, mAm.mProcessStats.getMemFactorLocked(),
+                                    s.lastActivity);
+                        }
                     }
                 }
             }
@@ -3376,9 +3394,11 @@
         ProcessServiceRecord psr;
         if (r.executeNesting == 0) {
             r.executeFg = fg;
-            ServiceState stracker = r.getTracker();
-            if (stracker != null) {
-                stracker.setExecuting(true, mAm.mProcessStats.getMemFactorLocked(), now);
+            synchronized (mAm.mProcessStats.mLock) {
+                final ServiceState stracker = r.getTracker();
+                if (stracker != null) {
+                    stracker.setExecuting(true, mAm.mProcessStats.getMemFactorLocked(), now);
+                }
             }
             if (r.app != null) {
                 psr = r.app.mServices;
@@ -3573,7 +3593,9 @@
         if (!mRestartingServices.contains(r)) {
             r.createdFromFg = false;
             mRestartingServices.add(r);
-            r.makeRestarting(mAm.mProcessStats.getMemFactorLocked(), now);
+            synchronized (mAm.mProcessStats.mLock) {
+                r.makeRestarting(mAm.mProcessStats.getMemFactorLocked(), now);
+            }
         }
 
         cancelForegroundNotificationLocked(r);
@@ -3651,8 +3673,10 @@
                 }
             }
             if (!stillTracking) {
-                r.restartTracker.setRestarting(false, mAm.mProcessStats.getMemFactorLocked(),
-                        SystemClock.uptimeMillis());
+                synchronized (mAm.mProcessStats.mLock) {
+                    r.restartTracker.setRestarting(false, mAm.mProcessStats.getMemFactorLocked(),
+                            SystemClock.uptimeMillis());
+                }
                 r.restartTracker = null;
             }
         }
@@ -4172,9 +4196,11 @@
                     + r);
             r.fgRequired = false;
             r.fgWaiting = false;
-            ServiceState stracker = r.getTracker();
-            if (stracker != null) {
-                stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(), now);
+            synchronized (mAm.mProcessStats.mLock) {
+                ServiceState stracker = r.getTracker();
+                if (stracker != null) {
+                    stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(), now);
+                }
             }
             mAm.mAppOpsService.finishOperation(AppOpsManager.getToken(mAm.mAppOpsService),
                     AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null);
@@ -4231,9 +4257,11 @@
         cancelForegroundNotificationLocked(r);
         if (r.isForeground) {
             decActiveForegroundAppLocked(smap, r);
-            ServiceState stracker = r.getTracker();
-            if (stracker != null) {
-                stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(), now);
+            synchronized (mAm.mProcessStats.mLock) {
+                ServiceState stracker = r.getTracker();
+                if (stracker != null) {
+                    stracker.setForeground(false, mAm.mProcessStats.getMemFactorLocked(), now);
+                }
             }
             mAm.mAppOpsService.finishOperation(
                     AppOpsManager.getToken(mAm.mAppOpsService),
@@ -4301,13 +4329,15 @@
            ((ServiceRestarter)r.restarter).setService(null);
         }
 
-        int memFactor = mAm.mProcessStats.getMemFactorLocked();
-        if (r.tracker != null) {
-            r.tracker.setStarted(false, memFactor, now);
-            r.tracker.setBound(false, memFactor, now);
-            if (r.executeNesting == 0) {
-                r.tracker.clearCurrentOwner(r, false);
-                r.tracker = null;
+        synchronized (mAm.mProcessStats.mLock) {
+            final int memFactor = mAm.mProcessStats.getMemFactorLocked();
+            if (r.tracker != null) {
+                r.tracker.setStarted(false, memFactor, now);
+                r.tracker.setBound(false, memFactor, now);
+                if (r.executeNesting == 0) {
+                    r.tracker.clearCurrentOwner(r, false);
+                    r.tracker = null;
+                }
             }
         }
 
@@ -4438,8 +4468,10 @@
                 boolean hasAutoCreate = s.hasAutoCreateConnections();
                 if (!hasAutoCreate) {
                     if (s.tracker != null) {
-                        s.tracker.setBound(false, mAm.mProcessStats.getMemFactorLocked(),
-                                SystemClock.uptimeMillis());
+                        synchronized (mAm.mProcessStats.mLock) {
+                            s.tracker.setBound(false, mAm.mProcessStats.getMemFactorLocked(),
+                                    SystemClock.uptimeMillis());
+                        }
                     }
                 }
                 bringDownServiceIfNeededLocked(s, true, hasAutoCreate, enqueueOomAdj);
@@ -4531,12 +4563,14 @@
 
     private void serviceProcessGoneLocked(ServiceRecord r, boolean enqueueOomAdj) {
         if (r.tracker != null) {
-            int memFactor = mAm.mProcessStats.getMemFactorLocked();
-            long now = SystemClock.uptimeMillis();
-            r.tracker.setExecuting(false, memFactor, now);
-            r.tracker.setForeground(false, memFactor, now);
-            r.tracker.setBound(false, memFactor, now);
-            r.tracker.setStarted(false, memFactor, now);
+            synchronized (mAm.mProcessStats.mLock) {
+                final int memFactor = mAm.mProcessStats.getMemFactorLocked();
+                final long now = SystemClock.uptimeMillis();
+                r.tracker.setExecuting(false, memFactor, now);
+                r.tracker.setForeground(false, memFactor, now);
+                r.tracker.setBound(false, memFactor, now);
+                r.tracker.setStarted(false, memFactor, now);
+            }
         }
         serviceDoneExecutingLocked(r, true, true, enqueueOomAdj);
     }
@@ -4583,13 +4617,15 @@
             }
             r.executeFg = false;
             if (r.tracker != null) {
-                final int memFactor = mAm.mProcessStats.getMemFactorLocked();
-                final long now = SystemClock.uptimeMillis();
-                r.tracker.setExecuting(false, memFactor, now);
-                r.tracker.setForeground(false, memFactor, now);
-                if (finishing) {
-                    r.tracker.clearCurrentOwner(r, false);
-                    r.tracker = null;
+                synchronized (mAm.mProcessStats.mLock) {
+                    final int memFactor = mAm.mProcessStats.getMemFactorLocked();
+                    final long now = SystemClock.uptimeMillis();
+                    r.tracker.setExecuting(false, memFactor, now);
+                    r.tracker.setForeground(false, memFactor, now);
+                    if (finishing) {
+                        r.tracker.clearCurrentOwner(r, false);
+                        r.tracker = null;
+                    }
                 }
             }
             if (finishing) {
@@ -4974,8 +5010,10 @@
                     // down it.
                     sr.startRequested = false;
                     if (sr.tracker != null) {
-                        sr.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
-                                SystemClock.uptimeMillis());
+                        synchronized (mAm.mProcessStats.mLock) {
+                            sr.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
+                                    SystemClock.uptimeMillis());
+                        }
                     }
                 }
             }
@@ -6276,10 +6314,17 @@
             final String msg = "Background started FGS: "
                     + ((r.mAllowStartForeground != REASON_DENIED) ? "Allowed " : "Disallowed ")
                     + r.mInfoAllowStartForeground;
-            Slog.wtfQuiet(TAG, msg);
             if (r.mAllowStartForeground != REASON_DENIED) {
+                if (ActivityManagerUtils.shouldSamplePackageForAtom(r.packageName,
+                        mAm.mConstants.mFgsStartAllowedLogSampleRate)) {
+                    Slog.wtfQuiet(TAG, msg);
+                }
                 Slog.i(TAG, msg);
             } else {
+                if (ActivityManagerUtils.shouldSamplePackageForAtom(r.packageName,
+                        mAm.mConstants.mFgsStartDeniedLogSampleRate)) {
+                    Slog.wtfQuiet(TAG, msg);
+                }
                 Slog.w(TAG, msg);
             }
             r.mLoggedInfoAllowStartForeground = true;
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index ac0a198..eeb41a3 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -108,6 +108,8 @@
     static final String KEY_FG_TO_BG_FGS_GRACE_DURATION = "fg_to_bg_fgs_grace_duration";
     static final String KEY_FGS_START_FOREGROUND_TIMEOUT = "fgs_start_foreground_timeout";
     static final String KEY_FGS_ATOM_SAMPLE_RATE = "fgs_atom_sample_rate";
+    static final String KEY_FGS_START_ALLOWED_LOG_SAMPLE_RATE = "fgs_start_allowed_log_sample_rate";
+    static final String KEY_FGS_START_DENIED_LOG_SAMPLE_RATE = "fgs_start_denied_log_sample_rate";
     static final String KEY_FGS_ALLOW_OPT_OUT = "fgs_allow_opt_out";
 
     private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
@@ -152,6 +154,8 @@
     private static final long DEFAULT_FG_TO_BG_FGS_GRACE_DURATION = 5 * 1000;
     private static final int DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS = 10 * 1000;
     private static final float DEFAULT_FGS_ATOM_SAMPLE_RATE = 1; // 100 %
+    private static final float DEFAULT_FGS_START_ALLOWED_LOG_SAMPLE_RATE = 0.25f; // 25%
+    private static final float DEFAULT_FGS_START_DENIED_LOG_SAMPLE_RATE = 1; // 100%
     /**
      * Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED}
      */
@@ -496,6 +500,20 @@
     volatile float mFgsAtomSampleRate = DEFAULT_FGS_ATOM_SAMPLE_RATE;
 
     /**
+     * Sample rate for the allowed FGS start WTF logs.
+     *
+     * If the value is 0.1, 10% of the logs would be sampled.
+     */
+    volatile float mFgsStartAllowedLogSampleRate = DEFAULT_FGS_START_ALLOWED_LOG_SAMPLE_RATE;
+
+    /**
+     * Sample rate for the denied FGS start WTF logs.
+     *
+     * If the value is 0.1, 10% of the logs would be sampled.
+     */
+    volatile float mFgsStartDeniedLogSampleRate = DEFAULT_FGS_START_DENIED_LOG_SAMPLE_RATE;
+
+    /**
      * Whether to allow "opt-out" from the foreground service restrictions.
      * (https://developer.android.com/about/versions/12/foreground-services)
      */
@@ -711,6 +729,12 @@
                             case KEY_FGS_ATOM_SAMPLE_RATE:
                                 updateFgsAtomSamplePercent();
                                 break;
+                            case KEY_FGS_START_ALLOWED_LOG_SAMPLE_RATE:
+                                updateFgsStartAllowedLogSamplePercent();
+                                break;
+                            case KEY_FGS_START_DENIED_LOG_SAMPLE_RATE:
+                                updateFgsStartDeniedLogSamplePercent();
+                                break;
                             case KEY_FGS_ALLOW_OPT_OUT:
                                 updateFgsAllowOptOut();
                                 break;
@@ -1057,6 +1081,20 @@
                 DEFAULT_FGS_ATOM_SAMPLE_RATE);
     }
 
+    private void updateFgsStartAllowedLogSamplePercent() {
+        mFgsStartAllowedLogSampleRate = DeviceConfig.getFloat(
+                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                KEY_FGS_START_ALLOWED_LOG_SAMPLE_RATE,
+                DEFAULT_FGS_START_ALLOWED_LOG_SAMPLE_RATE);
+    }
+
+    private void updateFgsStartDeniedLogSamplePercent() {
+        mFgsStartDeniedLogSampleRate = DeviceConfig.getFloat(
+                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                KEY_FGS_START_DENIED_LOG_SAMPLE_RATE,
+                DEFAULT_FGS_START_DENIED_LOG_SAMPLE_RATE);
+    }
+
     private void updateFgsAllowOptOut() {
         mFgsAllowOptOut = DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -1285,6 +1323,10 @@
         pw.print("="); pw.println(mFgsStartRestrictionCheckCallerTargetSdk);
         pw.print("  "); pw.print(KEY_FGS_ATOM_SAMPLE_RATE);
         pw.print("="); pw.println(mFgsAtomSampleRate);
+        pw.print("  "); pw.print(KEY_FGS_START_ALLOWED_LOG_SAMPLE_RATE);
+        pw.print("="); pw.println(mFgsStartAllowedLogSampleRate);
+        pw.print("  "); pw.print(KEY_FGS_START_DENIED_LOG_SAMPLE_RATE);
+        pw.print("="); pw.println(mFgsStartDeniedLogSampleRate);
         pw.print("  "); pw.print(KEY_PUSH_MESSAGING_OVER_QUOTA_BEHAVIOR);
         pw.print("="); pw.println(mPushMessagingOverQuotaBehavior);
         pw.print("  "); pw.print(KEY_FGS_ALLOW_OPT_OUT);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f6510e6..85ffaa2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -193,6 +193,7 @@
 import android.app.usage.UsageStatsManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetManagerInternal;
 import android.content.AttributionSource;
 import android.content.AutofillOptions;
 import android.content.BroadcastReceiver;
@@ -16578,13 +16579,21 @@
         enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
                 "scheduleApplicationInfoChanged()");
 
-        synchronized (mProcLock) {
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                updateApplicationInfoLOSP(packageNames, userId);
-            } finally {
-                Binder.restoreCallingIdentity(origId);
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            final boolean updateFrameworkRes = packageNames.contains("android");
+            synchronized (mProcLock) {
+                updateApplicationInfoLOSP(packageNames, updateFrameworkRes, userId);
             }
+
+            AppWidgetManagerInternal widgets = LocalServices.getService(
+                    AppWidgetManagerInternal.class);
+            if (widgets != null) {
+                widgets.applyResourceOverlaysToWidgets(new HashSet<>(packageNames), userId,
+                        updateFrameworkRes);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
         }
     }
 
@@ -16601,11 +16610,12 @@
     }
 
     @GuardedBy(anyOf = {"this", "mProcLock"})
-    private void updateApplicationInfoLOSP(@NonNull List<String> packagesToUpdate, int userId) {
-        final boolean updateFrameworkRes = packagesToUpdate.contains("android");
+    private void updateApplicationInfoLOSP(@NonNull List<String> packagesToUpdate,
+            boolean updateFrameworkRes, int userId) {
         if (updateFrameworkRes) {
             ParsingPackageUtils.readConfigUseRoundIcon(null);
         }
+
         mProcessList.updateApplicationInfoLOSP(packagesToUpdate, userId, updateFrameworkRes);
 
         if (updateFrameworkRes) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 9f41c8b..ae14ca7 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -841,6 +841,12 @@
 
     public void noteEvent(final int code, final String name, final int uid) {
         enforceCallingPermission();
+        if (name == null) {
+            // TODO(b/194733136): Replace with an IllegalArgumentException throw.
+            Slog.wtfStack(TAG, "noteEvent called with null name. code = " + code);
+            return;
+        }
+
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 9dbb707..f32aa22 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -41,6 +41,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.ProcLocksReader;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.ServiceThread;
 
@@ -100,7 +101,7 @@
 
     // Defaults for phenotype flags.
     @VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = false;
-    @VisibleForTesting static final Boolean DEFAULT_USE_FREEZER = false;
+    @VisibleForTesting static final Boolean DEFAULT_USE_FREEZER = true;
     @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_1 = COMPACT_ACTION_FILE;
     @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_2 = COMPACT_ACTION_FULL;
     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_1 = 5_000;
@@ -275,7 +276,7 @@
             DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ;
     @GuardedBy("mPhenotypeFlagLock")
     private volatile boolean mUseCompaction = DEFAULT_USE_COMPACTION;
-    private volatile boolean mUseFreezer = DEFAULT_USE_FREEZER;
+    private volatile boolean mUseFreezer = false; // set to DEFAULT in init()
     @GuardedBy("this")
     private int mFreezerDisableCount = 1; // Freezer is initially disabled, until enabled
     private final Random mRandom = new Random();
@@ -319,6 +320,7 @@
     private int mPersistentCompactionCount;
     private int mBfgsCompactionCount;
     private final ProcessDependencies mProcessDependencies;
+    private final ProcLocksReader mProcLocksReader;
 
     public CachedAppOptimizer(ActivityManagerService am) {
         this(am, null, new DefaultProcessDependencies());
@@ -335,6 +337,7 @@
         mProcessDependencies = processDependencies;
         mTestCallback = callback;
         mSettingsObserver = new SettingsContentObserver();
+        mProcLocksReader = new ProcLocksReader();
     }
 
     /**
@@ -675,6 +678,8 @@
                     KEY_USE_FREEZER, DEFAULT_USE_FREEZER)) {
             mUseFreezer = isFreezerSupported();
             updateFreezerDebounceTimeout();
+        } else {
+            mUseFreezer = false;
         }
 
         final boolean useFreezer = mUseFreezer;
@@ -1312,7 +1317,7 @@
 
             try {
                 // pre-check for locks to avoid unnecessary freeze/unfreeze operations
-                if (Process.hasFileLocks(pid)) {
+                if (mProcLocksReader.hasFileLocks(pid)) {
                     if (DEBUG_FREEZER) {
                         Slog.d(TAG_AM, name + " (" + pid + ") holds file locks, not freezing");
                     }
@@ -1399,7 +1404,7 @@
 
             try {
                 // post-check to prevent races
-                if (Process.hasFileLocks(pid)) {
+                if (mProcLocksReader.hasFileLocks(pid)) {
                     if (DEBUG_FREEZER) {
                         Slog.d(TAG_AM, name + " (" + pid + ") holds file locks, reverting freeze");
                     }
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index e6cd509..9161271 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -47,6 +47,7 @@
     public AssociationState.SourceState association; // Association tracking
     String stringName;              // Caching of toString.
     boolean serviceDead;            // Well is it?
+    private Object mProcStatsLock;  // Internal lock for accessing AssociationState
 
     // Please keep the following two enum list synced.
     private static final int[] BIND_ORIG_ENUMS = new int[] {
@@ -137,23 +138,29 @@
                 Slog.wtf(TAG_AM, "Inactive holder in referenced service "
                         + binding.service.shortInstanceName + ": proc=" + binding.service.app);
             } else {
-                association = holder.pkg.getAssociationStateLocked(holder.state,
-                        binding.service.instanceName.getClassName()).startSource(clientUid,
-                        clientProcessName, clientPackageName);
-
+                mProcStatsLock = binding.service.app.mService.mProcessStats.mLock;
+                synchronized (mProcStatsLock) {
+                    association = holder.pkg.getAssociationStateLocked(holder.state,
+                            binding.service.instanceName.getClassName()).startSource(clientUid,
+                            clientProcessName, clientPackageName);
+                }
             }
         }
     }
 
     public void trackProcState(int procState, int seq, long now) {
         if (association != null) {
-            association.trackProcState(procState, seq, now);
+            synchronized (mProcStatsLock) {
+                association.trackProcState(procState, seq, now);
+            }
         }
     }
 
     public void stopAssociation() {
         if (association != null) {
-            association.stop();
+            synchronized (mProcStatsLock) {
+                association.stop();
+            }
             association = null;
         }
     }
diff --git a/services/core/java/com/android/server/am/ContentProviderConnection.java b/services/core/java/com/android/server/am/ContentProviderConnection.java
index 3bc4fcf..3b9d47d 100644
--- a/services/core/java/com/android/server/am/ContentProviderConnection.java
+++ b/services/core/java/com/android/server/am/ContentProviderConnection.java
@@ -38,6 +38,7 @@
     public final String clientPackage;
     public AssociationState.SourceState association;
     public final long createTime;
+    private Object mProcStatsLock;  // Internal lock for accessing AssociationState
 
     /**
      * Internal lock that guards access to the two counters.
@@ -87,23 +88,29 @@
                 Slog.wtf(TAG_AM, "Inactive holder in referenced provider "
                         + provider.name.toShortString() + ": proc=" + provider.proc);
             } else {
-                association = holder.pkg.getAssociationStateLocked(holder.state,
-                        provider.name.getClassName()).startSource(client.uid, client.processName,
-                        clientPackage);
-
+                mProcStatsLock = provider.proc.mService.mProcessStats.mLock;
+                synchronized (mProcStatsLock) {
+                    association = holder.pkg.getAssociationStateLocked(holder.state,
+                            provider.name.getClassName()).startSource(client.uid,
+                            client.processName, clientPackage);
+                }
             }
         }
     }
 
     public void trackProcState(int procState, int seq, long now) {
         if (association != null) {
-            association.trackProcState(procState, seq, now);
+            synchronized (mProcStatsLock) {
+                association.trackProcState(procState, seq, now);
+            }
         }
     }
 
     public void stopAssociation() {
         if (association != null) {
-            association.stop();
+            synchronized (mProcStatsLock) {
+                association.stop();
+            }
             association = null;
         }
     }
diff --git a/services/core/java/com/android/server/am/ContentProviderRecord.java b/services/core/java/com/android/server/am/ContentProviderRecord.java
index 5fd15db..75f31c0 100644
--- a/services/core/java/com/android/server/am/ContentProviderRecord.java
+++ b/services/core/java/com/android/server/am/ContentProviderRecord.java
@@ -321,6 +321,7 @@
         final String mOwningProcessName;
         int mAcquisitionCount;
         AssociationState.SourceState mAssociation;
+        private Object mProcStatsLock;  // Internal lock for accessing AssociationState
 
         public ExternalProcessHandle(IBinder token, int owningUid, String owningProcessName) {
             mToken = token;
@@ -353,17 +354,21 @@
                     Slog.wtf(TAG_AM, "Inactive holder in referenced provider "
                             + provider.name.toShortString() + ": proc=" + provider.proc);
                 } else {
-                    mAssociation = holder.pkg.getAssociationStateLocked(holder.state,
-                            provider.name.getClassName()).startSource(mOwningUid,
-                            mOwningProcessName, null);
-
+                    mProcStatsLock = provider.proc.mService.mProcessStats.mLock;
+                    synchronized (mProcStatsLock) {
+                        mAssociation = holder.pkg.getAssociationStateLocked(holder.state,
+                                provider.name.getClassName()).startSource(mOwningUid,
+                                mOwningProcessName, null);
+                    }
                 }
             }
         }
 
         public void stopAssociation() {
             if (mAssociation != null) {
-                mAssociation.stop();
+                synchronized (mProcStatsLock) {
+                    mAssociation.stop();
+                }
                 mAssociation = null;
             }
         }
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 5c9d385..2e3e635 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -94,8 +94,6 @@
         sGlobalSettingToTypeMap.put(
                 Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES, String.class);
         sGlobalSettingToTypeMap.put(
-                Settings.Global.ANGLE_ALLOWLIST, String.class);
-        sGlobalSettingToTypeMap.put(
                 Settings.Global.ANGLE_EGL_FEATURES, String.class);
         sGlobalSettingToTypeMap.put(
                 Settings.Global.SHOW_ANGLE_IN_USE_DIALOG_BOX, String.class);
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index d6bf8db..5c19ceb 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -2792,8 +2792,10 @@
                 state.setNotCachedSinceIdle(false);
             }
             if (!doingAll) {
-                mService.setProcessTrackerStateLOSP(app,
-                        mService.mProcessStats.getMemFactorLocked(), now);
+                synchronized (mService.mProcessStats.mLock) {
+                    mService.setProcessTrackerStateLOSP(app,
+                            mService.mProcessStats.getMemFactorLocked(), now);
+                }
             } else {
                 state.setProcStateChanged(true);
             }
diff --git a/services/core/java/com/android/server/am/PhantomProcessList.java b/services/core/java/com/android/server/am/PhantomProcessList.java
index ca31681..b07684c 100644
--- a/services/core/java/com/android/server/am/PhantomProcessList.java
+++ b/services/core/java/com/android/server/am/PhantomProcessList.java
@@ -365,6 +365,9 @@
     private int onPhantomProcessFdEvent(FileDescriptor fd, int events) {
         synchronized (mLock) {
             final PhantomProcessRecord proc = mPhantomProcessesPidFds.get(fd.getInt$());
+            if (proc == null) {
+                return 0;
+            }
             if ((events & EVENT_INPUT) != 0) {
                 proc.onProcDied(true);
             } else {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index a9905dc..64b9bd9 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -45,6 +45,9 @@
 import static android.app.AppOpsManager.OP_PLAY_AUDIO;
 import static android.app.AppOpsManager.OP_RECORD_AUDIO;
 import static android.app.AppOpsManager.OP_RECORD_AUDIO_HOTWORD;
+import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_FAILED;
+import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_RESUMED;
+import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_STARTED;
 import static android.app.AppOpsManager.OpEventProxyInfo;
 import static android.app.AppOpsManager.RestrictionBypass;
 import static android.app.AppOpsManager.SAMPLING_STRATEGY_BOOT_TIME_SAMPLING;
@@ -1238,6 +1241,11 @@
                     scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid, parent.packageName,
                             tag, true, event.getAttributionFlags(), event.getAttributionChainId());
                 }
+                // Note: this always sends MODE_ALLOWED, even if the mode is FOREGROUND
+                // TODO ntmyren: figure out how to get the real mode.
+                scheduleOpStartedIfNeededLocked(parent.op, parent.uid, parent.packageName,
+                        tag, event.getFlags(), MODE_ALLOWED, START_TYPE_RESUMED,
+                        event.getAttributionFlags(), event.getAttributionChainId());
             }
             mPausedInProgressEvents = null;
         }
@@ -3438,7 +3446,7 @@
                         + " package " + packageName + "flags: " +
                         AppOpsManager.flagsToString(flags));
                 return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag,
-                        packageName + " flags: " + AppOpsManager.flagsToString(flags));
+                        packageName);
             }
             final Op op = getOpLocked(ops, code, uid, true);
             final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag);
@@ -3945,13 +3953,15 @@
         }
 
         boolean isRestricted = false;
+        int startType = START_TYPE_FAILED;
         synchronized (this) {
             final Ops ops = getOpsLocked(uid, packageName, attributionTag,
                     pvr.isAttributionTagValid, pvr.bypass, /* edit */ true);
             if (ops == null) {
                 if (!dryRun) {
                     scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
-                            flags, AppOpsManager.MODE_IGNORED);
+                            flags, AppOpsManager.MODE_IGNORED, startType, attributionFlags,
+                            attributionChainId);
                 }
                 if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid
                         + " package " + packageName + " flags: "
@@ -3977,7 +3987,7 @@
                     if (!dryRun) {
                         attributedOp.rejected(uidState.state, flags);
                         scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
-                                flags, uidMode);
+                                flags, uidMode, startType, attributionFlags, attributionChainId);
                     }
                     return new SyncNotedAppOp(uidMode, code, attributionTag, packageName);
                 }
@@ -3993,7 +4003,7 @@
                     if (!dryRun) {
                         attributedOp.rejected(uidState.state, flags);
                         scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
-                                flags, mode);
+                                flags, mode, startType, attributionFlags, attributionChainId);
                     }
                     return new SyncNotedAppOp(mode, code, attributionTag, packageName);
                 }
@@ -4011,12 +4021,14 @@
                         attributedOp.started(clientId, proxyUid, proxyPackageName,
                                 proxyAttributionTag, uidState.state, flags, attributionFlags,
                                 attributionChainId);
+                        startType = START_TYPE_STARTED;
                     }
                 } catch (RemoteException e) {
                     throw new RuntimeException(e);
                 }
                 scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, flags,
-                        isRestricted ? MODE_IGNORED : MODE_ALLOWED);
+                        isRestricted ? MODE_IGNORED : MODE_ALLOWED, startType, attributionFlags,
+                        attributionChainId);
             }
         }
 
@@ -4187,7 +4199,9 @@
     }
 
     private void scheduleOpStartedIfNeededLocked(int code, int uid, String pkgName,
-            String attributionTag, @OpFlags int flags, @Mode int result) {
+            String attributionTag, @OpFlags int flags, @Mode int result,
+            @AppOpsManager.OnOpStartedListener.StartedType int startedType,
+            @AttributionFlags int attributionFlags, int attributionChainId) {
         ArraySet<StartedCallback> dispatchedCallbacks = null;
         final int callbackListCount = mStartedWatchers.size();
         for (int i = 0; i < callbackListCount; i++) {
@@ -4213,12 +4227,13 @@
         mHandler.sendMessage(PooledLambda.obtainMessage(
                 AppOpsService::notifyOpStarted,
                 this, dispatchedCallbacks, code, uid, pkgName, attributionTag, flags,
-                result));
+                result, startedType, attributionFlags, attributionChainId));
     }
 
     private void notifyOpStarted(ArraySet<StartedCallback> callbacks,
             int code, int uid, String packageName, String attributionTag, @OpFlags int flags,
-            @Mode int result) {
+            @Mode int result, @AppOpsManager.OnOpStartedListener.StartedType int startedType,
+            @AttributionFlags int attributionFlags, int attributionChainId) {
         final long identity = Binder.clearCallingIdentity();
         try {
             final int callbackCount = callbacks.size();
@@ -4226,7 +4241,7 @@
                 final StartedCallback callback = callbacks.valueAt(i);
                 try {
                     callback.mCallback.opStarted(code, uid, packageName, attributionTag, flags,
-                            result);
+                            result, startedType, attributionFlags, attributionChainId);
                 } catch (RemoteException e) {
                     /* do nothing */
                 }
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 8961a5a..5ecdfe4 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1719,11 +1719,10 @@
         if (client == null) {
             return;
         }
-        Log.w(TAG, "Speaker client died");
-        if (removeCommunicationRouteClient(client.getBinder(), false)
-                != null) {
-            onUpdateCommunicationRoute("onCommunicationRouteClientDied");
-        }
+        Log.w(TAG, "Communication client died");
+        setCommunicationRouteForClient(
+                client.getBinder(), client.getPid(), null, BtHelper.SCO_MODE_UNDEFINED,
+                "onCommunicationRouteClientDied");
     }
 
     /**
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f7d0914..8d9479c 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -736,6 +736,11 @@
     private VolumePolicy mVolumePolicy = VolumePolicy.DEFAULT;
     private long mLoweredFromNormalToVibrateTime;
 
+    // Uid of the active hotword detection service to check if caller is the one or not.
+    @GuardedBy("mHotwordDetectionServiceUidLock")
+    private int mHotwordDetectionServiceUid = android.os.Process.INVALID_UID;
+    private final Object mHotwordDetectionServiceUidLock = new Object();
+
     // Array of Uids of valid accessibility services to check if caller is one of them
     private final Object mAccessibilityServiceUidsLock = new Object();
     @GuardedBy("mAccessibilityServiceUidsLock")
@@ -1337,6 +1342,9 @@
             updateAssistantUId(true);
             AudioSystem.setRttEnabled(mRttEnabled);
         }
+        synchronized (mHotwordDetectionServiceUidLock) {
+            AudioSystem.setHotwordDetectionServiceUid(mHotwordDetectionServiceUid);
+        }
         synchronized (mAccessibilityServiceUidsLock) {
             AudioSystem.setA11yServicesUids(mAccessibilityServiceUids);
         }
@@ -2815,8 +2823,8 @@
         if (uid == android.os.Process.SYSTEM_UID) {
             uid = UserHandle.getUid(getCurrentUserId(), UserHandle.getAppId(uid));
         }
-        if (mAppOps.noteOp(STREAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage)
-                != AppOpsManager.MODE_ALLOWED) {
+        // validate calling package and app op
+        if (!checkNoteAppOp(STREAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage)) {
             return;
         }
 
@@ -3539,8 +3547,7 @@
         if (uid == android.os.Process.SYSTEM_UID) {
             uid = UserHandle.getUid(getCurrentUserId(), UserHandle.getAppId(uid));
         }
-        if (mAppOps.noteOp(STREAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage)
-                != AppOpsManager.MODE_ALLOWED) {
+        if (!checkNoteAppOp(STREAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage)) {
             return;
         }
 
@@ -3975,8 +3982,7 @@
             uid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
         }
         // If OP_AUDIO_MASTER_VOLUME is set, disallow unmuting.
-        if (!mute && mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, uid, callingPackage)
-                != AppOpsManager.MODE_ALLOWED) {
+        if (!mute && !checkNoteAppOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, uid, callingPackage)) {
             return;
         }
         if (userId != UserHandle.getCallingUserId() &&
@@ -4107,8 +4113,7 @@
                         ? MediaMetrics.Value.MUTE : MediaMetrics.Value.UNMUTE);
 
         // If OP_MUTE_MICROPHONE is set, disallow unmuting.
-        if (!on && mAppOps.noteOp(AppOpsManager.OP_MUTE_MICROPHONE, uid, callingPackage)
-                != AppOpsManager.MODE_ALLOWED) {
+        if (!on && !checkNoteAppOp(AppOpsManager.OP_MUTE_MICROPHONE, uid, callingPackage)) {
             mmi.set(MediaMetrics.Property.EARLY_RETURN, "disallow unmuting").record();
             return;
         }
@@ -9108,6 +9113,16 @@
         }
 
         @Override
+        public void setHotwordDetectionServiceUid(int uid) {
+            synchronized (mHotwordDetectionServiceUidLock) {
+                if (mHotwordDetectionServiceUid != uid) {
+                    mHotwordDetectionServiceUid = uid;
+                    AudioSystem.setHotwordDetectionServiceUid(mHotwordDetectionServiceUid);
+                }
+            }
+        }
+
+        @Override
         public void setAccessibilityServiceUids(IntArray uids) {
             synchronized (mAccessibilityServiceUidsLock) {
                 if (uids.size() == 0) {
@@ -10504,4 +10519,31 @@
         }
         mFullVolumeDevices.remove(audioSystemDeviceOut);
     }
+
+    //====================
+    // Helper functions for app ops
+    //====================
+    /**
+     * Validates, and notes an app op for a given uid and package name.
+     * Validation comes from exception catching: a security exception indicates the package
+     * doesn't exist, an IAE indicates the uid and package don't match. The code only checks
+     * if exception was thrown for robustness to code changes in op validation
+     * @param op the app op to check
+     * @param uid the uid of the caller
+     * @param packageName the package to check
+     * @return true if the origin of the call is valid (no uid / package mismatch) and the caller
+     *      is allowed to perform the operation
+     */
+    private boolean checkNoteAppOp(int op, int uid, String packageName) {
+        try {
+            if (mAppOps.noteOp(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
+                return false;
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Error noting op:" + op + " on uid:" + uid + " for package:"
+                    + packageName, e);
+            return false;
+        }
+        return true;
+    }
 }
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index 973fbd2..6d56780 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -367,6 +367,14 @@
     }
 
     /**
+     * Same as {@link AudioSystem#setHotwordDetectionServiceUid(int)}
+     * Communicate UID of current HotwordDetectionService to audio policy service.
+     */
+    public int setHotwordDetectionServiceUid(int uid) {
+        return AudioSystem.setHotwordDetectionServiceUid(uid);
+    }
+
+    /**
      * Same as {@link AudioSystem#setCurrentImeUid(int)}
      * Communicate UID of current InputMethodService to audio policy service.
      */
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 4973d45..b1d300c 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -1106,6 +1106,11 @@
             return Settings.Secure.getInt(context.getContentResolver(),
                     CoexCoordinator.SETTING_ENABLE_NAME, 1) != 0;
         }
+
+        public boolean isCoexFaceNonBypassHapticsDisabled(Context context) {
+            return Settings.Secure.getInt(context.getContentResolver(),
+                    CoexCoordinator.FACE_HAPTIC_DISABLE, 1) != 0;
+        }
     }
 
     /**
@@ -1137,6 +1142,8 @@
         //  by default.
         CoexCoordinator coexCoordinator = CoexCoordinator.getInstance();
         coexCoordinator.setAdvancedLogicEnabled(injector.isAdvancedCoexLogicEnabled(context));
+        coexCoordinator.setFaceHapticDisabledWhenNonBypass(
+                injector.isCoexFaceNonBypassHapticsDisabled(context));
 
         try {
             injector.getActivityManagerService().registerUserSwitchObserver(
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 013c74d..6f38ed0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -54,12 +54,18 @@
     public static final int STATE_NEW = 0;
     // Framework/HAL have started this operation
     public static final int STATE_STARTED = 1;
-    // Operation is started, but requires some user action (such as finger lift & re-touch)
+    // Operation is started, but requires some user action to start (such as finger lift & re-touch)
     public static final int STATE_STARTED_PAUSED = 2;
+    // Same as above, except auth was attempted (rejected, timed out, etc).
+    public static final int STATE_STARTED_PAUSED_ATTEMPTED = 3;
     // Done, errored, canceled, etc. HAL/framework are not running this sensor anymore.
-    public static final int STATE_STOPPED = 3;
+    public static final int STATE_STOPPED = 4;
 
-    @IntDef({STATE_NEW, STATE_STARTED, STATE_STARTED_PAUSED, STATE_STOPPED})
+    @IntDef({STATE_NEW,
+            STATE_STARTED,
+            STATE_STARTED_PAUSED,
+            STATE_STARTED_PAUSED_ATTEMPTED,
+            STATE_STOPPED})
     @interface State {}
 
     private final boolean mIsStrongBiometric;
@@ -76,7 +82,7 @@
 
     private long mStartTimeMs;
 
-    protected boolean mAuthAttempted;
+    private boolean mAuthAttempted;
 
     // TODO: This is currently hard to maintain, as each AuthenticationClient subclass must update
     //  the state. We should think of a way to improve this in the future.
@@ -92,6 +98,12 @@
      */
     protected abstract void handleLifecycleAfterAuth(boolean authenticated);
 
+    /**
+     * @return true if a user was detected (i.e. face was found, fingerprint sensor was touched.
+     *         etc)
+     */
+    public abstract boolean wasUserDetected();
+
     public AuthenticationClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
             @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
             int targetUserId, long operationId, boolean restricted, @NonNull String owner,
@@ -174,7 +186,8 @@
                 + ", isBP: " + isBiometricPrompt()
                 + ", listener: " + listener
                 + ", requireConfirmation: " + mRequireConfirmation
-                + ", user: " + getTargetUserId());
+                + ", user: " + getTargetUserId()
+                + ", clientMonitor: " + toString());
 
         final PerformanceTracker pm = PerformanceTracker.getInstanceForSensorId(getSensorId());
         if (isCryptoOperation()) {
@@ -298,6 +311,11 @@
                 public void handleLifecycleAfterAuth() {
                     AuthenticationClient.this.handleLifecycleAfterAuth(true /* authenticated */);
                 }
+
+                @Override
+                public void sendAuthenticationCanceled() {
+                    sendCancelOnly(listener);
+                }
             });
         } else {
             // Allow system-defined limit of number of attempts before giving up
@@ -332,10 +350,30 @@
                 public void handleLifecycleAfterAuth() {
                     AuthenticationClient.this.handleLifecycleAfterAuth(false /* authenticated */);
                 }
+
+                @Override
+                public void sendAuthenticationCanceled() {
+                    sendCancelOnly(listener);
+                }
             });
         }
     }
 
+    private void sendCancelOnly(@Nullable ClientMonitorCallbackConverter listener) {
+        if (listener == null) {
+            Slog.e(TAG, "Unable to sendAuthenticationCanceled, listener null");
+            return;
+        }
+        try {
+            listener.onError(getSensorId(),
+                    getCookie(),
+                    BiometricConstants.BIOMETRIC_ERROR_CANCELED,
+                    0 /* vendorCode */);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote exception", e);
+        }
+    }
+
     @Override
     public void onAcquired(int acquiredInfo, int vendorCode) {
         super.onAcquired(acquiredInfo, vendorCode);
@@ -349,9 +387,11 @@
     }
 
     @Override
-    public void onError(int errorCode, int vendorCode) {
+    public void onError(@BiometricConstants.Errors int errorCode, int vendorCode) {
         super.onError(errorCode, vendorCode);
         mState = STATE_STOPPED;
+
+        CoexCoordinator.getInstance().onAuthenticationError(this, errorCode, this::vibrateError);
     }
 
     /**
@@ -413,4 +453,8 @@
     public boolean interruptsPrecedingClients() {
         return true;
     }
+
+    public boolean wasAuthAttempted() {
+        return mAuthAttempted;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
index e5e1385..3eb6f4a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
@@ -206,7 +206,6 @@
             }
             mToken = null;
         }
-        mListener = null;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index b20316e..feb9e2a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -295,6 +295,7 @@
         @Override
         public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
             mHandler.post(() -> {
+                clientMonitor.destroy();
                 if (mCurrentOperation == null) {
                     Slog.e(getTag(), "[Finishing] " + clientMonitor
                             + " but current operation is null, success: " + success
diff --git a/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java b/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java
index b576673..25d4a38 100644
--- a/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java
@@ -22,6 +22,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.hardware.biometrics.BiometricConstants;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.Slog;
@@ -45,6 +46,8 @@
     private static final String TAG = "BiometricCoexCoordinator";
     public static final String SETTING_ENABLE_NAME =
             "com.android.server.biometrics.sensors.CoexCoordinator.enable";
+    public static final String FACE_HAPTIC_DISABLE =
+            "com.android.server.biometrics.sensors.CoexCoordinator.disable_face_haptics";
     private static final boolean DEBUG = true;
 
     // Successful authentications should be used within this amount of time.
@@ -52,7 +55,7 @@
 
     /**
      * Callback interface notifying the owner of "results" from the CoexCoordinator's business
-     * logic.
+     * logic for accept and reject.
      */
     interface Callback {
         /**
@@ -71,6 +74,22 @@
          * from scheduler if auth was successful).
          */
         void handleLifecycleAfterAuth();
+
+        /**
+         * Requests the owner to notify the caller that authentication was canceled.
+         */
+        void sendAuthenticationCanceled();
+    }
+
+    /**
+     * Callback interface notifying the owner of "results" from the CoexCoordinator's business
+     * logic for errors.
+     */
+    interface ErrorCallback {
+        /**
+         * Requests the owner to initiate a vibration for this event.
+         */
+        void sendHapticFeedback();
     }
 
     private static CoexCoordinator sInstance;
@@ -144,6 +163,10 @@
         mAdvancedLogicEnabled = enabled;
     }
 
+    public void setFaceHapticDisabledWhenNonBypass(boolean disabled) {
+        mFaceHapticDisabledWhenNonBypass = disabled;
+    }
+
     @VisibleForTesting
     void reset() {
         mClientMap.clear();
@@ -153,6 +176,7 @@
     private final Map<Integer, AuthenticationClient<?>> mClientMap;
     @VisibleForTesting final LinkedList<SuccessfulAuth> mSuccessfulAuths;
     private boolean mAdvancedLogicEnabled;
+    private boolean mFaceHapticDisabledWhenNonBypass;
     private final Handler mHandler;
 
     private CoexCoordinator() {
@@ -191,6 +215,9 @@
         mClientMap.remove(sensorType);
     }
 
+    /**
+     * Notify the coordinator that authentication succeeded (accepted)
+     */
     public void onAuthenticationSucceeded(long currentTimeMillis,
             @NonNull AuthenticationClient<?> client,
             @NonNull Callback callback) {
@@ -225,7 +252,11 @@
                         mSuccessfulAuths.add(new SuccessfulAuth(mHandler, mSuccessfulAuths,
                                 currentTimeMillis, SENSOR_TYPE_FACE, client, callback));
                     } else {
-                        callback.sendHapticFeedback();
+                        if (mFaceHapticDisabledWhenNonBypass && !face.isKeyguardBypassEnabled()) {
+                            Slog.w(TAG, "Skipping face success haptic");
+                        } else {
+                            callback.sendHapticFeedback();
+                        }
                         callback.sendAuthenticationResult(true /* addAuthTokenIfStrong */);
                         callback.handleLifecycleAfterAuth();
                     }
@@ -257,6 +288,9 @@
         }
     }
 
+    /**
+     * Notify the coordinator that a rejection has occurred.
+     */
     public void onAuthenticationRejected(long currentTimeMillis,
             @NonNull AuthenticationClient<?> client,
             @LockoutTracker.LockoutMode int lockoutMode,
@@ -278,12 +312,23 @@
                         // BiometricScheduler do not get stuck.
                         Slog.d(TAG, "Face rejected in multi-sensor auth, udfps: " + udfps);
                         callback.handleLifecycleAfterAuth();
-                    } else {
-                        // UDFPS is not actively authenticating (finger not touching, already
-                        // rejected, etc).
+                    } else if (isUdfpsAuthAttempted(udfps)) {
+                        // If UDFPS is STATE_STARTED_PAUSED (e.g. finger rejected but can still
+                        // auth after pointer goes down, it means UDFPS encountered a rejection. In
+                        // this case, we need to play the final reject haptic since face auth is
+                        // also done now.
                         callback.sendHapticFeedback();
                         callback.handleLifecycleAfterAuth();
                     }
+                    else {
+                        // UDFPS auth has never been attempted.
+                        if (mFaceHapticDisabledWhenNonBypass && !face.isKeyguardBypassEnabled()) {
+                            Slog.w(TAG, "Skipping face reject haptic");
+                        } else {
+                            callback.sendHapticFeedback();
+                        }
+                        callback.handleLifecycleAfterAuth();
+                    }
                 } else if (isCurrentUdfps(client)) {
                     // Face should either be running, or have already finished
                     SuccessfulAuth auth = popSuccessfulFaceAuthIfExists(currentTimeMillis);
@@ -330,11 +375,63 @@
         }
     }
 
+    /**
+     * Notify the coordinator that an error has occurred.
+     */
+    public void onAuthenticationError(@NonNull AuthenticationClient<?> client,
+            @BiometricConstants.Errors int error, @NonNull ErrorCallback callback) {
+        // Figure out non-coex state
+        final boolean shouldUsuallyVibrate;
+        if (isCurrentFaceAuth(client)) {
+            final boolean notDetectedOnKeyguard = client.isKeyguard() && !client.wasUserDetected();
+            final boolean authAttempted = client.wasAuthAttempted();
+
+            switch (error) {
+                case BiometricConstants.BIOMETRIC_ERROR_TIMEOUT:
+                case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT:
+                case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT:
+                    shouldUsuallyVibrate = authAttempted && !notDetectedOnKeyguard;
+                    break;
+                default:
+                    shouldUsuallyVibrate = false;
+                    break;
+            }
+        } else {
+            shouldUsuallyVibrate = false;
+        }
+
+        // Figure out coex state
+        final boolean keyguardAdvancedLogic = mAdvancedLogicEnabled && client.isKeyguard();
+        final boolean hapticSuppressedByCoex;
+
+        if (keyguardAdvancedLogic) {
+            if (isSingleAuthOnly(client)) {
+                hapticSuppressedByCoex = false;
+            } else {
+                hapticSuppressedByCoex = isCurrentFaceAuth(client)
+                        && !client.isKeyguardBypassEnabled();
+            }
+        } else {
+            hapticSuppressedByCoex = false;
+        }
+
+        // Combine and send feedback if appropriate
+        Slog.d(TAG, "shouldUsuallyVibrate: " + shouldUsuallyVibrate
+                + ", hapticSuppressedByCoex: " + hapticSuppressedByCoex);
+        if (shouldUsuallyVibrate && !hapticSuppressedByCoex) {
+            callback.sendHapticFeedback();
+        }
+    }
+
     @Nullable
     private SuccessfulAuth popSuccessfulFaceAuthIfExists(long currentTimeMillis) {
         for (SuccessfulAuth auth : mSuccessfulAuths) {
             if (currentTimeMillis - auth.mAuthTimestamp >= SUCCESSFUL_AUTH_VALID_DURATION_MS) {
-                Slog.d(TAG, "Removing stale auth: " + auth);
+                // TODO(b/193089985): This removes the auth but does not notify the client with
+                //  an appropriate lifecycle event (such as ERROR_CANCELED), and violates the
+                //  API contract. However, this might be OK for now since the validity duration
+                //  is way longer than the time it takes to auth with fingerprint.
+                Slog.e(TAG, "Removing stale auth: " + auth);
                 mSuccessfulAuths.remove(auth);
             } else if (auth.mSensorType == SENSOR_TYPE_FACE) {
                 mSuccessfulAuths.remove(auth);
@@ -345,9 +442,13 @@
     }
 
     private void removeAndFinishAllFaceFromQueue() {
+        // Note that these auth are all successful, but have never notified the client (e.g.
+        // keyguard). To comply with the authentication lifecycle, we must notify the client that
+        // auth is "done". The safest thing to do is to send ERROR_CANCELED.
         for (SuccessfulAuth auth : mSuccessfulAuths) {
             if (auth.mSensorType == SENSOR_TYPE_FACE) {
-                Slog.d(TAG, "Removing from queue and finishing: " + auth);
+                Slog.d(TAG, "Removing from queue, canceling, and finishing: " + auth);
+                auth.mCallback.sendAuthenticationCanceled();
                 auth.mCallback.handleLifecycleAfterAuth();
                 mSuccessfulAuths.remove(auth);
             }
@@ -374,6 +475,13 @@
         return false;
     }
 
+    private static boolean isUdfpsAuthAttempted(@Nullable AuthenticationClient<?> client) {
+        if (client instanceof Udfps) {
+            return client.getState() == AuthenticationClient.STATE_STARTED_PAUSED_ATTEMPTED;
+        }
+        return false;
+    }
+
     private boolean isUnknownClient(@NonNull AuthenticationClient<?> client) {
         for (AuthenticationClient<?> c : mClientMap.values()) {
             if (c == client) {
@@ -400,6 +508,7 @@
     public String toString() {
         StringBuilder sb = new StringBuilder();
         sb.append("Enabled: ").append(mAdvancedLogicEnabled);
+        sb.append(", Face Haptic Disabled: ").append(mFaceHapticDisabledWhenNonBypass);
         sb.append(", Queue size: " ).append(mSuccessfulAuths.size());
         for (SuccessfulAuth auth : mSuccessfulAuths) {
             sb.append(", Auth: ").append(auth.toString());
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index f7fd8d0..d66a279 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -127,7 +127,8 @@
         }
     }
 
-    private boolean wasUserDetected() {
+    @Override
+    public boolean wasUserDetected() {
         // Do not provide haptic feedback if the user was not detected, and an error (usually
         // ERROR_TIMEOUT) is received.
         return mLastAcquire != FaceManager.FACE_ACQUIRED_NOT_DETECTED
@@ -160,7 +161,7 @@
     }
 
     @Override
-    public void onError(int error, int vendorCode) {
+    public void onError(@BiometricConstants.Errors int error, int vendorCode) {
         mUsageStats.addEvent(new UsageStats.AuthenticationEvent(
                 getStartTimeMs(),
                 System.currentTimeMillis() - getStartTimeMs() /* latency */,
@@ -169,25 +170,8 @@
                 vendorCode,
                 getTargetUserId()));
 
-        switch (error) {
-            case BiometricConstants.BIOMETRIC_ERROR_TIMEOUT:
-                if (!wasUserDetected() && !isBiometricPrompt()) {
-                    // No vibration if user was not detected on keyguard
-                    break;
-                }
-            case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT:
-            case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT:
-                if (mAuthAttempted) {
-                    // Only vibrate if auth was attempted. If the user was already locked out prior
-                    // to starting authentication, do not vibrate.
-                    vibrateError();
-                }
-                break;
-            case BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL:
-                BiometricNotificationUtils.showReEnrollmentNotification(getContext());
-                break;
-            default:
-                break;
+        if (error == BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL) {
+            BiometricNotificationUtils.showReEnrollmentNotification(getContext());
         }
 
         super.onError(error, vendorCode);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
index c33b957..33950af 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
@@ -115,7 +115,8 @@
         }
     }
 
-    private boolean wasUserDetected() {
+    @Override
+    public boolean wasUserDetected() {
         // Do not provide haptic feedback if the user was not detected, and an error (usually
         // ERROR_TIMEOUT) is received.
         return mLastAcquire != FaceManager.FACE_ACQUIRED_NOT_DETECTED
@@ -147,7 +148,7 @@
     }
 
     @Override
-    public void onError(int error, int vendorCode) {
+    public void onError(@BiometricConstants.Errors int error, int vendorCode) {
         mUsageStats.addEvent(new UsageStats.AuthenticationEvent(
                 getStartTimeMs(),
                 System.currentTimeMillis() - getStartTimeMs() /* latency */,
@@ -156,24 +157,6 @@
                 vendorCode,
                 getTargetUserId()));
 
-        switch (error) {
-            case BiometricConstants.BIOMETRIC_ERROR_TIMEOUT:
-                if (!wasUserDetected() && !isBiometricPrompt()) {
-                    // No vibration if user was not detected on keyguard
-                    break;
-                }
-            case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT:
-            case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT:
-                if (mAuthAttempted) {
-                    // Only vibrate if auth was attempted. If the user was already locked out prior
-                    // to starting authentication, do not vibrate.
-                    vibrateError();
-                }
-                break;
-            default:
-                break;
-        }
-
         super.onError(error, vendorCode);
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 99e6e62..37ee76a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -106,6 +106,12 @@
     }
 
     @Override
+    public boolean wasUserDetected() {
+        // TODO: Update if it needs to be used for fingerprint, i.e. success/reject, error_timeout
+        return false;
+    }
+
+    @Override
     public void onAuthenticated(BiometricAuthenticator.Identifier identifier,
             boolean authenticated, ArrayList<Byte> token) {
         super.onAuthenticated(identifier, authenticated, token);
@@ -114,7 +120,7 @@
             mState = STATE_STOPPED;
             UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
         } else {
-            mState = STATE_STARTED_PAUSED;
+            mState = STATE_STARTED_PAUSED_ATTEMPTED;
         }
     }
 
@@ -188,7 +194,7 @@
     public void onPointerUp() {
         try {
             mIsPointerDown = false;
-            mState = STATE_STARTED_PAUSED;
+            mState = STATE_STARTED_PAUSED_ATTEMPTED;
             mALSProbeCallback.getProbe().disable();
             getFreshDaemon().onPointerUp(0 /* pointerId */);
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
index 7558d15..5060744 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
@@ -112,7 +112,7 @@
             resetFailedAttempts(getTargetUserId());
             UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
         } else {
-            mState = STATE_STARTED_PAUSED;
+            mState = STATE_STARTED_PAUSED_ATTEMPTED;
             final @LockoutTracker.LockoutMode int lockoutMode =
                     mLockoutFrameworkImpl.getLockoutModeForUser(getTargetUserId());
             if (lockoutMode != LockoutTracker.LOCKOUT_NONE) {
@@ -153,6 +153,12 @@
     }
 
     @Override
+    public boolean wasUserDetected() {
+        // TODO: Update if it needs to be used for fingerprint, i.e. success/reject, error_timeout
+        return false;
+    }
+
+    @Override
     public @LockoutTracker.LockoutMode int handleFailedAttempt(int userId) {
         mLockoutFrameworkImpl.addFailedAttemptForUser(userId);
         return super.handleFailedAttempt(userId);
@@ -206,7 +212,7 @@
     @Override
     public void onPointerUp() {
         mIsPointerDown = false;
-        mState = STATE_STARTED_PAUSED;
+        mState = STATE_STARTED_PAUSED_ATTEMPTED;
         mALSProbeCallback.getProbe().disable();
         UdfpsHelper.onFingerUp(getFreshDaemon());
 
diff --git a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
index 54a4ad4..23f0ffb 100644
--- a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
@@ -52,11 +52,11 @@
     public BroadcastRadioService(Context context) {
         super(context);
 
-        mHal1 = new com.android.server.broadcastradio.hal1.BroadcastRadioService();
+        mHal1 = new com.android.server.broadcastradio.hal1.BroadcastRadioService(mLock);
         mV1Modules = mHal1.loadModules();
         OptionalInt max = mV1Modules.stream().mapToInt(RadioManager.ModuleProperties::getId).max();
         mHal2 = new com.android.server.broadcastradio.hal2.BroadcastRadioService(
-                max.isPresent() ? max.getAsInt() + 1 : 0);
+                max.isPresent() ? max.getAsInt() + 1 : 0, mLock);
     }
 
     @Override
@@ -111,7 +111,7 @@
             synchronized (mLock) {
                 if (!mHal2.hasAnyModules()) {
                     Slog.i(TAG, "There are no HAL 2.x modules registered");
-                    return new AnnouncementAggregator(listener);
+                    return new AnnouncementAggregator(listener, mLock);
                 }
 
                 return mHal2.addAnnouncementListener(enabledTypes, listener);
diff --git a/services/core/java/com/android/server/broadcastradio/OWNERS b/services/core/java/com/android/server/broadcastradio/OWNERS
index ea4421e..3e360e7 100644
--- a/services/core/java/com/android/server/broadcastradio/OWNERS
+++ b/services/core/java/com/android/server/broadcastradio/OWNERS
@@ -1,2 +1,3 @@
+keunyoung@google.com
+oscarazu@google.com
 twasilczyk@google.com
-randolphs@google.com
diff --git a/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java
index e8ac547..5da6032 100644
--- a/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java
@@ -17,16 +17,9 @@
 package com.android.server.broadcastradio.hal1;
 
 import android.annotation.NonNull;
-import android.Manifest;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.hardware.radio.IRadioService;
 import android.hardware.radio.ITuner;
 import android.hardware.radio.ITunerCallback;
 import android.hardware.radio.RadioManager;
-import android.os.ParcelableException;
-
-import com.android.server.SystemService;
 
 import java.util.List;
 import java.util.Objects;
@@ -37,7 +30,7 @@
      */
     private final long mNativeContext = nativeInit();
 
-    private final Object mLock = new Object();
+    private final Object mLock;
 
     @Override
     protected void finalize() throws Throwable {
@@ -51,6 +44,14 @@
     private native Tuner nativeOpenTuner(long nativeContext, int moduleId,
             RadioManager.BandConfig config, boolean withAudio, ITunerCallback callback);
 
+    /**
+     * Constructor. should pass
+     * {@code com.android.server.broadcastradio.BroadcastRadioService#mLock} for lock.
+     */
+    public BroadcastRadioService(@NonNull Object lock) {
+        mLock = lock;
+    }
+
     public @NonNull List<RadioManager.ModuleProperties> loadModules() {
         synchronized (mLock) {
             return Objects.requireNonNull(nativeLoadModules(mNativeContext));
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/AnnouncementAggregator.java b/services/core/java/com/android/server/broadcastradio/hal2/AnnouncementAggregator.java
index 5307697..42e296f 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/AnnouncementAggregator.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/AnnouncementAggregator.java
@@ -35,7 +35,7 @@
 public class AnnouncementAggregator extends ICloseHandle.Stub {
     private static final String TAG = "BcRadio2Srv.AnnAggr";
 
-    private final Object mLock = new Object();
+    private final Object mLock;
     @NonNull private final IAnnouncementListener mListener;
     private final IBinder.DeathRecipient mDeathRecipient = new DeathRecipient();
 
@@ -45,8 +45,9 @@
     @GuardedBy("mLock")
     private boolean mIsClosed = false;
 
-    public AnnouncementAggregator(@NonNull IAnnouncementListener listener) {
+    public AnnouncementAggregator(@NonNull IAnnouncementListener listener, @NonNull Object lock) {
         mListener = Objects.requireNonNull(listener);
+        mLock = Objects.requireNonNull(lock);
         try {
             listener.asBinder().linkToDeath(mDeathRecipient, 0);
         } catch (RemoteException ex) {
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
index 5e79c59..5c07f76 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
@@ -42,7 +42,7 @@
 public class BroadcastRadioService {
     private static final String TAG = "BcRadio2Srv";
 
-    private final Object mLock = new Object();
+    private final Object mLock;
 
     @GuardedBy("mLock")
     private int mNextModuleId = 0;
@@ -68,7 +68,7 @@
                     moduleId = mNextModuleId;
                 }
 
-                RadioModule module = RadioModule.tryLoadingModule(moduleId, serviceName);
+                RadioModule module = RadioModule.tryLoadingModule(moduleId, serviceName, mLock);
                 if (module == null) {
                     return;
                 }
@@ -116,8 +116,9 @@
         }
     };
 
-    public BroadcastRadioService(int nextModuleId) {
+    public BroadcastRadioService(int nextModuleId, Object lock) {
         mNextModuleId = nextModuleId;
+        mLock = lock;
         try {
             IServiceManager manager = IServiceManager.getService();
             if (manager == null) {
@@ -174,7 +175,7 @@
 
     public ICloseHandle addAnnouncementListener(@NonNull int[] enabledTypes,
             @NonNull IAnnouncementListener listener) {
-        AnnouncementAggregator aggregator = new AnnouncementAggregator(listener);
+        AnnouncementAggregator aggregator = new AnnouncementAggregator(listener, mLock);
         boolean anySupported = false;
         synchronized (mLock) {
             for (RadioModule module : mModules.values()) {
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
index b7e188c..ef7f4c9 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -58,7 +58,7 @@
     @NonNull private final IBroadcastRadio mService;
     @NonNull public final RadioManager.ModuleProperties mProperties;
 
-    private final Object mLock = new Object();
+    private final Object mLock;
     @NonNull private final Handler mHandler;
 
     @GuardedBy("mLock")
@@ -132,13 +132,15 @@
 
     @VisibleForTesting
     RadioModule(@NonNull IBroadcastRadio service,
-            @NonNull RadioManager.ModuleProperties properties) {
+            @NonNull RadioManager.ModuleProperties properties, @NonNull Object lock) {
         mProperties = Objects.requireNonNull(properties);
         mService = Objects.requireNonNull(service);
+        mLock = Objects.requireNonNull(lock);
         mHandler = new Handler(Looper.getMainLooper());
     }
 
-    public static @Nullable RadioModule tryLoadingModule(int idx, @NonNull String fqName) {
+    public static @Nullable RadioModule tryLoadingModule(int idx, @NonNull String fqName,
+            Object lock) {
         try {
             IBroadcastRadio service = IBroadcastRadio.getService(fqName);
             if (service == null) return null;
@@ -156,7 +158,7 @@
             RadioManager.ModuleProperties prop = Convert.propertiesFromHal(idx, fqName,
                     service.getProperties(), amfmConfig.value, dabConfig.value);
 
-            return new RadioModule(service, prop);
+            return new RadioModule(service, prop, lock);
         } catch (RemoteException ex) {
             Slog.e(TAG, "failed to load module " + fqName, ex);
             return null;
@@ -178,7 +180,8 @@
                 });
                 mHalTunerSession = Objects.requireNonNull(hwSession.value);
             }
-            TunerSession tunerSession = new TunerSession(this, mHalTunerSession, userCb);
+            TunerSession tunerSession = new TunerSession(this, mHalTunerSession, userCb,
+                    mLock);
             mAidlTunerSessions.add(tunerSession);
 
             // Propagate state to new client. Note: These callbacks are invoked while holding mLock
@@ -377,7 +380,7 @@
             }
         };
 
-        synchronized (mService) {
+        synchronized (mLock) {
             mService.registerAnnouncementListener(enabledList, hwListener, (result, closeHnd) -> {
                 halResult.value = result;
                 hwCloseHandle.value = closeHnd;
@@ -401,7 +404,7 @@
         if (id == 0) throw new IllegalArgumentException("Image ID is missing");
 
         byte[] rawImage;
-        synchronized (mService) {
+        synchronized (mLock) {
             List<Byte> rawList = Utils.maybeRethrow(() -> mService.getImage(id));
             rawImage = new byte[rawList.size()];
             for (int i = 0; i < rawList.size(); i++) {
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
index 7ab3bdd..200af2f 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
@@ -40,7 +40,7 @@
     private static final String TAG = "BcRadio2Srv.session";
     private static final String kAudioDeviceName = "Radio tuner source";
 
-    private final Object mLock = new Object();
+    private final Object mLock;
 
     private final RadioModule mModule;
     private final ITunerSession mHwSession;
@@ -53,10 +53,12 @@
     private RadioManager.BandConfig mDummyConfig = null;
 
     TunerSession(@NonNull RadioModule module, @NonNull ITunerSession hwSession,
-            @NonNull android.hardware.radio.ITunerCallback callback) {
+            @NonNull android.hardware.radio.ITunerCallback callback,
+            @NonNull Object lock) {
         mModule = Objects.requireNonNull(module);
         mHwSession = Objects.requireNonNull(hwSession);
         mCallback = Objects.requireNonNull(callback);
+        mLock = Objects.requireNonNull(lock);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index ed7d185..6610e8c 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -20,9 +20,14 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
+import android.app.compat.CompatChanges;
 import android.app.TaskStackListener;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
+import android.compat.annotation.Overridable;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -49,6 +54,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.stats.camera.nano.CameraProtos.CameraStreamProto;
 import android.util.ArrayMap;
@@ -97,6 +103,95 @@
 
     public static final String CAMERA_SERVICE_PROXY_BINDER_NAME = "media.camera.proxy";
 
+    /**
+     * When enabled this change id forces the packages it is applied to override the default
+     * camera rotate & crop behavior. The default behavior along with all possible override
+     * combinations is discussed in the table below.
+     */
+    @ChangeId
+    @Overridable
+    @Disabled
+    @TestApi
+    public static final long OVERRIDE_CAMERA_ROTATE_AND_CROP_DEFAULTS = 189229956L; // buganizer id
+
+    /**
+     * When enabled this change id forces the packages it is applied to ignore the current value of
+     * 'android:resizeableActivity' as well as target SDK equal to or below M and consider the
+     * activity as non-resizeable. In this case, the value of camera rotate & crop will only depend
+     * on potential mismatches between the orientation of the camera and the fixed orientation of
+     * the activity. You can check the table below for further details on the possible override
+     * combinations.
+     */
+    @ChangeId
+    @Overridable
+    @Disabled
+    @TestApi
+    public static final long OVERRIDE_CAMERA_RESIZABLE_AND_SDK_CHECK = 191513214L; // buganizer id
+
+    /**
+     * This change id forces the packages it is applied to override the default camera rotate & crop
+     * behavior. Enabling it will set the crop & rotate parameter to
+     * {@link android.hardware.camera2.CaptureRequest#SCALER_ROTATE_AND_CROP_90} and disabling it
+     * will reset the parameter to
+     * {@link android.hardware.camera2.CaptureRequest#SCALER_ROTATE_AND_CROP_NONE} as long as camera
+     * clients include {@link android.hardware.camera2.CaptureRequest#SCALER_ROTATE_AND_CROP_AUTO}
+     * in their capture requests.
+     *
+     * This treatment only takes effect if OVERRIDE_CAMERA_ROTATE_AND_CROP_DEFAULTS is also enabled.
+     * The table below includes further information about the possible override combinations.
+     */
+    @ChangeId
+    @Overridable
+    @Disabled
+    @TestApi
+    public static final long OVERRIDE_CAMERA_ROTATE_AND_CROP = 190069291L; //buganizer id
+
+    /**
+     * Possible override combinations
+     *
+     *            |OVERRIDE     |          |OVERRIDE_
+     *            |CAMERA_      |OVERRIDE  |CAMERA_
+     *            |ROTATE_      |CAMERA_   |RESIZEABLE_
+     *            |AND_CROP_    |ROTATE_   |AND_SDK_
+     *            |DEFAULTS     |AND_CROP  |CHECK
+     * ______________________________________________
+     * Default    |             |          |
+     * Behavior   | D           |D         |D
+     * ______________________________________________
+     * Ignore     |             |          |
+     * SDK&Resize | D           |D         |E
+     * ______________________________________________
+     * Default    |             |          |
+     * Behavior   | D           |E         |D
+     * ______________________________________________
+     * Ignore     |             |          |
+     * SDK&Resize | D           |E         |E
+     * ______________________________________________
+     * Rotate&Crop|             |          |
+     * disabled   | E           |D         |D
+     * ______________________________________________
+     * Rotate&Crop|             |          |
+     * disabled   | E           |D         |E
+     * ______________________________________________
+     * Rotate&Crop|             |          |
+     * enabled    | E           |E         |D
+     * ______________________________________________
+     * Rotate&Crop|             |          |
+     * enabled    | E           |E         |E
+     * ______________________________________________
+     * Where:
+     * E -> Override enabled
+     * D -> Override disabled
+     * Default behavior ->  Rotate&crop will be enabled only in cases
+     *                      where the fixed app orientation mismatches
+     *                      with the orientation of the camera.
+     *                      Additionally the app must either target M (or below)
+     *                      or is declared as non-resizeable.
+     * Ignore SDK&Resize -> Rotate&crop will be enabled only in cases
+     *                      where the fixed app orientation mismatches
+     *                      with the orientation of the camera.
+     */
+
     // Flags arguments to NFC adapter to enable/disable NFC
     public static final int DISABLE_POLLING_FLAGS = 0x1000;
     public static final int ENABLE_POLLING_FLAGS = 0x0000;
@@ -257,6 +352,7 @@
         private boolean isFixedOrientationLandscape;
         private boolean isFixedOrientationPortrait;
         private int displayId;
+        private int userId;
     }
 
     private final class TaskStateHandler extends TaskStackListener {
@@ -273,6 +369,7 @@
                 info.frontTaskId = taskInfo.taskId;
                 info.isResizeable = taskInfo.isResizeable;
                 info.displayId = taskInfo.displayId;
+                info.userId = taskInfo.userId;
                 info.isFixedOrientationLandscape = ActivityInfo.isFixedOrientationLandscape(
                         taskInfo.topActivityInfo.screenOrientation);
                 info.isFixedOrientationPortrait = ActivityInfo.isFixedOrientationPortrait(
@@ -303,7 +400,7 @@
             Log.e(TAG, "Top task with package name: " + packageName + " not found!");
             return null;
         }
-    };
+    }
 
     private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
@@ -345,7 +442,8 @@
          * Gets whether crop-rotate-scale is needed.
          */
         private boolean getNeedCropRotateScale(@NonNull Context ctx, @NonNull String packageName,
-                @Nullable TaskInfo taskInfo, int sensorOrientation, int lensFacing) {
+                @Nullable TaskInfo taskInfo, int sensorOrientation, int lensFacing,
+                boolean ignoreResizableAndSdkCheck) {
             if (taskInfo == null) {
                 return false;
             }
@@ -357,9 +455,11 @@
                 return false;
             }
 
-            // Only enable the crop-rotate-scale workaround if the app targets M or below and is not
+            // In case the activity behavior is not explicitly overridden, enable the
+            // crop-rotate-scale workaround if the app targets M (or below) or is not
             // resizeable.
-            if (!isMOrBelow(ctx, packageName) && taskInfo.isResizeable) {
+            if (!ignoreResizableAndSdkCheck && !isMOrBelow(ctx, packageName) &&
+                    taskInfo.isResizeable) {
                 Slog.v(TAG,
                         "The activity is N or above and claims to support resizeable-activity. "
                                 + "Crop-rotate-scale is disabled.");
@@ -367,22 +467,32 @@
             }
 
             DisplayManager displayManager = ctx.getSystemService(DisplayManager.class);
-            Display display = displayManager.getDisplay(taskInfo.displayId);
-            int rotation = display.getRotation();
             int rotationDegree = 0;
-            switch (rotation) {
-                case Surface.ROTATION_0:
-                    rotationDegree = 0;
-                    break;
-                case Surface.ROTATION_90:
-                    rotationDegree = 90;
-                    break;
-                case Surface.ROTATION_180:
-                    rotationDegree = 180;
-                    break;
-                case Surface.ROTATION_270:
-                    rotationDegree = 270;
-                    break;
+            if (displayManager != null) {
+                Display display = displayManager.getDisplay(taskInfo.displayId);
+                if (display == null) {
+                    Slog.e(TAG, "Invalid display id: " + taskInfo.displayId);
+                    return false;
+                }
+
+                int rotation = display.getRotation();
+                switch (rotation) {
+                    case Surface.ROTATION_0:
+                        rotationDegree = 0;
+                        break;
+                    case Surface.ROTATION_90:
+                        rotationDegree = 90;
+                        break;
+                    case Surface.ROTATION_180:
+                        rotationDegree = 180;
+                        break;
+                    case Surface.ROTATION_270:
+                        rotationDegree = 270;
+                        break;
+                }
+            } else {
+                Slog.e(TAG, "Failed to query display manager!");
+                return false;
             }
 
             // Here we only need to know whether the camera is landscape or portrait. Therefore we
@@ -414,9 +524,28 @@
             //  regions in capture requests/results to account for thea physical rotation. The
             //  former is somewhat tricky as it assumes that camera clients always check for the
             //  current value by retrieving the camera characteristics from the camera device.
-            return getNeedCropRotateScale(mContext, packageName,
-                    mTaskStackListener.getFrontTaskInfo(packageName), sensorOrientation,
-                    lensFacing);
+            TaskInfo taskInfo = mTaskStackListener.getFrontTaskInfo(packageName);
+            if ((taskInfo != null) && (CompatChanges.isChangeEnabled(
+                        OVERRIDE_CAMERA_ROTATE_AND_CROP_DEFAULTS, packageName,
+                        UserHandle.getUserHandleForUid(taskInfo.userId)))) {
+                if (CompatChanges.isChangeEnabled(OVERRIDE_CAMERA_ROTATE_AND_CROP, packageName,
+                        UserHandle.getUserHandleForUid(taskInfo.userId))) {
+                    Slog.v(TAG, "OVERRIDE_CAMERA_ROTATE_AND_CROP enabled!");
+                    return true;
+                } else {
+                    Slog.v(TAG, "OVERRIDE_CAMERA_ROTATE_AND_CROP disabled!");
+                    return false;
+                }
+            }
+            boolean ignoreResizableAndSdkCheck = false;
+            if ((taskInfo != null) && (CompatChanges.isChangeEnabled(
+                    OVERRIDE_CAMERA_RESIZABLE_AND_SDK_CHECK, packageName,
+                    UserHandle.getUserHandleForUid(taskInfo.userId)))) {
+                Slog.v(TAG, "OVERRIDE_CAMERA_RESIZABLE_AND_SDK_CHECK enabled!");
+                ignoreResizableAndSdkCheck = true;
+            }
+            return getNeedCropRotateScale(mContext, packageName, taskInfo, sensorOrientation,
+                    lensFacing, ignoreResizableAndSdkCheck);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/devicestate/DeviceState.java b/services/core/java/com/android/server/devicestate/DeviceState.java
index e693bcc..7fe24ff 100644
--- a/services/core/java/com/android/server/devicestate/DeviceState.java
+++ b/services/core/java/com/android/server/devicestate/DeviceState.java
@@ -19,11 +19,14 @@
 import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
 import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
 
+import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 
 import com.android.internal.util.Preconditions;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
 
 /**
@@ -39,6 +42,19 @@
  * @see DeviceStateManagerService
  */
 public final class DeviceState {
+    /**
+     * Flag that indicates sticky requests should be cancelled when this device state becomes the
+     * base device state.
+     */
+    public static final int FLAG_CANCEL_STICKY_REQUESTS = 1 << 0;
+
+    /** @hide */
+    @IntDef(prefix = {"FLAG_"}, flag = true, value = {
+            FLAG_CANCEL_STICKY_REQUESTS,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DeviceStateFlags {}
+
     /** Unique identifier for the device state. */
     @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE)
     private final int mIdentifier;
@@ -47,14 +63,19 @@
     @NonNull
     private final String mName;
 
+    @DeviceStateFlags
+    private final int mFlags;
+
     public DeviceState(
             @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier,
-            @NonNull String name) {
+            @NonNull String name,
+            @DeviceStateFlags int flags) {
         Preconditions.checkArgumentInRange(identifier, MINIMUM_DEVICE_STATE, MAXIMUM_DEVICE_STATE,
                 "identifier");
 
         mIdentifier = identifier;
         mName = name;
+        mFlags = flags;
     }
 
     /** Returns the unique identifier for the device state. */
@@ -69,6 +90,11 @@
         return mName;
     }
 
+    @DeviceStateFlags
+    public int getFlags() {
+        return mFlags;
+    }
+
     @Override
     public String toString() {
         return "DeviceState{" + "identifier=" + mIdentifier + ", name='" + mName + '\'' + '}';
@@ -80,11 +106,12 @@
         if (o == null || getClass() != o.getClass()) return false;
         DeviceState that = (DeviceState) o;
         return mIdentifier == that.mIdentifier
-                && Objects.equals(mName, that.mName);
+                && Objects.equals(mName, that.mName)
+                && mFlags == that.mFlags;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mIdentifier, mName);
+        return Objects.hash(mIdentifier, mName, mFlags);
     }
 }
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index a8b0994..383b392 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -19,8 +19,13 @@
 import static android.Manifest.permission.CONTROL_DEVICE_STATE;
 import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
 import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES;
+import static android.os.Process.THREAD_PRIORITY_DISPLAY;
 
+import static com.android.server.devicestate.OverrideRequestController.STATUS_ACTIVE;
+import static com.android.server.devicestate.OverrideRequestController.STATUS_CANCELED;
+import static com.android.server.devicestate.OverrideRequestController.STATUS_SUSPENDED;
+
+import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -30,12 +35,12 @@
 import android.hardware.devicestate.IDeviceStateManager;
 import android.hardware.devicestate.IDeviceStateManagerCallback;
 import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
-import android.util.ArrayMap;
-import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -43,14 +48,21 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.LocalServices;
+import com.android.server.ServiceThread;
 import com.android.server.SystemService;
 import com.android.server.policy.DeviceStatePolicyImpl;
+import com.android.server.wm.ActivityTaskManagerInternal;
+import com.android.server.wm.WindowProcessController;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Optional;
+import java.util.WeakHashMap;
 
 /**
  * A system service that manages the state of a device with user-configurable hardware like a
@@ -81,10 +93,20 @@
     private static final boolean DEBUG = false;
 
     private final Object mLock = new Object();
+    // Internal system service thread used to dispatch calls to the policy and to registered
+    // callbacks though its handler (mHandler). Provides a guarantee of callback order when
+    // leveraging mHandler and also enables posting messages with the service lock held.
+    private final HandlerThread mHandlerThread;
+    private final Handler mHandler;
     @NonNull
     private final DeviceStatePolicy mDeviceStatePolicy;
     @NonNull
     private final BinderService mBinderService;
+    @NonNull
+    private final OverrideRequestController mOverrideRequestController;
+    @VisibleForTesting
+    @NonNull
+    public ActivityTaskManagerInternal mActivityTaskManagerInternal;
 
     // All supported device states keyed by identifier.
     @GuardedBy("mLock")
@@ -109,17 +131,16 @@
     @NonNull
     private Optional<DeviceState> mBaseState = Optional.empty();
 
+    // The current active override request. When set the device state specified here will take
+    // precedence over mBaseState.
+    @GuardedBy("mLock")
+    @NonNull
+    private Optional<OverrideRequest> mActiveOverride = Optional.empty();
+
     // List of processes registered to receive notifications about changes to device state and
     // request status indexed by process id.
     @GuardedBy("mLock")
     private final SparseArray<ProcessRecord> mProcessRecords = new SparseArray<>();
-    // List of override requests with the highest precedence request at the end.
-    @GuardedBy("mLock")
-    private final ArrayList<OverrideRequestRecord> mRequestRecords = new ArrayList<>();
-    // Set of override requests that are pending a call to notifyStatusIfNeeded() to be notified
-    // of a change in status.
-    @GuardedBy("mLock")
-    private final ArraySet<OverrideRequestRecord> mRequestsPendingStatusChange = new ArraySet<>();
 
     public DeviceStateManagerService(@NonNull Context context) {
         this(context, new DeviceStatePolicyImpl(context));
@@ -128,9 +149,17 @@
     @VisibleForTesting
     DeviceStateManagerService(@NonNull Context context, @NonNull DeviceStatePolicy policy) {
         super(context);
+        // Service thread assigned THREAD_PRIORITY_DISPLAY because this service indirectly drives
+        // display (on/off) and window (position) events through its callbacks.
+        mHandlerThread = new ServiceThread(TAG, THREAD_PRIORITY_DISPLAY, false /* allowIo */);
+        mHandlerThread.start();
+        mHandler = mHandlerThread.getThreadHandler();
+        mOverrideRequestController = new OverrideRequestController(
+                this::onOverrideRequestStatusChangedLocked);
         mDeviceStatePolicy = policy;
         mDeviceStatePolicy.getDeviceStateProvider().setListener(new DeviceStateProviderListener());
         mBinderService = new BinderService();
+        mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
     }
 
     @Override
@@ -138,6 +167,11 @@
         publishBinderService(Context.DEVICE_STATE_SERVICE, mBinderService);
     }
 
+    @VisibleForTesting
+    Handler getHandler() {
+        return mHandler;
+    }
+
     /**
      * Returns the current state the system is in. Note that the system may be in the process of
      * configuring a different state.
@@ -191,12 +225,10 @@
     @NonNull
     Optional<DeviceState> getOverrideState() {
         synchronized (mLock) {
-            if (mRequestRecords.isEmpty()) {
-                return Optional.empty();
+            if (mActiveOverride.isPresent()) {
+                return getStateLocked(mActiveOverride.get().getRequestedState());
             }
-
-            OverrideRequestRecord topRequest = mRequestRecords.get(mRequestRecords.size() - 1);
-            return Optional.of(topRequest.mRequestedState);
+            return Optional.empty();
         }
     }
 
@@ -247,43 +279,41 @@
     }
 
     private void updateSupportedStates(DeviceState[] supportedDeviceStates) {
-        boolean updatedPendingState;
-        boolean hasBaseState;
         synchronized (mLock) {
             final int[] oldStateIdentifiers = getSupportedStateIdentifiersLocked();
 
+            // Whether or not at least one device state has the flag FLAG_CANCEL_STICKY_REQUESTS
+            // set. If set to true, the OverrideRequestController will be configured to allow sticky
+            // requests.
+            boolean hasTerminalDeviceState = false;
             mDeviceStates.clear();
             for (int i = 0; i < supportedDeviceStates.length; i++) {
                 DeviceState state = supportedDeviceStates[i];
+                if ((state.getFlags() & DeviceState.FLAG_CANCEL_STICKY_REQUESTS) != 0) {
+                    hasTerminalDeviceState = true;
+                }
                 mDeviceStates.put(state.getIdentifier(), state);
             }
 
+            mOverrideRequestController.setStickyRequestsAllowed(hasTerminalDeviceState);
+
             final int[] newStateIdentifiers = getSupportedStateIdentifiersLocked();
             if (Arrays.equals(oldStateIdentifiers, newStateIdentifiers)) {
                 return;
             }
 
-            final int requestSize = mRequestRecords.size();
-            for (int i = 0; i < requestSize; i++) {
-                OverrideRequestRecord request = mRequestRecords.get(i);
-                if (!isSupportedStateLocked(request.mRequestedState.getIdentifier())) {
-                    request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED);
-                }
+            mOverrideRequestController.handleNewSupportedStates(newStateIdentifiers);
+            updatePendingStateLocked();
+
+            if (!mPendingState.isPresent()) {
+                // If the change in the supported states didn't result in a change of the pending
+                // state commitPendingState() will never be called and the callbacks will never be
+                // notified of the change.
+                notifyDeviceStateInfoChangedAsync();
             }
 
-            updatedPendingState = updatePendingStateLocked();
-            hasBaseState = mBaseState.isPresent();
+            mHandler.post(this::notifyPolicyIfNeeded);
         }
-
-        if (hasBaseState && !updatedPendingState) {
-            // If the change in the supported states didn't result in a change of the pending state
-            // commitPendingState() will never be called and the callbacks will never be notified
-            // of the change.
-            notifyDeviceStateInfoChanged();
-        }
-
-        notifyRequestsOfStatusChangeIfNeeded();
-        notifyPolicyIfNeeded();
     }
 
     /**
@@ -311,7 +341,6 @@
      * @see #isSupportedStateLocked(int)
      */
     private void setBaseState(int identifier) {
-        boolean updatedPendingState;
         synchronized (mLock) {
             final Optional<DeviceState> baseStateOptional = getStateLocked(identifier);
             if (!baseStateOptional.isPresent()) {
@@ -325,26 +354,21 @@
             }
             mBaseState = Optional.of(baseState);
 
-            final int requestSize = mRequestRecords.size();
-            for (int i = 0; i < requestSize; i++) {
-                OverrideRequestRecord request = mRequestRecords.get(i);
-                if ((request.mFlags & FLAG_CANCEL_WHEN_BASE_CHANGES) > 0) {
-                    request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED);
-                }
+            if ((baseState.getFlags() & DeviceState.FLAG_CANCEL_STICKY_REQUESTS) != 0) {
+                mOverrideRequestController.cancelStickyRequests();
+            }
+            mOverrideRequestController.handleBaseStateChanged();
+            updatePendingStateLocked();
+
+            if (!mPendingState.isPresent()) {
+                // If the change in base state didn't result in a change of the pending state
+                // commitPendingState() will never be called and the callbacks will never be
+                // notified of the change.
+                notifyDeviceStateInfoChangedAsync();
             }
 
-            updatedPendingState = updatePendingStateLocked();
+            mHandler.post(this::notifyPolicyIfNeeded);
         }
-
-        if (!updatedPendingState) {
-            // If the change in base state didn't result in a change of the pending state
-            // commitPendingState() will never be called and the callbacks will never be notified
-            // of the change.
-            notifyDeviceStateInfoChanged();
-        }
-
-        notifyRequestsOfStatusChangeIfNeeded();
-        notifyPolicyIfNeeded();
     }
 
     /**
@@ -362,8 +386,8 @@
         }
 
         final DeviceState stateToConfigure;
-        if (!mRequestRecords.isEmpty()) {
-            stateToConfigure = mRequestRecords.get(mRequestRecords.size() - 1).mRequestedState;
+        if (mActiveOverride.isPresent()) {
+            stateToConfigure = getStateLocked(mActiveOverride.get().getRequestedState()).get();
         } else if (mBaseState.isPresent()
                 && isSupportedStateLocked(mBaseState.get().getIdentifier())) {
             // Base state could have recently become unsupported after a change in supported states.
@@ -429,108 +453,106 @@
      * </p>
      */
     private void commitPendingState() {
-        // Update the current state.
         synchronized (mLock) {
             final DeviceState newState = mPendingState.get();
             if (DEBUG) {
                 Slog.d(TAG, "Committing state: " + newState);
             }
 
-            if (!mRequestRecords.isEmpty()) {
-                final OverrideRequestRecord topRequest =
-                        mRequestRecords.get(mRequestRecords.size() - 1);
-                if (topRequest.mRequestedState.getIdentifier() == newState.getIdentifier()) {
-                    // The top request could have come in while the service was awaiting callback
-                    // from the policy. In that case we only set it to active if it matches the
-                    // current committed state, otherwise it will be set to active when its
-                    // requested state is committed.
-                    topRequest.setStatusLocked(OverrideRequestRecord.STATUS_ACTIVE);
-                }
-            }
-
             FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_STATE_CHANGED,
                     newState.getIdentifier(), !mCommittedState.isPresent());
 
             mCommittedState = Optional.of(newState);
             mPendingState = Optional.empty();
             updatePendingStateLocked();
+
+            // Notify callbacks of a change.
+            notifyDeviceStateInfoChangedAsync();
+
+            // The top request could have come in while the service was awaiting callback
+            // from the policy. In that case we only set it to active if it matches the
+            // current committed state, otherwise it will be set to active when its
+            // requested state is committed.
+            OverrideRequest activeRequest = mActiveOverride.orElse(null);
+            if (activeRequest != null
+                    && activeRequest.getRequestedState() == newState.getIdentifier()) {
+                ProcessRecord processRecord = mProcessRecords.get(activeRequest.getPid());
+                if (processRecord != null) {
+                    processRecord.notifyRequestActiveAsync(activeRequest.getToken());
+                }
+            }
+
+            // Try to configure the next state if needed.
+            mHandler.post(this::notifyPolicyIfNeeded);
         }
-
-        // Notify callbacks of a change.
-        notifyDeviceStateInfoChanged();
-
-        // Notify the top request that it's active.
-        notifyRequestsOfStatusChangeIfNeeded();
-
-        // Try to configure the next state if needed.
-        notifyPolicyIfNeeded();
     }
 
-    private void notifyDeviceStateInfoChanged() {
-        if (Thread.holdsLock(mLock)) {
-            throw new IllegalStateException(
-                    "Attempting to notify callbacks with service lock held.");
-        }
-
-        // Grab the lock and copy the process records and the current info.
-        ArrayList<ProcessRecord> registeredProcesses;
-        DeviceStateInfo info;
+    private void notifyDeviceStateInfoChangedAsync() {
         synchronized (mLock) {
             if (mProcessRecords.size() == 0) {
                 return;
             }
 
-            registeredProcesses = new ArrayList<>();
+            ArrayList<ProcessRecord> registeredProcesses = new ArrayList<>();
             for (int i = 0; i < mProcessRecords.size(); i++) {
                 registeredProcesses.add(mProcessRecords.valueAt(i));
             }
 
-            info = getDeviceStateInfoLocked();
-        }
+            DeviceStateInfo info = getDeviceStateInfoLocked();
 
-        // After releasing the lock, send the notifications out.
-        for (int i = 0; i < registeredProcesses.size(); i++) {
-            registeredProcesses.get(i).notifyDeviceStateInfoAsync(info);
+            for (int i = 0; i < registeredProcesses.size(); i++) {
+                registeredProcesses.get(i).notifyDeviceStateInfoAsync(info);
+            }
         }
     }
 
-    /**
-     * Notifies all dirty requests (requests that have a change in status, but have not yet been
-     * notified) that their status has changed.
-     */
-    private void notifyRequestsOfStatusChangeIfNeeded() {
-        if (Thread.holdsLock(mLock)) {
-            throw new IllegalStateException(
-                    "Attempting to notify requests with service lock held.");
-        }
-
-        ArraySet<OverrideRequestRecord> dirtyRequests;
-        synchronized (mLock) {
-            if (mRequestsPendingStatusChange.isEmpty()) {
-                return;
+    private void onOverrideRequestStatusChangedLocked(@NonNull OverrideRequest request,
+            @OverrideRequestController.RequestStatus int status) {
+        if (status == STATUS_ACTIVE) {
+            mActiveOverride = Optional.of(request);
+        } else if (status == STATUS_SUSPENDED || status == STATUS_CANCELED) {
+            if (mActiveOverride.isPresent() && mActiveOverride.get() == request) {
+                mActiveOverride = Optional.empty();
             }
-
-            dirtyRequests = new ArraySet<>(mRequestsPendingStatusChange);
-            mRequestsPendingStatusChange.clear();
+        } else {
+            throw new IllegalArgumentException("Unknown request status: " + status);
         }
 
-        // After releasing the lock, send the notifications out.
-        for (int i = 0; i < dirtyRequests.size(); i++) {
-            dirtyRequests.valueAt(i).notifyStatusIfNeeded();
+        boolean updatedPendingState = updatePendingStateLocked();
+
+        ProcessRecord processRecord = mProcessRecords.get(request.getPid());
+        if (processRecord == null) {
+            // If the process is no longer registered with the service, for example if it has died,
+            // there is no need to notify it of a change in request status.
+            mHandler.post(this::notifyPolicyIfNeeded);
+            return;
         }
+
+        if (status == STATUS_ACTIVE) {
+            if (!updatedPendingState && !mPendingState.isPresent()) {
+                // If the pending state was not updated and there is not currently a pending state
+                // then this newly active request will never be notified of a change in state.
+                // Schedule the notification now.
+                processRecord.notifyRequestActiveAsync(request.getToken());
+            }
+        } else if (status == STATUS_SUSPENDED) {
+            processRecord.notifyRequestSuspendedAsync(request.getToken());
+        } else {
+            processRecord.notifyRequestCanceledAsync(request.getToken());
+        }
+
+        mHandler.post(this::notifyPolicyIfNeeded);
     }
 
     private void registerProcess(int pid, IDeviceStateManagerCallback callback) {
-        DeviceStateInfo currentInfo;
-        ProcessRecord record;
-        // Grab the lock to register the callback and get the current state.
         synchronized (mLock) {
             if (mProcessRecords.contains(pid)) {
                 throw new SecurityException("The calling process has already registered an"
                         + " IDeviceStateManagerCallback.");
             }
 
-            record = new ProcessRecord(callback, pid);
+            ProcessRecord record = new ProcessRecord(callback, pid, this::handleProcessDied,
+                    mHandlerThread.getThreadHandler());
             try {
                 callback.asBinder().linkToDeath(record, 0);
             } catch (RemoteException ex) {
@@ -538,34 +560,21 @@
             }
             mProcessRecords.put(pid, record);
 
-            currentInfo = mCommittedState.isPresent() ? getDeviceStateInfoLocked() : null;
-        }
-
-        if (currentInfo != null) {
-            // If there is not a committed state we'll wait to notify the process of the initial
-            // value.
-            record.notifyDeviceStateInfoAsync(currentInfo);
+            DeviceStateInfo currentInfo = mCommittedState.isPresent()
+                    ? getDeviceStateInfoLocked() : null;
+            if (currentInfo != null) {
+                // If there is not a committed state we'll wait to notify the process of the initial
+                // value.
+                record.notifyDeviceStateInfoAsync(currentInfo);
+            }
         }
     }
 
     private void handleProcessDied(ProcessRecord processRecord) {
         synchronized (mLock) {
-            // Cancel all requests from this process.
-            final int requestCount = processRecord.mRequestRecords.size();
-            for (int i = 0; i < requestCount; i++) {
-                final OverrideRequestRecord request = processRecord.mRequestRecords.valueAt(i);
-                // Cancel the request but don't mark it as dirty since there's no need to send
-                // notifications if the process has died.
-                request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED,
-                        false /* markDirty */);
-            }
-
             mProcessRecords.remove(processRecord.mPid);
-
-            updatePendingStateLocked();
+            mOverrideRequestController.handleProcessDied(processRecord.mPid);
         }
-
-        notifyPolicyIfNeeded();
     }
 
     private void requestStateInternal(int state, int flags, int callingPid,
@@ -577,7 +586,7 @@
                         + " has no registered callback.");
             }
 
-            if (processRecord.mRequestRecords.get(token) != null) {
+            if (mOverrideRequestController.hasRequest(token)) {
                 throw new IllegalStateException("Request has already been made for the supplied"
                         + " token: " + token);
             }
@@ -588,27 +597,9 @@
                         + " is not supported.");
             }
 
-            OverrideRequestRecord topRecord = mRequestRecords.isEmpty()
-                    ? null : mRequestRecords.get(mRequestRecords.size() - 1);
-            if (topRecord != null) {
-                topRecord.setStatusLocked(OverrideRequestRecord.STATUS_SUSPENDED);
-            }
-
-            final OverrideRequestRecord request =
-                    new OverrideRequestRecord(processRecord, token, deviceState.get(), flags);
-            mRequestRecords.add(request);
-            processRecord.mRequestRecords.put(request.mToken, request);
-
-            final boolean updatedPendingState = updatePendingStateLocked();
-            if (!updatedPendingState && !mPendingState.isPresent()) {
-                // We don't set the status of the new request to ACTIVE if the request updated the
-                // pending state as it will be set in commitPendingState().
-                request.setStatusLocked(OverrideRequestRecord.STATUS_ACTIVE, true /* markDirty */);
-            }
+            OverrideRequest request = new OverrideRequest(token, callingPid, state, flags);
+            mOverrideRequestController.addRequest(request);
         }
-
-        notifyRequestsOfStatusChangeIfNeeded();
-        notifyPolicyIfNeeded();
     }
 
     private void cancelRequestInternal(int callingPid, @NonNull IBinder token) {
@@ -619,18 +610,8 @@
                         + " has no registered callback.");
             }
 
-            OverrideRequestRecord request = processRecord.mRequestRecords.get(token);
-            if (request == null) {
-                throw new IllegalStateException("No known request for the given token");
-            }
-
-            request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED);
-
-            updatePendingStateLocked();
+            mOverrideRequestController.cancelRequest(token);
         }
-
-        notifyRequestsOfStatusChangeIfNeeded();
-        notifyPolicyIfNeeded();
     }
 
     private void dumpInternal(PrintWriter pw) {
@@ -650,16 +631,7 @@
                 pw.println("  " + i + ": mPid=" + processRecord.mPid);
             }
 
-            final int requestCount = mRequestRecords.size();
-            pw.println();
-            pw.println("Override requests: size=" + requestCount);
-            for (int i = 0; i < requestCount; i++) {
-                OverrideRequestRecord requestRecord = mRequestRecords.get(i);
-                pw.println("  " + i + ": mPid=" + requestRecord.mProcessRecord.mPid
-                        + ", mRequestedState=" + requestRecord.mRequestedState
-                        + ", mFlags=" + requestRecord.mFlags
-                        + ", mStatus=" + requestRecord.statusToString(requestRecord.mStatus));
-            }
+            mOverrideRequestController.dumpInternal(pw);
         }
     }
 
@@ -683,142 +655,107 @@
         }
     }
 
-    private final class ProcessRecord implements IBinder.DeathRecipient {
+    private static final class ProcessRecord implements IBinder.DeathRecipient {
+        public interface DeathListener {
+            void onProcessDied(ProcessRecord record);
+        }
+
+        private static final int STATUS_ACTIVE = 0;
+
+        private static final int STATUS_SUSPENDED = 1;
+
+        private static final int STATUS_CANCELED = 2;
+
+        @IntDef(prefix = {"STATUS_"}, value = {
+                STATUS_ACTIVE,
+                STATUS_SUSPENDED,
+                STATUS_CANCELED
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        private @interface RequestStatus {}
+
         private final IDeviceStateManagerCallback mCallback;
         private final int mPid;
+        private final DeathListener mDeathListener;
+        private final Handler mHandler;
 
-        private final ArrayMap<IBinder, OverrideRequestRecord> mRequestRecords = new ArrayMap<>();
+        private final WeakHashMap<IBinder, Integer> mLastNotifiedStatus = new WeakHashMap<>();
 
-        ProcessRecord(IDeviceStateManagerCallback callback, int pid) {
+        ProcessRecord(IDeviceStateManagerCallback callback, int pid, DeathListener deathListener,
+                Handler handler) {
             mCallback = callback;
             mPid = pid;
+            mDeathListener = deathListener;
+            mHandler = handler;
         }
 
         @Override
         public void binderDied() {
-            handleProcessDied(this);
+            mDeathListener.onProcessDied(this);
         }
 
         public void notifyDeviceStateInfoAsync(@NonNull DeviceStateInfo info) {
-            try {
-                mCallback.onDeviceStateInfoChanged(info);
-            } catch (RemoteException ex) {
-                Slog.w(TAG, "Failed to notify process " + mPid + " that device state changed.",
-                        ex);
-            }
-        }
-
-        public void notifyRequestActiveAsync(OverrideRequestRecord request) {
-            try {
-                mCallback.onRequestActive(request.mToken);
-            } catch (RemoteException ex) {
-                Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
-                        ex);
-            }
-        }
-
-        public void notifyRequestSuspendedAsync(OverrideRequestRecord request) {
-            try {
-                mCallback.onRequestSuspended(request.mToken);
-            } catch (RemoteException ex) {
-                Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
-                        ex);
-            }
-        }
-
-        public void notifyRequestCanceledAsync(OverrideRequestRecord request) {
-            try {
-                mCallback.onRequestCanceled(request.mToken);
-            } catch (RemoteException ex) {
-                Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
-                        ex);
-            }
-        }
-    }
-
-    /** A record describing a request to override the state of the device. */
-    private final class OverrideRequestRecord {
-        public static final int STATUS_UNKNOWN = 0;
-        public static final int STATUS_ACTIVE = 1;
-        public static final int STATUS_SUSPENDED = 2;
-        public static final int STATUS_CANCELED = 3;
-
-        @Nullable
-        public String statusToString(int status) {
-            switch (status) {
-                case STATUS_ACTIVE:
-                    return "ACTIVE";
-                case STATUS_SUSPENDED:
-                    return "SUSPENDED";
-                case STATUS_CANCELED:
-                    return "CANCELED";
-                case STATUS_UNKNOWN:
-                    return "UNKNOWN";
-                default:
-                    return null;
-            }
-        }
-
-        private final ProcessRecord mProcessRecord;
-        @NonNull
-        private final IBinder mToken;
-        @NonNull
-        private final DeviceState mRequestedState;
-        private final int mFlags;
-
-        private int mStatus = STATUS_UNKNOWN;
-        private int mLastNotifiedStatus = STATUS_UNKNOWN;
-
-        OverrideRequestRecord(@NonNull ProcessRecord processRecord, @NonNull IBinder token,
-                @NonNull DeviceState requestedState, int flags) {
-            mProcessRecord = processRecord;
-            mToken = token;
-            mRequestedState = requestedState;
-            mFlags = flags;
-        }
-
-        public void setStatusLocked(int status) {
-            setStatusLocked(status, true /* markDirty */);
-        }
-
-        public void setStatusLocked(int status, boolean markDirty) {
-            if (mStatus != status) {
-                if (mStatus == STATUS_CANCELED) {
-                    throw new IllegalStateException(
-                            "Can not alter the status of a request after set to CANCELED.");
+            mHandler.post(() -> {
+                try {
+                    mCallback.onDeviceStateInfoChanged(info);
+                } catch (RemoteException ex) {
+                    Slog.w(TAG, "Failed to notify process " + mPid + " that device state changed.",
+                            ex);
                 }
-
-                mStatus = status;
-
-                if (mStatus == STATUS_CANCELED) {
-                    mRequestRecords.remove(this);
-                    mProcessRecord.mRequestRecords.remove(mToken);
-                }
-
-                if (markDirty) {
-                    mRequestsPendingStatusChange.add(this);
-                }
-            }
+            });
         }
 
-        public void notifyStatusIfNeeded() {
-            int stateToReport;
-            synchronized (mLock) {
-                if (mLastNotifiedStatus == mStatus) {
-                    return;
+        public void notifyRequestActiveAsync(IBinder token) {
+            @RequestStatus Integer lastStatus = mLastNotifiedStatus.get(token);
+            if (lastStatus != null
+                    && (lastStatus == STATUS_ACTIVE || lastStatus == STATUS_CANCELED)) {
+                return;
+            }
+
+            mLastNotifiedStatus.put(token, STATUS_ACTIVE);
+            mHandler.post(() -> {
+                try {
+                    mCallback.onRequestActive(token);
+                } catch (RemoteException ex) {
+                    Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
+                            ex);
                 }
+            });
+        }
 
-                stateToReport = mStatus;
-                mLastNotifiedStatus = mStatus;
+        public void notifyRequestSuspendedAsync(IBinder token) {
+            @RequestStatus Integer lastStatus = mLastNotifiedStatus.get(token);
+            if (lastStatus != null
+                    && (lastStatus == STATUS_SUSPENDED || lastStatus == STATUS_CANCELED)) {
+                return;
             }
 
-            if (stateToReport == STATUS_ACTIVE) {
-                mProcessRecord.notifyRequestActiveAsync(this);
-            } else if (stateToReport == STATUS_SUSPENDED) {
-                mProcessRecord.notifyRequestSuspendedAsync(this);
-            } else if (stateToReport == STATUS_CANCELED) {
-                mProcessRecord.notifyRequestCanceledAsync(this);
+            mLastNotifiedStatus.put(token, STATUS_SUSPENDED);
+            mHandler.post(() -> {
+                try {
+                    mCallback.onRequestSuspended(token);
+                } catch (RemoteException ex) {
+                    Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
+                            ex);
+                }
+            });
+        }
+
+        public void notifyRequestCanceledAsync(IBinder token) {
+            @RequestStatus Integer lastStatus = mLastNotifiedStatus.get(token);
+            if (lastStatus != null && lastStatus == STATUS_CANCELED) {
+                return;
             }
+
+            mLastNotifiedStatus.put(token, STATUS_CANCELED);
+            mHandler.post(() -> {
+                try {
+                    mCallback.onRequestCanceled(token);
+                } catch (RemoteException ex) {
+                    Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
+                            ex);
+                }
+            });
         }
     }
 
@@ -848,14 +785,21 @@
 
         @Override // Binder call
         public void requestState(IBinder token, int state, int flags) {
-            getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
-                    "Permission required to request device state.");
+            final int callingPid = Binder.getCallingPid();
+            // Allow top processes to request a device state change
+            // If the calling process ID is not the top app, then we check if this process
+            // holds a permission to CONTROL_DEVICE_STATE
+            final WindowProcessController topApp = mActivityTaskManagerInternal.getTopApp();
+            if (topApp.getPid() != callingPid) {
+                getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
+                        "Permission required to request device state, "
+                                + "or the call must come from the top focused app.");
+            }
 
             if (token == null) {
                 throw new IllegalArgumentException("Request token must not be null.");
             }
 
-            final int callingPid = Binder.getCallingPid();
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 requestStateInternal(state, flags, callingPid, token);
@@ -866,14 +810,21 @@
 
         @Override // Binder call
         public void cancelRequest(IBinder token) {
-            getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
-                    "Permission required to clear requested device state.");
+            final int callingPid = Binder.getCallingPid();
+            // Allow top processes to cancel a device state change
+            // If the calling process ID is not the top app, then we check if this process
+            // holds a permission to CONTROL_DEVICE_STATE
+            final WindowProcessController topApp = mActivityTaskManagerInternal.getTopApp();
+            if (topApp.getPid() != callingPid) {
+                getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
+                        "Permission required to cancel device state, "
+                                + "or the call must come from the top focused app.");
+            }
 
             if (token == null) {
                 throw new IllegalArgumentException("Request token must not be null.");
             }
 
-            final int callingPid = Binder.getCallingPid();
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 cancelRequestInternal(callingPid, token);
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
index 56b68b7..eed68f8 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
@@ -27,7 +27,9 @@
 import android.os.ShellCommand;
 
 import java.io.PrintWriter;
+import java.util.Arrays;
 import java.util.Optional;
+import java.util.stream.Collectors;
 
 /**
  * ShellCommands for {@link DeviceStateManagerService}.
@@ -56,14 +58,18 @@
         switch (cmd) {
             case "state":
                 return runState(pw);
+            case "print-state":
+                return runPrintState(pw);
             case "print-states":
                 return runPrintStates(pw);
+            case "print-states-simple":
+                return runPrintStatesSimple(pw);
             default:
                 return handleDefaultCommands(cmd);
         }
     }
 
-    private void printState(PrintWriter pw) {
+    private void printAllStates(PrintWriter pw) {
         Optional<DeviceState> committedState = mService.getCommittedState();
         Optional<DeviceState> baseState = mService.getBaseState();
         Optional<DeviceState> overrideState = mService.getOverrideState();
@@ -79,7 +85,8 @@
     private int runState(PrintWriter pw) {
         final String nextArg = getNextArg();
         if (nextArg == null) {
-            printState(pw);
+            printAllStates(pw);
+            return 0;
         }
 
         final Context context = mService.getContext();
@@ -123,6 +130,16 @@
         return 0;
     }
 
+    private int runPrintState(PrintWriter pw) {
+        Optional<DeviceState> deviceState = mService.getCommittedState();
+        if (deviceState.isPresent()) {
+            pw.println(deviceState.get().getIdentifier());
+            return 0;
+        }
+        getErrPrintWriter().println("Error: device state not available.");
+        return 1;
+    }
+
     private int runPrintStates(PrintWriter pw) {
         DeviceState[] states = mService.getSupportedStates();
         pw.print("Supported states: [\n");
@@ -133,6 +150,14 @@
         return 0;
     }
 
+    private int runPrintStatesSimple(PrintWriter pw) {
+        pw.print(Arrays.stream(mService.getSupportedStates())
+                .map(DeviceState::getIdentifier)
+                .map(Object::toString)
+                .collect(Collectors.joining(",")));
+        return 0;
+    }
+
     @Override
     public void onHelp() {
         PrintWriter pw = getOutPrintWriter();
@@ -141,8 +166,12 @@
         pw.println("    Print this help text.");
         pw.println("  state [reset|OVERRIDE_DEVICE_STATE]");
         pw.println("    Return or override device state.");
+        pw.println("  print-state");
+        pw.println("    Return the current device state.");
         pw.println("  print-states");
         pw.println("    Return list of currently supported device states.");
+        pw.println("  print-states-simple");
+        pw.println("    Return the currently supported device states in comma separated format.");
     }
 
     private static String toString(@NonNull Optional<DeviceState> state) {
diff --git a/services/core/java/com/android/server/devicestate/OverrideRequest.java b/services/core/java/com/android/server/devicestate/OverrideRequest.java
new file mode 100644
index 0000000..35a4c84
--- /dev/null
+++ b/services/core/java/com/android/server/devicestate/OverrideRequest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.devicestate;
+
+import android.hardware.devicestate.DeviceStateRequest;
+import android.os.IBinder;
+
+/**
+ * A request to override the state managed by {@link DeviceStateManagerService}.
+ *
+ * @see OverrideRequestController
+ */
+final class OverrideRequest {
+    private final IBinder mToken;
+    private final int mPid;
+    private final int mRequestedState;
+    @DeviceStateRequest.RequestFlags
+    private final int mFlags;
+
+    OverrideRequest(IBinder token, int pid, int requestedState,
+            @DeviceStateRequest.RequestFlags int flags) {
+        mToken = token;
+        mPid = pid;
+        mRequestedState = requestedState;
+        mFlags = flags;
+    }
+
+    IBinder getToken() {
+        return mToken;
+    }
+
+    int getPid() {
+        return mPid;
+    }
+
+    int getRequestedState() {
+        return mRequestedState;
+    }
+
+    @DeviceStateRequest.RequestFlags
+    int getFlags() {
+        return mFlags;
+    }
+}
diff --git a/services/core/java/com/android/server/devicestate/OverrideRequestController.java b/services/core/java/com/android/server/devicestate/OverrideRequestController.java
new file mode 100644
index 0000000..05c9eb2
--- /dev/null
+++ b/services/core/java/com/android/server/devicestate/OverrideRequestController.java
@@ -0,0 +1,322 @@
+/*
+ * 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.devicestate;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.devicestate.DeviceStateRequest;
+import android.os.IBinder;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Manages the lifecycle of override requests.
+ * <p>
+ * New requests are added with {@link #addRequest(OverrideRequest)} and are kept active until
+ * either:
+ * <ul>
+ *     <li>A new request is added with {@link #addRequest(OverrideRequest)}, in which case the
+ *     request will become suspended.</li>
+ *     <li>The request is cancelled with {@link #cancelRequest(IBinder)} or as a side effect
+ *     of other methods calls, such as {@link #handleProcessDied(int)}.</li>
+ * </ul>
+ */
+final class OverrideRequestController {
+    static final int STATUS_UNKNOWN = 0;
+    /**
+     * The request is the top-most request.
+     */
+    static final int STATUS_ACTIVE = 1;
+    /**
+     * The request is still present but is being superseded by another request.
+     */
+    static final int STATUS_SUSPENDED = 2;
+    /**
+     * The request is not longer valid.
+     */
+    static final int STATUS_CANCELED = 3;
+
+    @IntDef(prefix = {"STATUS_"}, value = {
+            STATUS_UNKNOWN,
+            STATUS_ACTIVE,
+            STATUS_SUSPENDED,
+            STATUS_CANCELED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface RequestStatus {}
+
+    static String statusToString(@RequestStatus int status) {
+        switch (status) {
+            case STATUS_ACTIVE:
+                return "ACTIVE";
+            case STATUS_SUSPENDED:
+                return "SUSPENDED";
+            case STATUS_CANCELED:
+                return "CANCELED";
+            case STATUS_UNKNOWN:
+                return "UNKNOWN";
+        }
+        throw new IllegalArgumentException("Unknown status: " + status);
+    }
+
+    private final StatusChangeListener mListener;
+    private final List<OverrideRequest> mTmpRequestsToCancel = new ArrayList<>();
+
+    // List of override requests with the most recent override request at the end.
+    private final ArrayList<OverrideRequest> mRequests = new ArrayList<>();
+
+    private boolean mStickyRequestsAllowed;
+    // List of override requests that have outlived their process and will only be cancelled through
+    // a call to cancelStickyRequests().
+    private final ArrayList<OverrideRequest> mStickyRequests = new ArrayList<>();
+
+    OverrideRequestController(@NonNull StatusChangeListener listener) {
+        mListener = listener;
+    }
+
+    /**
+     * Sets sticky requests as either allowed or disallowed. When sticky requests are allowed a call
+     * to {@link #handleProcessDied(int)} will not result in the request being cancelled
+     * immediately. Instead, the request will be marked sticky and must be cancelled with a call
+     * to {@link #cancelStickyRequests()}.
+     */
+    void setStickyRequestsAllowed(boolean stickyRequestsAllowed) {
+        mStickyRequestsAllowed = stickyRequestsAllowed;
+        if (!mStickyRequestsAllowed) {
+            cancelStickyRequests();
+        }
+    }
+
+    /**
+     * Adds a request to the top of the stack and notifies the listener of all changes to request
+     * status as a result of this operation.
+     */
+    void addRequest(@NonNull OverrideRequest request) {
+        mRequests.add(request);
+        mListener.onStatusChanged(request, STATUS_ACTIVE);
+
+        if (mRequests.size() > 1) {
+            OverrideRequest prevRequest = mRequests.get(mRequests.size() - 2);
+            mListener.onStatusChanged(prevRequest, STATUS_SUSPENDED);
+        }
+    }
+
+    /**
+     * Cancels the request with the specified {@code token} and notifies the listener of all changes
+     * to request status as a result of this operation.
+     */
+    void cancelRequest(@NonNull IBinder token) {
+        int index = getRequestIndex(token);
+        if (index == -1) {
+            return;
+        }
+
+        OverrideRequest request = mRequests.remove(index);
+        if (index == mRequests.size() && mRequests.size() > 0) {
+            // We removed the current active request so we need to set the new active request
+            // before cancelling this request.
+            OverrideRequest newTop = getLast(mRequests);
+            mListener.onStatusChanged(newTop, STATUS_ACTIVE);
+        }
+        mListener.onStatusChanged(request, STATUS_CANCELED);
+    }
+
+    /**
+     * Cancels all requests that are currently marked sticky and notifies the listener of all
+     * changes to request status as a result of this operation.
+     *
+     * @see #setStickyRequestsAllowed(boolean)
+     */
+    void cancelStickyRequests() {
+        mTmpRequestsToCancel.clear();
+        mTmpRequestsToCancel.addAll(mStickyRequests);
+        cancelRequestsLocked(mTmpRequestsToCancel);
+    }
+
+    /**
+     * Returns {@code true} if this controller is current managing a request with the specified
+     * {@code token}, {@code false} otherwise.
+     */
+    boolean hasRequest(@NonNull IBinder token) {
+        return getRequestIndex(token) != -1;
+    }
+
+    /**
+     * Notifies the controller that the process with the specified {@code pid} has died. The
+     * controller will notify the listener of all changes to request status as a result of this
+     * operation.
+     */
+    void handleProcessDied(int pid) {
+        if (mRequests.isEmpty()) {
+            return;
+        }
+
+        mTmpRequestsToCancel.clear();
+        OverrideRequest prevActiveRequest = getLast(mRequests);
+        for (OverrideRequest request : mRequests) {
+            if (request.getPid() == pid) {
+                mTmpRequestsToCancel.add(request);
+            }
+        }
+
+        if (mStickyRequestsAllowed) {
+            // Do not cancel the requests now because sticky requests are allowed. These
+            // requests will be cancelled on a call to cancelStickyRequests().
+            mStickyRequests.addAll(mTmpRequestsToCancel);
+            return;
+        }
+
+        cancelRequestsLocked(mTmpRequestsToCancel);
+    }
+
+    /**
+     * Notifies the controller that the base state has changed. The controller will notify the
+     * listener of all changes to request status as a result of this change.
+     *
+     * @return {@code true} if calling this method has lead to a new active request, {@code false}
+     * otherwise.
+     */
+    boolean handleBaseStateChanged() {
+        if (mRequests.isEmpty()) {
+            return false;
+        }
+
+        mTmpRequestsToCancel.clear();
+        OverrideRequest prevActiveRequest = getLast(mRequests);
+        for (int i = 0; i < mRequests.size(); i++) {
+            OverrideRequest request = mRequests.get(i);
+            if ((request.getFlags() & DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES) != 0) {
+                mTmpRequestsToCancel.add(request);
+            }
+        }
+
+        final boolean newActiveRequest = cancelRequestsLocked(mTmpRequestsToCancel);
+        return newActiveRequest;
+    }
+
+    /**
+     * Notifies the controller that the set of supported states has changed. The controller will
+     * notify the listener of all changes to request status as a result of this change.
+     *
+     * @return {@code true} if calling this method has lead to a new active request, {@code false}
+     * otherwise.
+     */
+    boolean handleNewSupportedStates(int[] newSupportedStates) {
+        if (mRequests.isEmpty()) {
+            return false;
+        }
+
+        mTmpRequestsToCancel.clear();
+        for (int i = 0; i < mRequests.size(); i++) {
+            OverrideRequest request = mRequests.get(i);
+            if (!contains(newSupportedStates, request.getRequestedState())) {
+                mTmpRequestsToCancel.add(request);
+            }
+        }
+
+        final boolean newActiveRequest = cancelRequestsLocked(mTmpRequestsToCancel);
+        return newActiveRequest;
+    }
+
+    void dumpInternal(PrintWriter pw) {
+        final int requestCount = mRequests.size();
+        pw.println();
+        pw.println("Override requests: size=" + requestCount);
+        for (int i = 0; i < requestCount; i++) {
+            OverrideRequest overrideRequest = mRequests.get(i);
+            int status = (i == requestCount - 1) ? STATUS_ACTIVE : STATUS_SUSPENDED;
+            pw.println("  " + i + ": mPid=" + overrideRequest.getPid()
+                    + ", mRequestedState=" + overrideRequest.getRequestedState()
+                    + ", mFlags=" + overrideRequest.getFlags()
+                    + ", mStatus=" + statusToString(status));
+        }
+    }
+
+    /**
+     * Handles cancelling a set of requests. If the set of requests to cancel will lead to a new
+     * request becoming active this request will also be notified of its change in state.
+     *
+     * @return {@code true} if calling this method has lead to a new active request, {@code false}
+     * otherwise.
+     */
+    private boolean cancelRequestsLocked(List<OverrideRequest> requestsToCancel) {
+        if (requestsToCancel.isEmpty()) {
+            return false;
+        }
+
+        OverrideRequest prevActiveRequest = getLast(mRequests);
+        boolean causedNewRequestToBecomeActive = false;
+        mRequests.removeAll(requestsToCancel);
+        mStickyRequests.removeAll(requestsToCancel);
+        if (!mRequests.isEmpty()) {
+            OverrideRequest newActiveRequest = getLast(mRequests);
+            if (newActiveRequest != prevActiveRequest) {
+                mListener.onStatusChanged(newActiveRequest, STATUS_ACTIVE);
+                causedNewRequestToBecomeActive = true;
+            }
+        }
+
+        for (int i = 0; i < requestsToCancel.size(); i++) {
+            mListener.onStatusChanged(requestsToCancel.get(i), STATUS_CANCELED);
+        }
+        return causedNewRequestToBecomeActive;
+    }
+
+    private int getRequestIndex(@NonNull IBinder token) {
+        final int numberOfRequests = mRequests.size();
+        if (numberOfRequests == 0) {
+            return -1;
+        }
+
+        for (int i = 0; i < numberOfRequests; i++) {
+            OverrideRequest request = mRequests.get(i);
+            if (request.getToken() == token) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    @Nullable
+    private static <T> T getLast(List<T> list) {
+        return list.size() > 0 ? list.get(list.size() - 1) : null;
+    }
+
+    private static boolean contains(int[] array, int value) {
+        for (int i = 0; i < array.length; i++) {
+            if (array[i] == value) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public interface StatusChangeListener {
+        /**
+         * Notifies the listener of a change in request status. If a change within the controller
+         * causes one request to become active and one to become either suspended or cancelled, this
+         * method is guaranteed to be called with the active request first before the suspended or
+         * cancelled request.
+         */
+        void onStatusChanged(@NonNull OverrideRequest request, @RequestStatus int newStatus);
+    }
+}
diff --git a/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
index 1acd5d0..9dd2f84 100644
--- a/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
+++ b/services/core/java/com/android/server/display/DeviceStateToLayoutMap.java
@@ -111,8 +111,8 @@
                 for (com.android.server.display.config.layout.Display d: l.getDisplay()) {
                     layout.createDisplayLocked(
                             DisplayAddress.fromPhysicalDisplayId(d.getAddress().longValue()),
-                            d.getIsDefault(),
-                            d.getEnabled());
+                            d.isDefaultDisplay(),
+                            d.isEnabled());
                 }
             }
         } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 9f806af..806bcc2 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -16,7 +16,9 @@
 
 package com.android.server.display;
 
+import android.annotation.Nullable;
 import android.content.Context;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.display.DisplayViewport;
 import android.os.IBinder;
@@ -105,6 +107,34 @@
     }
 
     /**
+     * Returns the window token of the level of the WindowManager hierarchy to mirror, or null
+     * if layer mirroring by SurfaceFlinger should not be performed.
+     * For now, only used for mirroring started from MediaProjection.
+     */
+    @Nullable
+    public IBinder getWindowTokenClientToMirrorLocked() {
+        return null;
+    }
+
+    /**
+     * Updates the window token of the level of the level of the WindowManager hierarchy to mirror.
+     * If windowToken is null, then no layer mirroring by SurfaceFlinger to should be performed.
+     * For now, only used for mirroring started from MediaProjection.
+     */
+    public void setWindowTokenClientToMirrorLocked(IBinder windowToken) {
+    }
+
+    /**
+     * Returns the default size of the surface associated with the display, or null if the surface
+     * is not provided for layer mirroring by SurfaceFlinger.
+     * For now, only used for mirroring started from MediaProjection.
+     */
+    @Nullable
+    public Point getDisplaySurfaceDefaultSize() {
+        return null;
+    }
+
+    /**
      * Gets the name of the display device.
      *
      * @return The display device name.
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 4c9d0f2..2ae5cbb 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -590,7 +590,7 @@
             newIndex = i - newStart;
             final float newBacklightVal;
             final float newNitsVal;
-            isLastValue = mRawBacklight[i] > mBacklightMaximum
+            isLastValue = mRawBacklight[i] >= mBacklightMaximum
                     || i >= mRawBacklight.length - 1;
             // Clamp beginning and end to valid backlight values.
             if (newIndex == 0) {
diff --git a/services/core/java/com/android/server/display/DisplayDeviceRepository.java b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
index 57f4486..2b52350 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceRepository.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceRepository.java
@@ -123,6 +123,17 @@
         return null;
     }
 
+    // String uniqueId -> DisplayDevice object with that given uniqueId
+    public DisplayDevice getByUniqueIdLocked(@NonNull String uniqueId) {
+        for (int i = mDisplayDevices.size() - 1; i >= 0; i--) {
+            final DisplayDevice displayDevice = mDisplayDevices.get(i);
+            if (displayDevice.getUniqueId().equals(uniqueId)) {
+                return displayDevice;
+            }
+        }
+        return null;
+    }
+
     private void handleDisplayDeviceAdded(DisplayDevice device) {
         synchronized (mSyncRoot) {
             DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 7d06d6e..73bcea6 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -63,8 +63,6 @@
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.display.DisplayManagerInternal.DisplayGroupListener;
 import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener;
-import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
-import android.hardware.display.DisplayManagerInternal.RefreshRateRange;
 import android.hardware.display.DisplayViewport;
 import android.hardware.display.DisplayedContentSample;
 import android.hardware.display.DisplayedContentSamplingAttributes;
@@ -192,14 +190,16 @@
 
     private static final String PROP_DEFAULT_DISPLAY_TOP_INSET = "persist.sys.displayinset.top";
     private static final long WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT = 10000;
-    private static final float THRESHOLD_FOR_REFRESH_RATES_DIVIDERS = 0.1f;
+    // This value needs to be in sync with the threshold
+    // in RefreshRateConfigs::getFrameRateDivider.
+    private static final float THRESHOLD_FOR_REFRESH_RATES_DIVIDERS = 0.0009f;
 
     private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS = 1;
     private static final int MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS = 2;
     private static final int MSG_DELIVER_DISPLAY_EVENT = 3;
     private static final int MSG_REQUEST_TRAVERSAL = 4;
     private static final int MSG_UPDATE_VIEWPORT = 5;
-    private static final int MSG_LOAD_BRIGHTNESS_CONFIGURATION = 6;
+    private static final int MSG_LOAD_BRIGHTNESS_CONFIGURATIONS = 6;
     private static final int MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE = 7;
     private static final int MSG_DELIVER_DISPLAY_GROUP_EVENT = 8;
 
@@ -525,16 +525,29 @@
         final int newUserId = to.getUserIdentifier();
         final int userSerial = getUserManager().getUserSerialNumber(newUserId);
         synchronized (mSyncRoot) {
-            final DisplayPowerController displayPowerController = mDisplayPowerControllers.get(
-                    Display.DEFAULT_DISPLAY);
-            if (mCurrentUserId != newUserId) {
+            boolean userSwitching = mCurrentUserId != newUserId;
+            if (userSwitching) {
                 mCurrentUserId = newUserId;
-                BrightnessConfiguration config =
-                        mPersistentDataStore.getBrightnessConfiguration(userSerial);
-                displayPowerController.setBrightnessConfiguration(config);
-                handleSettingsChange();
             }
-            displayPowerController.onSwitchUser(newUserId);
+            mLogicalDisplayMapper.forEachLocked(logicalDisplay -> {
+                if (logicalDisplay.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) {
+                    return;
+                }
+                final DisplayPowerController dpc = mDisplayPowerControllers.get(
+                        logicalDisplay.getDisplayIdLocked());
+                if (dpc == null) {
+                    return;
+                }
+                if (userSwitching) {
+                    BrightnessConfiguration config =
+                            getBrightnessConfigForDisplayWithPdsFallbackLocked(
+                            logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId(),
+                            userSerial);
+                    dpc.setBrightnessConfiguration(config);
+                }
+                dpc.onSwitchUser(newUserId);
+            });
+            handleSettingsChange();
         }
     }
 
@@ -636,6 +649,9 @@
         synchronized (mSyncRoot) {
             final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
             if (display != null) {
+                // Do not let constrain be overwritten by override from WindowManager.
+                info.shouldConstrainMetricsForLauncher =
+                        display.getDisplayInfoLocked().shouldConstrainMetricsForLauncher;
                 if (display.setDisplayInfoOverrideFromWindowManagerLocked(info)) {
                     handleLogicalDisplayChangedLocked(display);
                     scheduleTraversalLocked(false);
@@ -812,7 +828,7 @@
 
         // Override the refresh rate only if it is a divider of the current
         // refresh rate. This calculation needs to be in sync with the native code
-        // in RefreshRateConfigs::getRefreshRateDividerForUid
+        // in RefreshRateConfigs::getFrameRateDivider
         Display.Mode currentMode = info.getMode();
         float numPeriods = currentMode.getRefreshRate() / frameRateHz;
         float numPeriodsRound = Math.round(numPeriods);
@@ -1315,6 +1331,13 @@
         if (work != null) {
             mHandler.post(work);
         }
+        final int displayId = display.getDisplayIdLocked();
+        DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
+        if (dpc != null) {
+            dpc.onDisplayChanged();
+        }
+        mPersistentDataStore.saveIfNeeded();
+        mHandler.sendEmptyMessage(MSG_LOAD_BRIGHTNESS_CONFIGURATIONS);
         handleLogicalDisplayChangedLocked(display);
     }
 
@@ -1423,24 +1446,42 @@
         return mDisplayModeDirector.getModeSwitchingType();
     }
 
-    private void setBrightnessConfigurationForUserInternal(
-            @Nullable BrightnessConfiguration c, @UserIdInt int userId,
-            @Nullable String packageName) {
+    private void setBrightnessConfigurationForDisplayInternal(
+            @Nullable BrightnessConfiguration c, String uniqueId, @UserIdInt int userId,
+            String packageName) {
         validateBrightnessConfiguration(c);
         final int userSerial = getUserManager().getUserSerialNumber(userId);
         synchronized (mSyncRoot) {
             try {
-                mPersistentDataStore.setBrightnessConfigurationForUser(c, userSerial,
-                        packageName);
+                DisplayDevice displayDevice = mDisplayDeviceRepo.getByUniqueIdLocked(uniqueId);
+                if (displayDevice == null) {
+                    return;
+                }
+                mPersistentDataStore.setBrightnessConfigurationForDisplayLocked(c, displayDevice,
+                        userSerial, packageName);
             } finally {
                 mPersistentDataStore.saveIfNeeded();
             }
-            if (userId == mCurrentUserId) {
-                mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY).setBrightnessConfiguration(c);
+            if (userId != mCurrentUserId) {
+                return;
+            }
+            DisplayPowerController dpc = getDpcFromUniqueIdLocked(uniqueId);
+            if (dpc != null) {
+                dpc.setBrightnessConfiguration(c);
             }
         }
     }
 
+    private DisplayPowerController getDpcFromUniqueIdLocked(String uniqueId) {
+        final DisplayDevice displayDevice = mDisplayDeviceRepo.getByUniqueIdLocked(uniqueId);
+        final LogicalDisplay logicalDisplay = mLogicalDisplayMapper.getDisplayLocked(displayDevice);
+        if (logicalDisplay != null) {
+            final int displayId = logicalDisplay.getDisplayIdLocked();
+            return mDisplayPowerControllers.get(displayId);
+        }
+        return null;
+    }
+
     @VisibleForTesting
     void validateBrightnessConfiguration(BrightnessConfiguration config) {
         if (config == null) {
@@ -1463,13 +1504,22 @@
         return false;
     }
 
-    private void loadBrightnessConfiguration() {
+    private void loadBrightnessConfigurations() {
+        int userSerial = getUserManager().getUserSerialNumber(mContext.getUserId());
         synchronized (mSyncRoot) {
-            final int userSerial = getUserManager().getUserSerialNumber(mCurrentUserId);
-            BrightnessConfiguration config =
-                    mPersistentDataStore.getBrightnessConfiguration(userSerial);
-            mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY).setBrightnessConfiguration(
-                    config);
+            mLogicalDisplayMapper.forEachLocked((logicalDisplay) -> {
+                final String uniqueId =
+                        logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
+                final BrightnessConfiguration config =
+                        getBrightnessConfigForDisplayWithPdsFallbackLocked(uniqueId, userSerial);
+                if (config != null) {
+                    final DisplayPowerController dpc = mDisplayPowerControllers.get(
+                            logicalDisplay.getDisplayIdLocked());
+                    if (dpc != null) {
+                        dpc.setBrightnessConfiguration(config);
+                    }
+                }
+            });
         }
     }
 
@@ -1680,9 +1730,17 @@
         return SurfaceControl.getDisplayedContentSample(token, maxFrames, timestamp);
     }
 
-    void resetBrightnessConfiguration() {
-        setBrightnessConfigurationForUserInternal(null, mContext.getUserId(),
+    void resetBrightnessConfigurations() {
+        mPersistentDataStore.setBrightnessConfigurationForUser(null, mContext.getUserId(),
                 mContext.getPackageName());
+        mLogicalDisplayMapper.forEachLocked((logicalDisplay -> {
+            if (logicalDisplay.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) {
+                return;
+            }
+            final String uniqueId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
+            setBrightnessConfigurationForDisplayInternal(null, uniqueId, mContext.getUserId(),
+                    mContext.getPackageName());
+        }));
     }
 
     void setAutoBrightnessLoggingEnabled(boolean enabled) {
@@ -1723,6 +1781,21 @@
         }
     }
 
+    void setShouldConstrainMetricsForLauncher(boolean constrain) {
+        // Apply constrain for every display.
+        synchronized (mSyncRoot) {
+            int[] displayIds = mLogicalDisplayMapper.getDisplayIdsLocked(Process.myUid());
+            for (int i : displayIds) {
+                final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(i);
+                if (display == null) {
+                    return;
+                }
+                display.getDisplayInfoLocked().shouldConstrainMetricsForLauncher = constrain;
+                setDisplayInfoOverrideFromWindowManagerInternal(i, display.getDisplayInfoLocked());
+            }
+        }
+    }
+
     private void clearViewportsLocked() {
         mViewports.clear();
     }
@@ -1749,10 +1822,13 @@
         final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
         final boolean ownContent = (info.flags & DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY) != 0;
 
+        // Mirror the part of WM hierarchy that corresponds to the provided window token.
+        IBinder windowTokenClientToMirror = device.getWindowTokenClientToMirrorLocked();
+
         // Find the logical display that the display device is showing.
         // Certain displays only ever show their own content.
         LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
-        if (!ownContent) {
+        if (!ownContent && windowTokenClientToMirror == null) {
             if (display != null && !display.hasContentLocked()) {
                 // If the display does not have any content of its own, then
                 // automatically mirror the requested logical display contents if possible.
@@ -2113,6 +2189,18 @@
         return display == null ? null : display.getPrimaryDisplayDeviceLocked();
     }
 
+    private BrightnessConfiguration getBrightnessConfigForDisplayWithPdsFallbackLocked(
+            String uniqueId, int userSerial) {
+        BrightnessConfiguration config =
+                mPersistentDataStore.getBrightnessConfigurationForDisplayLocked(
+                        uniqueId, userSerial);
+        if (config == null) {
+            // Get from global configurations
+            config = mPersistentDataStore.getBrightnessConfiguration(userSerial);
+        }
+        return config;
+    }
+
     private final class DisplayManagerHandler extends Handler {
         public DisplayManagerHandler(Looper looper) {
             super(looper, null, true /*async*/);
@@ -2154,8 +2242,8 @@
                     break;
                 }
 
-                case MSG_LOAD_BRIGHTNESS_CONFIGURATION:
-                    loadBrightnessConfiguration();
+                case MSG_LOAD_BRIGHTNESS_CONFIGURATIONS:
+                    loadBrightnessConfigurations();
                     break;
 
                 case MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE:
@@ -2770,6 +2858,19 @@
         @Override // Binder call
         public void setBrightnessConfigurationForUser(
                 BrightnessConfiguration c, @UserIdInt int userId, String packageName) {
+            mLogicalDisplayMapper.forEachLocked(logicalDisplay -> {
+                if (logicalDisplay.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) {
+                    return;
+                }
+                final DisplayDevice displayDevice = logicalDisplay.getPrimaryDisplayDeviceLocked();
+                setBrightnessConfigurationForDisplay(c, displayDevice.getUniqueId(), userId,
+                        packageName);
+            });
+        }
+
+        @Override // Binder call
+        public void setBrightnessConfigurationForDisplay(BrightnessConfiguration c,
+                String uniqueId, int userId, String packageName) {
             mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS,
                     "Permission required to change the display's brightness configuration");
@@ -2777,21 +2878,19 @@
                 mContext.enforceCallingOrSelfPermission(
                         Manifest.permission.INTERACT_ACROSS_USERS,
                         "Permission required to change the display brightness"
-                        + " configuration of another user");
-            }
-            if (packageName != null && !validatePackageName(getCallingUid(), packageName)) {
-                packageName = null;
+                                + " configuration of another user");
             }
             final long token = Binder.clearCallingIdentity();
             try {
-                setBrightnessConfigurationForUserInternal(c, userId, packageName);
+                setBrightnessConfigurationForDisplayInternal(c, uniqueId, userId, packageName);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
         }
 
         @Override // Binder call
-        public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) {
+        public BrightnessConfiguration getBrightnessConfigurationForDisplay(String uniqueId,
+                int userId) {
             mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS,
                     "Permission required to read the display's brightness configuration");
@@ -2802,14 +2901,19 @@
                                 + " configuration of another user");
             }
             final long token = Binder.clearCallingIdentity();
+            final int userSerial = getUserManager().getUserSerialNumber(userId);
             try {
-                final int userSerial = getUserManager().getUserSerialNumber(userId);
                 synchronized (mSyncRoot) {
+                    // Get from per-display configurations
                     BrightnessConfiguration config =
-                            mPersistentDataStore.getBrightnessConfiguration(userSerial);
+                            getBrightnessConfigForDisplayWithPdsFallbackLocked(
+                                    uniqueId, userSerial);
                     if (config == null) {
-                        config = mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY)
-                                .getDefaultBrightnessConfiguration();
+                        // Get default configuration
+                        DisplayPowerController dpc = getDpcFromUniqueIdLocked(uniqueId);
+                        if (dpc != null) {
+                            config = dpc.getDefaultBrightnessConfiguration();
+                        }
                     }
                     return config;
                 }
@@ -2818,6 +2922,21 @@
             }
         }
 
+
+
+        @Override // Binder call
+        public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) {
+            final String uniqueId;
+            synchronized (mSyncRoot) {
+                DisplayDevice displayDevice = mLogicalDisplayMapper.getDisplayLocked(
+                        Display.DEFAULT_DISPLAY).getPrimaryDisplayDeviceLocked();
+                uniqueId = displayDevice.getUniqueId();
+            }
+            return getBrightnessConfigurationForDisplay(uniqueId, userId);
+
+
+        }
+
         @Override // Binder call
         public BrightnessConfiguration getDefaultBrightnessConfiguration() {
             mContext.enforceCallingOrSelfPermission(
@@ -3086,7 +3205,7 @@
                 initializeDisplayPowerControllersLocked();
             }
 
-            mHandler.sendEmptyMessage(MSG_LOAD_BRIGHTNESS_CONFIGURATION);
+            mHandler.sendEmptyMessage(MSG_LOAD_BRIGHTNESS_CONFIGURATIONS);
         }
 
         @Override
@@ -3285,6 +3404,9 @@
 
             synchronized (mSyncRoot) {
                 final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
+                if (display == null) {
+                    return null;
+                }
                 final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
                 if (device == null) {
                     return null;
@@ -3311,6 +3433,40 @@
             }
             return config.getRefreshRateLimitations();
         }
+
+        @Override
+        public IBinder getWindowTokenClientToMirror(int displayId) {
+            final DisplayDevice device;
+            synchronized (mSyncRoot) {
+                device = getDeviceForDisplayLocked(displayId);
+                if (device == null) {
+                    return null;
+                }
+            }
+            return device.getWindowTokenClientToMirrorLocked();
+        }
+
+        @Override
+        public void setWindowTokenClientToMirror(int displayId, IBinder windowToken) {
+            synchronized (mSyncRoot) {
+                final DisplayDevice device = getDeviceForDisplayLocked(displayId);
+                if (device != null) {
+                    device.setWindowTokenClientToMirrorLocked(windowToken);
+                }
+            }
+        }
+
+        @Override
+        public Point getDisplaySurfaceDefaultSize(int displayId) {
+            final DisplayDevice device;
+            synchronized (mSyncRoot) {
+                device = getDeviceForDisplayLocked(displayId);
+                if (device == null) {
+                    return null;
+                }
+            }
+            return device.getDisplaySurfaceDefaultSize();
+        }
     }
 
     class DesiredDisplayModeSpecsObserver
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index 48edb73..9412c93 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -58,6 +58,8 @@
                 return setDisplayModeDirectorLoggingEnabled(false);
             case "dwb-set-cct":
                 return setAmbientColorTemperatureOverride();
+            case "constrain-launcher-metrics":
+                return setConstrainLauncherMetrics();
             default:
                 return handleDefaultCommands(cmd);
         }
@@ -88,6 +90,9 @@
         pw.println("    Disable display mode director logging.");
         pw.println("  dwb-set-cct CCT");
         pw.println("    Sets the ambient color temperature override to CCT (use -1 to disable).");
+        pw.println("  constrain-launcher-metrics [true|false]");
+        pw.println("    Sets if Display#getRealSize and getRealMetrics should be constrained for ");
+        pw.println("    Launcher.");
         pw.println();
         Intent.printIntentArgsHelp(pw , "");
     }
@@ -115,7 +120,7 @@
     }
 
     private int resetBrightnessConfiguration() {
-        mService.resetBrightnessConfiguration();
+        mService.resetBrightnessConfigurations();
         return 0;
     }
 
@@ -150,4 +155,15 @@
         mService.setAmbientColorTemperatureOverride(cct);
         return 0;
     }
+
+    private int setConstrainLauncherMetrics() {
+        String constrainText = getNextArg();
+        if (constrainText == null) {
+            getErrPrintWriter().println("Error: no value specified");
+            return 1;
+        }
+        boolean constrain = Boolean.parseBoolean(constrainText);
+        mService.setShouldConstrainMetricsForLauncher(constrain);
+        return 0;
+    }
 }
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index f23ae6e..d66d7ee 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -52,6 +52,7 @@
 import android.view.DisplayInfo;
 
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
 import com.android.server.LocalServices;
@@ -153,7 +154,7 @@
                 updateVoteLocked(displayId, priority, vote);
             }
         };
-        mSensorObserver = new SensorObserver(context, ballotBox);
+        mSensorObserver = new SensorObserver(context, ballotBox, injector);
         mHbmObserver = new HbmObserver(injector, ballotBox, BackgroundThread.getHandler());
         mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
         mDeviceConfig = injector.getDeviceConfig();
@@ -2127,27 +2128,36 @@
         }
     }
 
-    private static class SensorObserver implements ProximityActiveListener {
-        private static final String PROXIMITY_SENSOR_NAME = null;
-        private static final String PROXIMITY_SENSOR_TYPE = Sensor.STRING_TYPE_PROXIMITY;
+    private static final class SensorObserver implements ProximityActiveListener,
+            DisplayManager.DisplayListener {
+        private final String mProximitySensorName = null;
+        private final String mProximitySensorType = Sensor.STRING_TYPE_PROXIMITY;
 
         private final BallotBox mBallotBox;
         private final Context mContext;
+        private final Injector mInjector;
+        @GuardedBy("mSensorObserverLock")
+        private final SparseBooleanArray mDozeStateByDisplay = new SparseBooleanArray();
+        private final Object mSensorObserverLock = new Object();
 
         private DisplayManager mDisplayManager;
         private DisplayManagerInternal mDisplayManagerInternal;
+        @GuardedBy("mSensorObserverLock")
         private boolean mIsProxActive = false;
 
-        SensorObserver(Context context, BallotBox ballotBox) {
+        SensorObserver(Context context, BallotBox ballotBox, Injector injector) {
             mContext = context;
             mBallotBox = ballotBox;
+            mInjector = injector;
         }
 
         @Override
         public void onProximityActive(boolean isActive) {
-            if (mIsProxActive != isActive) {
-                mIsProxActive = isActive;
-                recalculateVotes();
+            synchronized (mSensorObserverLock) {
+                if (mIsProxActive != isActive) {
+                    mIsProxActive = isActive;
+                    recalculateVotesLocked();
+                }
             }
         }
 
@@ -2158,17 +2168,27 @@
             final SensorManagerInternal sensorManager =
                     LocalServices.getService(SensorManagerInternal.class);
             sensorManager.addProximityActiveListener(BackgroundThread.getExecutor(), this);
+
+            synchronized (mSensorObserverLock) {
+                for (Display d : mDisplayManager.getDisplays()) {
+                    mDozeStateByDisplay.put(d.getDisplayId(), mInjector.isDozeState(d));
+                }
+            }
+            mInjector.registerDisplayListener(this, BackgroundThread.getHandler(),
+                    DisplayManager.EVENT_FLAG_DISPLAY_ADDED
+                            | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
+                            | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
         }
 
-        private void recalculateVotes() {
+        private void recalculateVotesLocked() {
             final Display[] displays = mDisplayManager.getDisplays();
             for (Display d : displays) {
                 int displayId = d.getDisplayId();
                 Vote vote = null;
-                if (mIsProxActive) {
+                if (mIsProxActive && !mDozeStateByDisplay.get(displayId)) {
                     final RefreshRateRange rate =
                             mDisplayManagerInternal.getRefreshRateForDisplayAndSensor(
-                                    displayId, PROXIMITY_SENSOR_NAME, PROXIMITY_SENSOR_TYPE);
+                                    displayId, mProximitySensorName, mProximitySensorType);
                     if (rate != null) {
                         vote = Vote.forRefreshRates(rate.min, rate.max);
                     }
@@ -2179,7 +2199,44 @@
 
         void dumpLocked(PrintWriter pw) {
             pw.println("  SensorObserver");
-            pw.println("    mIsProxActive=" + mIsProxActive);
+            synchronized (mSensorObserverLock) {
+                pw.println("    mIsProxActive=" + mIsProxActive);
+                pw.println("    mDozeStateByDisplay:");
+                for (int i = 0; i < mDozeStateByDisplay.size(); i++) {
+                    final int id = mDozeStateByDisplay.keyAt(i);
+                    final boolean dozed = mDozeStateByDisplay.valueAt(i);
+                    pw.println("      " + id + " -> " + dozed);
+                }
+            }
+        }
+
+        @Override
+        public void onDisplayAdded(int displayId) {
+            boolean isDozeState = mInjector.isDozeState(mDisplayManager.getDisplay(displayId));
+            synchronized (mSensorObserverLock) {
+                mDozeStateByDisplay.put(displayId, isDozeState);
+                recalculateVotesLocked();
+            }
+        }
+
+        @Override
+        public void onDisplayChanged(int displayId) {
+            boolean wasDozeState = mDozeStateByDisplay.get(displayId);
+            synchronized (mSensorObserverLock) {
+                mDozeStateByDisplay.put(displayId,
+                        mInjector.isDozeState(mDisplayManager.getDisplay(displayId)));
+                if (wasDozeState != mDozeStateByDisplay.get(displayId)) {
+                    recalculateVotesLocked();
+                }
+            }
+        }
+
+        @Override
+        public void onDisplayRemoved(int displayId) {
+            synchronized (mSensorObserverLock) {
+                mDozeStateByDisplay.delete(displayId);
+                recalculateVotesLocked();
+            }
         }
     }
 
@@ -2411,6 +2468,8 @@
                 Handler handler, long flags);
 
         BrightnessInfo getBrightnessInfo(int displayId);
+
+        boolean isDozeState(Display d);
     }
 
     @VisibleForTesting
@@ -2463,6 +2522,14 @@
             return null;
         }
 
+        @Override
+        public boolean isDozeState(Display d) {
+            if (d == null) {
+                return false;
+            }
+            return Display.isDozeState(d.getState());
+        }
+
         private DisplayManager getDisplayManager() {
             if (mDisplayManager == null) {
                 mDisplayManager = mContext.getSystemService(DisplayManager.class);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 59f536c..abbe13a 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -2122,7 +2122,6 @@
 
     private void ignoreProximitySensorUntilChangedInternal() {
         if (!mIgnoreProximityUntilChanged
-                && mPowerRequest.useProximitySensor
                 && mProximity == PROXIMITY_POSITIVE) {
             // Only ignore if it is still reporting positive (near)
             mIgnoreProximityUntilChanged = true;
@@ -2342,7 +2341,7 @@
             try {
                 // TODO(brightnessfloat): change BatteryStats to use float
                 mBatteryStats.noteScreenBrightness(BrightnessSynchronizer.brightnessFloatToInt(
-                        brightness, null));
+                        brightness));
             } catch (RemoteException e) {
                 // same process
             }
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index 6af1923..147050c 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -411,7 +411,7 @@
      * Updates the state of the screen and backlight asynchronously on a separate thread.
      */
     private final class PhotonicModulator extends Thread {
-        private static final int INITIAL_SCREEN_STATE = Display.STATE_OFF; // unknown, assume off
+        private static final int INITIAL_SCREEN_STATE = Display.STATE_UNKNOWN;
         private static final float INITIAL_BACKLIGHT_FLOAT = PowerManager.BRIGHTNESS_INVALID_FLOAT;
 
         private final Object mLock = new Object();
@@ -494,7 +494,9 @@
                     if (!backlightChanged) {
                         mBacklightChangeInProgress = false;
                     }
-                    if (!stateChanged && !backlightChanged) {
+                    boolean valid = state != Display.STATE_UNKNOWN && !Float.isNaN(brightnessState);
+                    boolean changed = stateChanged || backlightChanged;
+                    if (!valid || !changed) {
                         try {
                             mLock.wait();
                         } catch (InterruptedException ex) {
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index ff143bf..1769712 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -795,12 +795,11 @@
                             mBacklightAdapter.setBacklight(sdrBacklight, sdrNits, backlight, nits);
                             Trace.traceCounter(Trace.TRACE_TAG_POWER,
                                     "ScreenBrightness",
-                                    BrightnessSynchronizer.brightnessFloatToInt(
-                                            brightnessState, null));
+                                    BrightnessSynchronizer.brightnessFloatToInt(brightnessState));
                             Trace.traceCounter(Trace.TRACE_TAG_POWER,
                                     "SdrScreenBrightness",
                                     BrightnessSynchronizer.brightnessFloatToInt(
-                                            sdrBrightnessState, null));
+                                            sdrBrightnessState));
                         } finally {
                             Trace.traceEnd(Trace.TRACE_TAG_POWER);
                         }
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 5186744..86c9ca9 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -233,6 +233,8 @@
                 info.physicalXDpi = mOverrideDisplayInfo.physicalXDpi;
                 info.physicalYDpi = mOverrideDisplayInfo.physicalYDpi;
                 info.roundedCorners = mOverrideDisplayInfo.roundedCorners;
+                info.shouldConstrainMetricsForLauncher =
+                        mOverrideDisplayInfo.shouldConstrainMetricsForLauncher;
             }
             mInfo.set(info);
         }
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 4c9a2d7..a931718 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -195,6 +195,9 @@
     }
 
     public LogicalDisplay getDisplayLocked(DisplayDevice device) {
+        if (device == null) {
+            return null;
+        }
         final int count = mLogicalDisplays.size();
         for (int i = 0; i < count; i++) {
             final LogicalDisplay display = mLogicalDisplays.valueAt(i);
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index c90ddf4..4b0d43b 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -63,6 +63,15 @@
  *      &lt;display unique-id="XXXXXXX">
  *          &lt;color-mode>0&lt;/color-mode>
  *          &lt;brightness-value>0&lt;/brightness-value>
+ *          &lt;brightness-configurations>
+ *              &lt;brightness-configuration user-serial="0" package-name="com.example"
+ *              timestamp="1234">
+ *                  &lt;brightness-curve description="some text">
+ *                      &lt;brightness-point lux="0" nits="13.25"/>
+ *                      &lt;brightness-point lux="20" nits="35.94"/>
+ *                  &lt;/brightness-curve>
+ *              &lt;/brightness-configuration>
+ *          &lt;/brightness-configurations>
  *      &lt;/display>
  *  &lt;/display-states>
  *  &lt;stable-device-values>
@@ -120,7 +129,8 @@
     private final StableDeviceValues mStableDeviceValues = new StableDeviceValues();
 
     // Brightness configuration by user
-    private BrightnessConfigurations mBrightnessConfigurations = new BrightnessConfigurations();
+    private BrightnessConfigurations mGlobalBrightnessConfigurations =
+            new BrightnessConfigurations();
 
     // True if the data has been loaded.
     private boolean mLoaded;
@@ -293,18 +303,44 @@
         }
     }
 
+    // Used for testing & reset
     public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userSerial,
             @Nullable String packageName) {
         loadIfNeeded();
-        if (mBrightnessConfigurations.setBrightnessConfigurationForUser(c, userSerial,
+        if (mGlobalBrightnessConfigurations.setBrightnessConfigurationForUser(c, userSerial,
                 packageName)) {
+
             setDirty();
         }
     }
 
+    public boolean setBrightnessConfigurationForDisplayLocked(BrightnessConfiguration configuration,
+            DisplayDevice device, int userSerial, String packageName) {
+        if (device == null || !device.hasStableUniqueId()) {
+            return false;
+        }
+        DisplayState state = getDisplayState(device.getUniqueId(), /*createIfAbsent*/ true);
+        if (state.setBrightnessConfiguration(configuration, userSerial, packageName)) {
+            setDirty();
+            return true;
+        }
+        return false;
+    }
+
+
+    public BrightnessConfiguration getBrightnessConfigurationForDisplayLocked(
+            String uniqueDisplayId, int userSerial) {
+        loadIfNeeded();
+        DisplayState state = mDisplayStates.get(uniqueDisplayId);
+        if (state != null) {
+            return state.getBrightnessConfiguration(userSerial);
+        }
+        return null;
+    }
+
     public BrightnessConfiguration getBrightnessConfiguration(int userSerial) {
         loadIfNeeded();
-        return mBrightnessConfigurations.getBrightnessConfiguration(userSerial);
+        return mGlobalBrightnessConfigurations.getBrightnessConfiguration(userSerial);
     }
 
     private DisplayState getDisplayState(String uniqueId, boolean createIfAbsent) {
@@ -391,7 +427,7 @@
                 mStableDeviceValues.loadFromXml(parser);
             }
             if (parser.getName().equals(TAG_BRIGHTNESS_CONFIGURATIONS)) {
-                mBrightnessConfigurations.loadFromXml(parser);
+                mGlobalBrightnessConfigurations.loadFromXml(parser);
             }
         }
     }
@@ -470,7 +506,7 @@
         mStableDeviceValues.saveToXml(serializer);
         serializer.endTag(null, TAG_STABLE_DEVICE_VALUES);
         serializer.startTag(null, TAG_BRIGHTNESS_CONFIGURATIONS);
-        mBrightnessConfigurations.saveToXml(serializer);
+        mGlobalBrightnessConfigurations.saveToXml(serializer);
         serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATIONS);
         serializer.endTag(null, TAG_DISPLAY_MANAGER_STATE);
         serializer.endDocument();
@@ -493,14 +529,18 @@
         }
         pw.println("  StableDeviceValues:");
         mStableDeviceValues.dump(pw, "      ");
-        pw.println("  BrightnessConfigurations:");
-        mBrightnessConfigurations.dump(pw, "      ");
+        pw.println("  GlobalBrightnessConfigurations:");
+        mGlobalBrightnessConfigurations.dump(pw, "      ");
     }
 
     private static final class DisplayState {
         private int mColorMode;
         private float mBrightness;
 
+        // Brightness configuration by user
+        private BrightnessConfigurations mDisplayBrightnessConfigurations =
+                new BrightnessConfigurations();
+
         public boolean setColorMode(int colorMode) {
             if (colorMode == mColorMode) {
                 return false;
@@ -525,6 +565,16 @@
             return mBrightness;
         }
 
+        public boolean setBrightnessConfiguration(BrightnessConfiguration configuration,
+                int userSerial, String packageName) {
+            mDisplayBrightnessConfigurations.setBrightnessConfigurationForUser(
+                    configuration, userSerial, packageName);
+            return true;
+        }
+
+        public BrightnessConfiguration getBrightnessConfiguration(int userSerial) {
+            return mDisplayBrightnessConfigurations.mConfigurations.get(userSerial);
+        }
 
         public void loadFromXml(TypedXmlPullParser parser)
                 throws IOException, XmlPullParserException {
@@ -540,6 +590,9 @@
                         String brightness = parser.nextText();
                         mBrightness = Float.parseFloat(brightness);
                         break;
+                    case TAG_BRIGHTNESS_CONFIGURATIONS:
+                        mDisplayBrightnessConfigurations.loadFromXml(parser);
+                        break;
                 }
             }
         }
@@ -548,15 +601,21 @@
             serializer.startTag(null, TAG_COLOR_MODE);
             serializer.text(Integer.toString(mColorMode));
             serializer.endTag(null, TAG_COLOR_MODE);
+
             serializer.startTag(null, TAG_BRIGHTNESS_VALUE);
             serializer.text(Float.toString(mBrightness));
             serializer.endTag(null, TAG_BRIGHTNESS_VALUE);
 
+            serializer.startTag(null, TAG_BRIGHTNESS_CONFIGURATIONS);
+            mDisplayBrightnessConfigurations.saveToXml(serializer);
+            serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATIONS);
         }
 
         public void dump(final PrintWriter pw, final String prefix) {
             pw.println(prefix + "ColorMode=" + mColorMode);
             pw.println(prefix + "BrightnessValue=" + mBrightness);
+            pw.println(prefix + "DisplayBrightnessConfigurations: ");
+            mDisplayBrightnessConfigurations.dump(pw, prefix);
         }
     }
 
@@ -621,11 +680,11 @@
 
     private static final class BrightnessConfigurations {
         // Maps from a user ID to the users' given brightness configuration
-        private SparseArray<BrightnessConfiguration> mConfigurations;
+        private final SparseArray<BrightnessConfiguration> mConfigurations;
         // Timestamp of time the configuration was set.
-        private SparseLongArray mTimeStamps;
+        private final SparseLongArray mTimeStamps;
         // Package that set the configuration.
-        private SparseArray<String> mPackageNames;
+        private final SparseArray<String> mPackageNames;
 
         public BrightnessConfigurations() {
             mConfigurations = new SparseArray<>();
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index b7931c8..34d2b01 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -31,7 +31,9 @@
 import static com.android.server.display.DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP;
 import static com.android.server.display.DisplayDeviceInfo.FLAG_TRUSTED;
 
+import android.annotation.Nullable;
 import android.content.Context;
+import android.graphics.Point;
 import android.hardware.display.IVirtualDisplayCallback;
 import android.hardware.display.VirtualDisplayConfig;
 import android.media.projection.IMediaProjection;
@@ -231,6 +233,7 @@
         private Display.Mode mMode;
         private boolean mIsDisplayOn;
         private int mDisplayIdToMirror;
+        private IBinder mWindowTokenClientToMirror;
 
         public VirtualDisplayDevice(IBinder displayToken, IBinder appToken,
                 int ownerUid, String ownerPackageName, Surface surface, int flags,
@@ -253,6 +256,7 @@
             mUniqueIndex = uniqueIndex;
             mIsDisplayOn = surface != null;
             mDisplayIdToMirror = virtualDisplayConfig.getDisplayIdToMirror();
+            mWindowTokenClientToMirror = virtualDisplayConfig.getWindowTokenClientToMirror();
         }
 
         @Override
@@ -282,6 +286,26 @@
             return mDisplayIdToMirror;
         }
 
+        @Override
+        @Nullable
+        public IBinder getWindowTokenClientToMirrorLocked() {
+            return mWindowTokenClientToMirror;
+        }
+
+        @Override
+        public void setWindowTokenClientToMirrorLocked(IBinder windowToken) {
+            if (mWindowTokenClientToMirror != windowToken) {
+                mWindowTokenClientToMirror = windowToken;
+                sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
+                sendTraversalRequestLocked();
+            }
+        }
+
+        @Override
+        public Point getDisplaySurfaceDefaultSize() {
+            return mSurface.getDefaultSize();
+        }
+
         @VisibleForTesting
         Surface getSurfaceLocked() {
             return mSurface;
@@ -362,6 +386,7 @@
             pw.println("mDisplayState=" + Display.stateToString(mDisplayState));
             pw.println("mStopped=" + mStopped);
             pw.println("mDisplayIdToMirror=" + mDisplayIdToMirror);
+            pw.println("mWindowTokenClientToMirror=" + mWindowTokenClientToMirror);
         }
 
 
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index 016c5ed..1461675 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -744,7 +744,7 @@
      *
      * @throws SecurityException when it's not...
      */
-    protected final void assertCalledByPackageOwner(@NonNull String packageName) {
+    protected void assertCalledByPackageOwner(@NonNull String packageName) {
         Objects.requireNonNull(packageName);
         final int uid = Binder.getCallingUid();
         final String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 2ac2110..b0b8be2 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -3264,7 +3264,7 @@
      * Interface for the system to handle request from InputMonitors.
      */
     private final class InputMonitorHost extends IInputMonitorHost.Stub {
-        private final IBinder mToken;
+        private IBinder mToken;
 
         InputMonitorHost(IBinder token) {
             mToken = token;
@@ -3272,12 +3272,23 @@
 
         @Override
         public void pilferPointers() {
+            if (mToken == null) {
+                throw new IllegalStateException(
+                        "Illegal call to pilferPointers after InputMonitorHost is disposed.");
+            }
             nativePilferPointers(mPtr, mToken);
         }
 
         @Override
         public void dispose() {
-            nativeRemoveInputChannel(mPtr, mToken);
+            // We do not remove the input monitor here by calling nativeRemoveInputChannel because
+            // it causes a race in InputDispatcher between the removal of the InputChannel through
+            // that call and the InputChannel#dispose call (which causes an FD hangup) from the
+            // client (b/189135695).
+            //
+            // NOTE: This means the client is responsible for properly closing the InputMonitor by
+            // disposing the InputChannel and all its duplicates.
+            mToken = null;
         }
     }
 
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index 9d80b9c..2328dfc 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -116,8 +116,10 @@
      *
      * @param windowToken the window token that is now in control, or {@code null} if no client
      *                   window is in control of the IME.
+     * @param imeParentChanged {@code true} when the window manager thoughts the IME surface parent
+     *                         will end up to change later, or {@code false} otherwise.
      */
-    public abstract void reportImeControl(@Nullable IBinder windowToken);
+    public abstract void reportImeControl(@Nullable IBinder windowToken, boolean imeParentChanged);
 
     /**
      * Destroys the IME surface.
@@ -176,7 +178,8 @@
                 }
 
                 @Override
-                public void reportImeControl(@Nullable IBinder windowToken) {
+                public void reportImeControl(@Nullable IBinder windowToken,
+                        boolean imeParentChanged) {
                 }
 
                 @Override
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 7955ecac..fe6f2da 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -4122,6 +4122,18 @@
         }
     }
 
+    /** Called right after {@link IInputMethod#showSoftInput}. */
+    private void onShowHideSoftInputRequested(boolean show, IBinder requestToken,
+            @SoftInputShowHideReason int reason) {
+        final WindowManagerInternal.ImeTargetInfo info =
+                mWindowManagerInternal.onToggleImeRequested(
+                        show, mCurFocusedWindow, requestToken, mCurTokenDisplayId);
+        mSoftInputShowHideHistory.addEntry(new SoftInputShowHideHistory.Entry(
+                mCurFocusedWindowClient, mCurAttribute, info.focusedWindowName,
+                mCurFocusedWindowSoftInputMode, reason, mInFullscreenMode,
+                info.requestWindowName, info.imeControlTargetName, info.imeLayerTargetName));
+    }
+
     @BinderThread
     private void hideMySoftInput(@NonNull IBinder token, int flags) {
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideMySoftInput");
@@ -4240,18 +4252,11 @@
                     if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".showSoftInput("
                             + args.arg3 + ", " + msg.arg1 + ", " + args.arg2 + ") for reason: "
                             + InputMethodDebug.softInputDisplayReasonToString(reason));
+                    final IBinder token = (IBinder) args.arg3;
                     ((IInputMethod) args.arg1).showSoftInput(
-                            (IBinder) args.arg3, msg.arg1, (ResultReceiver) args.arg2);
-                    mSoftInputShowHideHistory.addEntry(new SoftInputShowHideHistory.Entry(
-                            mCurFocusedWindowClient, mCurAttribute,
-                            mWindowManagerInternal.getWindowName(mCurFocusedWindow),
-                            mCurFocusedWindowSoftInputMode, reason, mInFullscreenMode,
-                            mWindowManagerInternal.getWindowName(
-                                    mShowRequestWindowMap.get(args.arg3)),
-                            mWindowManagerInternal.getImeControlTargetNameForLogging(
-                                    mCurTokenDisplayId),
-                            mWindowManagerInternal.getImeTargetNameForLogging(
-                                    mCurTokenDisplayId)));
+                            token, msg.arg1 /* flags */, (ResultReceiver) args.arg2);
+                    final IBinder requestToken = mShowRequestWindowMap.get(token);
+                    onShowHideSoftInputRequested(true /* show */, requestToken, reason);
                 } catch (RemoteException e) {
                 }
                 args.recycle();
@@ -4263,18 +4268,11 @@
                     if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".hideSoftInput(0, "
                             + args.arg3 + ", " + args.arg2 + ") for reason: "
                             + InputMethodDebug.softInputDisplayReasonToString(reason));
+                    final IBinder token = (IBinder) args.arg3;
                     ((IInputMethod)args.arg1).hideSoftInput(
-                            (IBinder) args.arg3, 0, (ResultReceiver)args.arg2);
-                    mSoftInputShowHideHistory.addEntry(new SoftInputShowHideHistory.Entry(
-                            mCurFocusedWindowClient, mCurAttribute,
-                            mWindowManagerInternal.getWindowName(mCurFocusedWindow),
-                            mCurFocusedWindowSoftInputMode, reason, mInFullscreenMode,
-                            mWindowManagerInternal.getWindowName(
-                                    mHideRequestWindowMap.get(args.arg3)),
-                            mWindowManagerInternal.getImeControlTargetNameForLogging(
-                                    mCurTokenDisplayId),
-                            mWindowManagerInternal.getImeTargetNameForLogging(
-                                    mCurTokenDisplayId)));
+                            token, 0 /* flags */, (ResultReceiver) args.arg2);
+                    final IBinder requestToken = mHideRequestWindowMap.get(token);
+                    onShowHideSoftInputRequested(false /* show */, requestToken, reason);
                 } catch (RemoteException e) {
                 }
                 args.recycle();
@@ -4935,13 +4933,19 @@
         return mInputManagerInternal.transferTouchFocus(sourceInputToken, curHostInputToken);
     }
 
-    private void reportImeControl(@Nullable IBinder windowToken) {
+    private void reportImeControl(@Nullable IBinder windowToken, boolean imeParentChanged) {
         synchronized (mMethodMap) {
             if (mCurFocusedWindow != windowToken) {
                 // mCurPerceptible was set by the focused window, but it is no longer in control,
                 // so we reset mCurPerceptible.
                 mCurPerceptible = true;
             }
+            if (imeParentChanged) {
+                // Hide the IME method menu earlier when the IME surface parent will change in
+                // case seeing the dialog dismiss flickering during the next focused window
+                // starting the input connection.
+                mMenuController.hideInputMethodMenu();
+            }
         }
     }
 
@@ -4999,8 +5003,8 @@
         }
 
         @Override
-        public void reportImeControl(@Nullable IBinder windowToken) {
-            mService.reportImeControl(windowToken);
+        public void reportImeControl(@Nullable IBinder windowToken, boolean imeParentChanged) {
+            mService.reportImeControl(windowToken, imeParentChanged);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index ce195e6..aa4fa7c 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -225,7 +225,8 @@
                         }
 
                         @Override
-                        public void reportImeControl(@Nullable IBinder windowToken) {
+                        public void reportImeControl(@Nullable IBinder windowToken,
+                                boolean imeParentChanged) {
                         }
 
                         @Override
diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java
index bebe6ed..91f14de 100644
--- a/services/core/java/com/android/server/lights/LightsService.java
+++ b/services/core/java/com/android/server/lights/LightsService.java
@@ -293,7 +293,7 @@
                             + ": brightness=" + brightness);
                     return;
                 }
-                int brightnessInt = BrightnessSynchronizer.brightnessFloatToInt(brightness, null);
+                int brightnessInt = BrightnessSynchronizer.brightnessFloatToInt(brightness);
                 int color = brightnessInt & 0x000000ff;
                 color = 0xff000000 | (color << 16) | (color << 8) | color;
                 setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, brightnessMode);
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 db2a43f..2178672 100644
--- a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
@@ -192,9 +192,7 @@
 
     /** Logs a new incoming location for a location provider. */
     public void logProviderReceivedLocations(String provider, int numLocations) {
-        if (D) {
-            addLogEvent(EVENT_PROVIDER_RECEIVE_LOCATION, provider, numLocations);
-        }
+        addLogEvent(EVENT_PROVIDER_RECEIVE_LOCATION, provider, numLocations);
     }
 
     /** Logs a location deliver for a client of a location provider. */
diff --git a/services/core/java/com/android/server/media/MediaServerUtils.java b/services/core/java/com/android/server/media/MediaServerUtils.java
index a4f11b2..5fa2b1c 100644
--- a/services/core/java/com/android/server/media/MediaServerUtils.java
+++ b/services/core/java/com/android/server/media/MediaServerUtils.java
@@ -18,9 +18,6 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.media.AudioAttributes;
-import android.media.AudioManager;
-import android.media.AudioPlaybackConfiguration;
 import android.os.Binder;
 
 import java.io.PrintWriter;
@@ -32,7 +29,7 @@
     /**
      * Verify that caller holds {@link android.Manifest.permission#DUMP}.
      */
-    static boolean checkDumpPermission(Context context, String tag, PrintWriter pw) {
+    public static boolean checkDumpPermission(Context context, String tag, PrintWriter pw) {
         if (context.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                 != PackageManager.PERMISSION_GRANTED) {
             pw.println("Permission Denial: can't dump " + tag + " from from pid="
@@ -43,18 +40,4 @@
             return true;
         }
     }
-
-    /**
-     * Whether the given stream is currently active or not.
-     */
-    static boolean isStreamActive(AudioManager audioManager, int stream) {
-        for (AudioPlaybackConfiguration configuration
-                : audioManager.getActivePlaybackConfigurations()) {
-            AudioAttributes attributes = configuration.getAudioAttributes();
-            if (attributes != null && attributes.getVolumeControlStream() == stream) {
-                return configuration.isActive();
-            }
-        }
-        return false;
-    }
 }
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index abcf4fb..1525cd4 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -24,6 +24,7 @@
 import android.content.pm.ParceledListSlice;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
+import android.media.AudioSystem;
 import android.media.MediaMetadata;
 import android.media.Rating;
 import android.media.VolumeProvider;
@@ -515,7 +516,7 @@
             public void run() {
                 try {
                     if (useSuggested) {
-                        if (MediaServerUtils.isStreamActive(mAudioManager, stream)) {
+                        if (AudioSystem.isStreamActive(stream, 0)) {
                             mAudioManager.adjustSuggestedStreamVolumeForUid(stream,
                                     direction, flags, opPackageName, uid, pid,
                                     mContext.getApplicationInfo().targetSdkVersion);
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 491cd18..b477ea3 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -42,6 +42,7 @@
 import android.content.pm.PackageManager;
 import android.media.AudioManager;
 import android.media.AudioPlaybackConfiguration;
+import android.media.AudioSystem;
 import android.media.IRemoteSessionCallback;
 import android.media.MediaCommunicationManager;
 import android.media.Session2Token;
@@ -2147,7 +2148,7 @@
 
             boolean preferSuggestedStream = false;
             if (isValidLocalStreamType(suggestedStream)
-                    && MediaServerUtils.isStreamActive(mAudioManager, suggestedStream)) {
+                    && AudioSystem.isStreamActive(suggestedStream, 0)) {
                 preferSuggestedStream = true;
             }
             if (session == null || preferSuggestedStream) {
@@ -2156,8 +2157,7 @@
                             + ". flags=" + flags + ", preferSuggestedStream="
                             + preferSuggestedStream + ", session=" + session);
                 }
-                if (musicOnly && !MediaServerUtils.isStreamActive(mAudioManager,
-                        AudioManager.STREAM_MUSIC)) {
+                if (musicOnly && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
                     if (DEBUG_KEY_EVENT) {
                         Log.d(TAG, "Nothing is playing on the music stream. Skipping volume event,"
                                 + " flags=" + flags);
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index 50eed19..c4c21df 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -325,7 +325,8 @@
         int size = records.size();
         for (int i = 0; i < size; i++) {
             MediaSessionRecord record = records.get(i);
-            if (record.checkPlaybackActiveState(true)) {
+            // Do not send the volume key events to remote sessions.
+            if (record.checkPlaybackActiveState(true) && record.isPlaybackTypeLocal()) {
                 mCachedVolumeDefault = record;
                 return record;
             }
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 4ee867b..097b071 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -289,8 +289,7 @@
     private String mActiveIface;
 
     /** Set of any ifaces associated with mobile networks since boot. */
-    @GuardedBy("mStatsLock")
-    private String[] mMobileIfaces = new String[0];
+    private volatile String[] mMobileIfaces = new String[0];
 
     /** Set of all ifaces currently used by traffic that does not explicitly specify a Network. */
     @GuardedBy("mStatsLock")
@@ -935,7 +934,12 @@
 
     @Override
     public String[] getMobileIfaces() {
-        return mMobileIfaces;
+        // TODO (b/192758557): Remove debug log.
+        if (ArrayUtils.contains(mMobileIfaces, null)) {
+            throw new NullPointerException(
+                    "null element in mMobileIfaces: " + Arrays.toString(mMobileIfaces));
+        }
+        return mMobileIfaces.clone();
     }
 
     @Override
@@ -1084,7 +1088,8 @@
     }
 
     @Override
-    public long getIfaceStats(String iface, int type) {
+    public long getIfaceStats(@NonNull String iface, int type) {
+        Objects.requireNonNull(iface);
         long nativeIfaceStats = nativeGetIfaceStat(iface, type, checkBpfStatsEnable());
         if (nativeIfaceStats == -1) {
             return nativeIfaceStats;
@@ -1382,7 +1387,12 @@
             }
         }
 
-        mMobileIfaces = mobileIfaces.toArray(new String[mobileIfaces.size()]);
+        mMobileIfaces = mobileIfaces.toArray(new String[0]);
+        // TODO (b/192758557): Remove debug log.
+        if (ArrayUtils.contains(mMobileIfaces, null)) {
+            throw new NullPointerException(
+                    "null element in mMobileIfaces: " + Arrays.toString(mMobileIfaces));
+        }
     }
 
     private static int getSubIdForMobile(@NonNull NetworkStateSnapshot state) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index b54e8f9..78c909d 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -4363,8 +4363,7 @@
                             final int userId = r.getSbn().getUserId();
                             if (userId != info.userid && userId != UserHandle.USER_ALL &&
                                     !mUserProfiles.isCurrentProfile(userId)) {
-                                throw new SecurityException("Disallowed call from listener: "
-                                        + info.service);
+                                continue;
                             }
                             cancelNotificationFromListenerLocked(info, callingUid, callingPid,
                                     r.getSbn().getPackageName(), r.getSbn().getTag(),
@@ -4431,8 +4430,7 @@
                         final int userId = r.getSbn().getUserId();
                         if (userId != info.userid && userId != UserHandle.USER_ALL
                                 && !mUserProfiles.isCurrentProfile(userId)) {
-                            throw new SecurityException("Disallowed call from listener: "
-                                    + info.service);
+                            continue;
                         }
                         seen.add(r);
                         if (!r.isSeen()) {
@@ -7451,15 +7449,21 @@
                         sentAccessibilityEvent = true;
                     }
                     if (DBG) Slog.v(TAG, "Interrupting!");
+                    boolean isInsistentUpdate = isInsistentUpdate(record);
                     if (hasValidSound) {
-                        if (isInCall()) {
-                            playInCallNotification();
+                        if (isInsistentUpdate) {
+                            // don't reset insistent sound, it's jarring
                             beep = true;
                         } else {
-                            beep = playSound(record, soundUri);
-                        }
-                        if(beep) {
-                            mSoundNotificationKey = key;
+                            if (isInCall()) {
+                                playInCallNotification();
+                                beep = true;
+                            } else {
+                                beep = playSound(record, soundUri);
+                            }
+                            if (beep) {
+                                mSoundNotificationKey = key;
+                            }
                         }
                     }
 
@@ -7467,9 +7471,13 @@
                             mAudioManager.getRingerModeInternal()
                                     == AudioManager.RINGER_MODE_SILENT;
                     if (!isInCall() && hasValidVibrate && !ringerModeSilent) {
-                        buzz = playVibration(record, vibration, hasValidSound);
-                        if(buzz) {
-                            mVibrateNotificationKey = key;
+                        if (isInsistentUpdate) {
+                            buzz = true;
+                        } else {
+                            buzz = playVibration(record, vibration, hasValidSound);
+                            if (buzz) {
+                                mVibrateNotificationKey = key;
+                            }
                         }
                     }
                 } else if ((record.getFlags() & Notification.FLAG_INSISTENT) != 0) {
@@ -7573,6 +7581,19 @@
     }
 
     @GuardedBy("mNotificationLock")
+    boolean isInsistentUpdate(final NotificationRecord record) {
+        return (Objects.equals(record.getKey(), mSoundNotificationKey)
+                || Objects.equals(record.getKey(), mVibrateNotificationKey))
+                && isCurrentlyInsistent();
+    }
+
+    @GuardedBy("mNotificationLock")
+    boolean isCurrentlyInsistent() {
+        return isLoopingRingtoneNotification(mNotificationsByKey.get(mSoundNotificationKey))
+                || isLoopingRingtoneNotification(mNotificationsByKey.get(mVibrateNotificationKey));
+    }
+
+    @GuardedBy("mNotificationLock")
     boolean shouldMuteNotificationLocked(final NotificationRecord record) {
         // Suppressed because it's a silent update
         final Notification notification = record.getNotification();
@@ -7611,10 +7632,8 @@
             return true;
         }
 
-        // A looping ringtone, such as an incoming call is playing
-        if (isLoopingRingtoneNotification(mNotificationsByKey.get(mSoundNotificationKey))
-                || isLoopingRingtoneNotification(
-                        mNotificationsByKey.get(mVibrateNotificationKey))) {
+        // A different looping ringtone, such as an incoming call is playing
+        if (isCurrentlyInsistent() && !isInsistentUpdate(record)) {
             return true;
         }
 
@@ -8758,10 +8777,22 @@
 
     void snoozeNotificationInt(String key, long duration, String snoozeCriterionId,
             ManagedServiceInfo listener) {
-        String listenerName = listener == null ? null : listener.component.toShortString();
+        if (listener == null) {
+            return;
+        }
+        String listenerName = listener.component.toShortString();
         if ((duration <= 0 && snoozeCriterionId == null) || key == null) {
             return;
         }
+        synchronized (mNotificationLock) {
+            final NotificationRecord r = findInCurrentAndSnoozedNotificationByKeyLocked(key);
+            if (r == null) {
+                return;
+            }
+            if (!listener.enabledAndUserMatches(r.getSbn().getNormalizedUserId())){
+                return;
+            }
+        }
 
         if (DBG) {
             Slog.d(TAG, String.format("snooze event(%s, %d, %s, %s)", key, duration,
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 06ff691..ed9b539 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -1287,20 +1287,6 @@
                 }
             }
 
-            // This package isn't technically installed and won't be written to settings, so we can
-            // treat it as filtered until it's available again.
-            final AndroidPackage targetPkg = targetPkgSetting.pkg;
-            if (targetPkg == null) {
-                if (DEBUG_LOGGING) {
-                    Slog.wtf(TAG, "shouldFilterApplication: " + "targetPkg is null");
-                }
-                return true;
-            }
-            if (targetPkg.isStaticSharedLibrary()) {
-                // not an app, this filtering takes place at a higher level
-                return false;
-            }
-            final String targetName = targetPkg.getPackageName();
             if (DEBUG_TRACING) {
                 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "getAppId");
             }
@@ -1343,6 +1329,21 @@
                     Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                 }
             }
+
+            // This package isn't technically installed and won't be written to settings, so we can
+            // treat it as filtered until it's available again.
+            final AndroidPackage targetPkg = targetPkgSetting.pkg;
+            if (targetPkg == null) {
+                if (DEBUG_LOGGING) {
+                    Slog.wtf(TAG, "shouldFilterApplication: " + "targetPkg is null");
+                }
+                return true;
+            }
+            if (targetPkg.isStaticSharedLibrary()) {
+                // not an app, this filtering takes place at a higher level
+                return false;
+            }
+
             try {
                 if (DEBUG_TRACING) {
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mForceQueryable");
@@ -1415,6 +1416,7 @@
                 if (DEBUG_TRACING) {
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mOverlayReferenceMapper");
                 }
+                final String targetName = targetPkg.getPackageName();
                 if (callingSharedPkgSettings != null) {
                     int size = callingSharedPkgSettings.size();
                     for (int index = 0; index < size; index++) {
diff --git a/services/core/java/com/android/server/pm/IncrementalStates.java b/services/core/java/com/android/server/pm/IncrementalStates.java
index 7627281..3101ca7 100644
--- a/services/core/java/com/android/server/pm/IncrementalStates.java
+++ b/services/core/java/com/android/server/pm/IncrementalStates.java
@@ -118,12 +118,19 @@
      * @param progress Value between [0, 1].
      */
     public void setProgress(float progress) {
+        final boolean oldLoadingState;
         final boolean newLoadingState;
         synchronized (mLock) {
-            updateProgressLocked(progress);
+            oldLoadingState = mLoadingState.isLoading();
+            if (oldLoadingState) {
+                // Due to asynchronous progress reporting, incomplete progress might be received
+                // after the app is migrated off incremental. Ignore such progress updates.
+                updateProgressLocked(progress);
+            }
             newLoadingState = mLoadingState.isLoading();
         }
-        if (!newLoadingState) {
+        if (oldLoadingState && !newLoadingState) {
+            // Only report the state change when loading state changes from true to false
             onLoadingStateChanged();
         }
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ee44c10..140098d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -7220,6 +7220,7 @@
             t.traceEnd();
 
             mPermissionManager.readLegacyPermissionsTEMP(mSettings.mPermissions);
+            mPermissionManager.readLegacyPermissionStateTEMP();
 
             if (!mOnlyCore && mFirstBoot) {
                 requestCopyPreoptedFiles(mInjector);
@@ -7635,7 +7636,6 @@
                     + ((SystemClock.uptimeMillis()-startTime)/1000f)
                     + " seconds");
 
-            mPermissionManager.readLegacyPermissionStateTEMP();
             // If the build fingerprint has changed since the last time we booted,
             // we need to re-grant app permission to catch any new ones that
             // appear.  This is really a hack, and means that apps can in some
@@ -22528,7 +22528,7 @@
 
         UserManagerInternal umInternal = mInjector.getUserManagerInternal();
         final int flags;
-        if (StorageManager.isUserKeyUnlocked(userId)) {
+        if (umInternal.isUserUnlockingOrUnlocked(userId)) {
             flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
         } else if (umInternal.isUserRunning(userId)) {
             flags = StorageManager.FLAG_STORAGE_DE;
@@ -24838,11 +24838,11 @@
             pw.println("vers,1");
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_VERSION)
-                && packageName == null) {
-            // dump version information for all volumes with installed packages
-            dump(DumpState.DUMP_VERSION, fd, pw, dumpState);
+        // reader
+        if (dumpState.isDumping(DumpState.DUMP_VERSION) && packageName == null) {
+            if (!checkin) {
+                dump(DumpState.DUMP_VERSION, fd, pw, dumpState);
+            }
         }
 
         if (!checkin
@@ -24873,8 +24873,7 @@
             ipw.decreaseIndent();
         }
 
-        if (dumpState.isDumping(DumpState.DUMP_VERIFIERS)
-                && packageName == null) {
+        if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) && packageName == null) {
             final String requiredVerifierPackage = mRequiredVerifierPackage;
             if (!checkin) {
                 if (dumpState.onTitlePrinted()) {
@@ -24895,8 +24894,7 @@
             }
         }
 
-        if (dumpState.isDumping(DumpState.DUMP_DOMAIN_VERIFIER)
-                && packageName == null) {
+        if (dumpState.isDumping(DumpState.DUMP_DOMAIN_VERIFIER) && packageName == null) {
             final DomainVerificationProxy proxy = mDomainVerificationManager.getProxy();
             final ComponentName verifierComponent = proxy.getComponentName();
             if (verifierComponent != null) {
@@ -24923,13 +24921,11 @@
             }
         }
 
-        if (dumpState.isDumping(DumpState.DUMP_LIBS)
-                && packageName == null) {
+        if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) {
             dump(DumpState.DUMP_LIBS, fd, pw, dumpState);
         }
 
-        if (dumpState.isDumping(DumpState.DUMP_FEATURES)
-                && packageName == null) {
+        if (dumpState.isDumping(DumpState.DUMP_FEATURES) && packageName == null) {
             if (dumpState.onTitlePrinted()) {
                 pw.println();
             }
@@ -24939,7 +24935,12 @@
 
             synchronized (mAvailableFeatures) {
                 for (FeatureInfo feat : mAvailableFeatures.values()) {
-                    if (!checkin) {
+                    if (checkin) {
+                        pw.print("feat,");
+                        pw.print(feat.name);
+                        pw.print(",");
+                        pw.println(feat.version);
+                    } else {
                         pw.print("  ");
                         pw.print(feat.name);
                         if (feat.version > 0) {
@@ -24947,73 +24948,55 @@
                             pw.print(feat.version);
                         }
                         pw.println();
-                    } else {
-                        pw.print("feat,");
-                        pw.print(feat.name);
-                        pw.print(",");
-                        pw.println(feat.version);
                     }
                 }
             }
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_ACTIVITY_RESOLVERS)) {
             synchronized (mLock) {
                 mComponentResolver.dumpActivityResolvers(pw, dumpState, packageName);
             }
         }
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_RECEIVER_RESOLVERS)) {
             synchronized (mLock) {
                 mComponentResolver.dumpReceiverResolvers(pw, dumpState, packageName);
             }
         }
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_RESOLVERS)) {
             synchronized (mLock) {
                 mComponentResolver.dumpServiceResolvers(pw, dumpState, packageName);
             }
         }
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_CONTENT_RESOLVERS)) {
             synchronized (mLock) {
                 mComponentResolver.dumpProviderResolvers(pw, dumpState, packageName);
             }
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_PREFERRED)
-                && packageName == null) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED)) {
             dump(DumpState.DUMP_PREFERRED, fd, pw, dumpState);
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)
-                && packageName == null) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)) {
             dump(DumpState.DUMP_PREFERRED_XML, fd, pw, dumpState);
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)
-                && packageName == null) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) {
             dump(DumpState.DUMP_DOMAIN_PREFERRED, fd, pw, dumpState);
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
             mSettings.dumpPermissions(pw, packageName, permissionNames, dumpState);
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
             synchronized (mLock) {
                 mComponentResolver.dumpContentProviders(pw, dumpState, packageName);
             }
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_KEYSETS)) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_KEYSETS)) {
             synchronized (mLock) {
                 mSettings.getKeySetManagerService().dumpLPr(pw, packageName, dumpState);
             }
@@ -25028,15 +25011,11 @@
             }
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_QUERIES)
-                && packageName == null) {
+        if (dumpState.isDumping(DumpState.DUMP_QUERIES)) {
             dump(DumpState.DUMP_QUERIES, fd, pw, dumpState);
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_SHARED_USERS)
-                && packageName == null) {
+        if (dumpState.isDumping(DumpState.DUMP_SHARED_USERS)) {
             // This cannot be moved to ComputerEngine since the set of packages in the
             // SharedUserSetting do not have a copy.
             synchronized (mLock) {
@@ -25044,9 +25023,7 @@
             }
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_CHANGES)
-                && packageName == null) {
+        if (dumpState.isDumping(DumpState.DUMP_CHANGES)) {
             if (dumpState.onTitlePrinted()) pw.println();
             pw.println("Package Changes:");
             synchronized (mLock) {
@@ -25073,9 +25050,7 @@
             }
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_FROZEN)
-                && packageName == null) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_FROZEN) && packageName == null) {
             // XXX should handle packageName != null by dumping only install data that
             // the given package is involved with.
             if (dumpState.onTitlePrinted()) pw.println();
@@ -25096,9 +25071,7 @@
             ipw.decreaseIndent();
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_VOLUMES)
-                && packageName == null) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_VOLUMES) && packageName == null) {
             if (dumpState.onTitlePrinted()) pw.println();
 
             final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
@@ -25117,61 +25090,50 @@
             ipw.decreaseIndent();
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS)
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS)
                 && packageName == null) {
             synchronized (mLock) {
                 mComponentResolver.dumpServicePermissions(pw, dumpState);
             }
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_DEXOPT)
-                && packageName == null) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_DEXOPT)) {
             if (dumpState.onTitlePrinted()) pw.println();
             dump(DumpState.DUMP_DEXOPT, fd, pw, dumpState);
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_COMPILER_STATS)
-                && packageName == null) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_COMPILER_STATS)) {
             if (dumpState.onTitlePrinted()) pw.println();
             dump(DumpState.DUMP_COMPILER_STATS, fd, pw, dumpState);
         }
 
-        if (dumpState.isDumping(DumpState.DUMP_MESSAGES)
-                && packageName == null) {
-            if (!checkin) {
-                if (dumpState.onTitlePrinted()) pw.println();
-                synchronized (mLock) {
-                    mSettings.dumpReadMessagesLPr(pw, dumpState);
-                }
-                pw.println();
-                pw.println("Package warning messages:");
-                dumpCriticalInfo(pw, null);
-            } else {
-                dumpCriticalInfo(pw, "msg,");
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES) && packageName == null) {
+            if (dumpState.onTitlePrinted()) pw.println();
+            synchronized (mLock) {
+                mSettings.dumpReadMessagesLPr(pw, dumpState);
             }
+            pw.println();
+            pw.println("Package warning messages:");
+            dumpCriticalInfo(pw, null);
+        }
+
+        if (checkin && dumpState.isDumping(DumpState.DUMP_MESSAGES)) {
+            dumpCriticalInfo(pw, "msg,");
         }
 
         // PackageInstaller should be called outside of mPackages lock
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_INSTALLS)
-                && packageName == null) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_INSTALLS) && packageName == null) {
             // XXX should handle packageName != null by dumping only install data that
             // the given package is involved with.
             if (dumpState.onTitlePrinted()) pw.println();
             mInstallerService.dump(new IndentingPrintWriter(pw, "  ", 120));
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_APEX)
-                && packageName == null) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_APEX)) {
             mApexManager.dump(pw, packageName);
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_PER_UID_READ_TIMEOUTS)
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_PER_UID_READ_TIMEOUTS)
                 && packageName == null) {
             pw.println();
             pw.println("Per UID read timeouts:");
@@ -25190,9 +25152,7 @@
             }
         }
 
-        if (!checkin
-                && dumpState.isDumping(DumpState.DUMP_SNAPSHOT_STATISTICS)
-                && packageName == null) {
+        if (!checkin && dumpState.isDumping(DumpState.DUMP_SNAPSHOT_STATISTICS)) {
             pw.println("Snapshot statistics");
             if (!mSnapshotEnabled) {
                 pw.println("  Snapshots disabled");
@@ -25435,7 +25395,7 @@
         UserManagerInternal umInternal = mInjector.getUserManagerInternal();
         for (UserInfo user : mUserManager.getUsers(false /* includeDying */)) {
             final int flags;
-            if (StorageManager.isUserKeyUnlocked(user.id)) {
+            if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
                 flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
             } else if (umInternal.isUserRunning(user.id)) {
                 flags = StorageManager.FLAG_STORAGE_DE;
@@ -25775,7 +25735,7 @@
         StorageManagerInternal smInternal = mInjector.getLocalService(StorageManagerInternal.class);
         for (UserInfo user : mUserManager.getUsers(false /*excludeDying*/)) {
             final int flags;
-            if (StorageManager.isUserKeyUnlocked(user.id)) {
+            if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
                 flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
             } else if (umInternal.isUserRunning(user.id)) {
                 flags = StorageManager.FLAG_STORAGE_DE;
@@ -25789,7 +25749,7 @@
                     // Note: this code block is executed with the Installer lock
                     // already held, since it's invoked as a side-effect of
                     // executeBatchLI()
-                    if (StorageManager.isUserKeyUnlocked(user.id)) {
+                    if (umInternal.isUserUnlockingOrUnlocked(user.id)) {
                         // Prepare app data on external storage; currently this is used to
                         // setup any OBB dirs that were created by the installer correctly.
                         int uid = UserHandle.getUid(user.id, UserHandle.getAppId(pkg.getUid()));
@@ -26533,7 +26493,7 @@
     }
 
     boolean readPermissionStateForUser(@UserIdInt int userId) {
-        synchronized (mPackages) {
+        synchronized (mLock) {
             mPermissionManager.writeLegacyPermissionStateTEMP();
             mSettings.readPermissionStateForUserSyncLPr(userId);
             mPermissionManager.readLegacyPermissionStateTEMP();
@@ -26609,7 +26569,7 @@
         if (packageName == null || alias == null) {
             return null;
         }
-        synchronized(mLock) {
+        synchronized (mLock) {
             final AndroidPackage pkg = mPackages.get(packageName);
             if (pkg == null
                     || shouldFilterApplicationLocked(getPackageSetting(pkg.getPackageName()),
@@ -26656,7 +26616,7 @@
         if (packageName == null || ks == null) {
             return false;
         }
-        synchronized(mLock) {
+        synchronized (mLock) {
             final AndroidPackage pkg = mPackages.get(packageName);
             if (pkg == null
                     || shouldFilterApplicationLocked(getPackageSetting(pkg.getPackageName()),
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index bf82bd8..88dd033 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -176,6 +176,10 @@
         name = orig.name;
         realName = orig.realName;
         doCopy(orig);
+        // Clone the user states.
+        for (int i = 0; i < mUserState.size(); i++) {
+            mUserState.put(mUserState.keyAt(i), new PackageUserState(mUserState.valueAt(i)));
+        }
     }
 
     public void setInstallerPackageName(String packageName) {
@@ -314,6 +318,7 @@
 
     void setInstalled(boolean inst, int userId) {
         modifyUserState(userId).installed = inst;
+        onChanged();
     }
 
     boolean getInstalled(int userId) {
@@ -326,6 +331,7 @@
 
     void setInstallReason(int installReason, int userId) {
         modifyUserState(userId).installReason = installReason;
+        onChanged();
     }
 
     int getUninstallReason(int userId) {
@@ -334,10 +340,13 @@
 
     void setUninstallReason(@UninstallReason int uninstallReason, int userId) {
         modifyUserState(userId).uninstallReason = uninstallReason;
+        onChanged();
     }
 
     boolean setOverlayPaths(OverlayPaths overlayPaths, int userId) {
-        return modifyUserState(userId).setOverlayPaths(overlayPaths);
+        boolean returnValue = modifyUserState(userId).setOverlayPaths(overlayPaths);
+        onChanged();
+        return returnValue;
     }
 
     OverlayPaths getOverlayPaths(int userId) {
@@ -346,7 +355,10 @@
 
     boolean setOverlayPathsForLibrary(String libName, OverlayPaths overlayPaths,
             int userId) {
-        return modifyUserState(userId).setSharedLibraryOverlayPaths(libName, overlayPaths);
+        boolean returnValue =  modifyUserState(userId)
+                .setSharedLibraryOverlayPaths(libName, overlayPaths);
+        onChanged();
+        return returnValue;
     }
 
     Map<String, OverlayPaths> getOverlayPathsForLibrary(int userId) {
@@ -395,6 +407,7 @@
 
     void setCeDataInode(long ceDataInode, int userId) {
         modifyUserState(userId).ceDataInode = ceDataInode;
+        onChanged();
     }
 
     boolean getStopped(int userId) {
@@ -403,6 +416,7 @@
 
     void setStopped(boolean stop, int userId) {
         modifyUserState(userId).stopped = stop;
+        onChanged();
     }
 
     boolean getNotLaunched(int userId) {
@@ -411,6 +425,7 @@
 
     void setNotLaunched(boolean stop, int userId) {
         modifyUserState(userId).notLaunched = stop;
+        onChanged();
     }
 
     boolean getHidden(int userId) {
@@ -419,6 +434,7 @@
 
     void setHidden(boolean hidden, int userId) {
         modifyUserState(userId).hidden = hidden;
+        onChanged();
     }
 
     int getDistractionFlags(int userId) {
@@ -427,6 +443,7 @@
 
     void setDistractionFlags(int distractionFlags, int userId) {
         modifyUserState(userId).distractionFlags = distractionFlags;
+        onChanged();
     }
 
     boolean getSuspended(int userId) {
@@ -487,6 +504,7 @@
 
     void setInstantApp(boolean instantApp, int userId) {
         modifyUserState(userId).instantApp = instantApp;
+        onChanged();
     }
 
     boolean getVirtulalPreload(int userId) {
@@ -495,6 +513,7 @@
 
     void setVirtualPreload(boolean virtualPreload, int userId) {
         modifyUserState(userId).virtualPreload = virtualPreload;
+        onChanged();
     }
 
     void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped,
@@ -547,20 +566,24 @@
 
     void setEnabledComponents(ArraySet<String> components, int userId) {
         modifyUserState(userId).enabledComponents = components;
+        onChanged();
     }
 
     void setDisabledComponents(ArraySet<String> components, int userId) {
         modifyUserState(userId).disabledComponents = components;
+        onChanged();
     }
 
     void setEnabledComponentsCopy(ArraySet<String> components, int userId) {
         modifyUserState(userId).enabledComponents = components != null
                 ? new ArraySet<String>(components) : null;
+        onChanged();
     }
 
     void setDisabledComponentsCopy(ArraySet<String> components, int userId) {
         modifyUserState(userId).disabledComponents = components != null
                 ? new ArraySet<String>(components) : null;
+        onChanged();
     }
 
     PackageUserState modifyUserStateComponents(int userId, boolean disabled, boolean enabled) {
@@ -582,10 +605,12 @@
 
     void addDisabledComponent(String componentClassName, int userId) {
         modifyUserStateComponents(userId, true, false).disabledComponents.add(componentClassName);
+        onChanged();
     }
 
     void addEnabledComponent(String componentClassName, int userId) {
         modifyUserStateComponents(userId, false, true).enabledComponents.add(componentClassName);
+        onChanged();
     }
 
     boolean enableComponentLPw(String componentClassName, int userId) {
@@ -593,6 +618,9 @@
         boolean changed = state.disabledComponents != null
                 ? state.disabledComponents.remove(componentClassName) : false;
         changed |= state.enabledComponents.add(componentClassName);
+        if (changed) {
+            onChanged();
+        }
         return changed;
     }
 
@@ -601,6 +629,9 @@
         boolean changed = state.enabledComponents != null
                 ? state.enabledComponents.remove(componentClassName) : false;
         changed |= state.disabledComponents.add(componentClassName);
+        if (changed) {
+            onChanged();
+        }
         return changed;
     }
 
@@ -610,6 +641,9 @@
                 ? state.disabledComponents.remove(componentClassName) : false;
         changed |= state.enabledComponents != null
                 ? state.enabledComponents.remove(componentClassName) : false;
+        if (changed) {
+            onChanged();
+        }
         return changed;
     }
 
@@ -701,6 +735,7 @@
     PackageSettingBase setPath(@NonNull File path) {
         this.mPath = path;
         this.mPathString = path.toString();
+        onChanged();
         return this;
     }
 
@@ -722,7 +757,9 @@
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public boolean overrideNonLocalizedLabelAndIcon(@NonNull ComponentName component,
             @Nullable String label, @Nullable Integer icon, @UserIdInt int userId) {
-        return modifyUserState(userId).overrideLabelAndIcon(component, label, icon);
+        boolean returnValue = modifyUserState(userId).overrideLabelAndIcon(component, label, icon);
+        onChanged();
+        return returnValue;
     }
 
     /**
@@ -732,6 +769,7 @@
      */
     public void resetOverrideComponentLabelIcon(@UserIdInt int userId) {
         modifyUserState(userId).resetOverrideComponentLabelIcon();
+        onChanged();
     }
 
     /**
@@ -741,6 +779,7 @@
      */
     public void setSplashScreenTheme(@UserIdInt int userId, @Nullable String themeName) {
         modifyUserState(userId).splashScreenTheme = themeName;
+        onChanged();
     }
 
     /**
@@ -776,6 +815,7 @@
      */
     public void setStatesOnCommit() {
         incrementalStates.onCommit(IncrementalManager.isIncrementalPath(getPathString()));
+        onChanged();
     }
 
     /**
@@ -783,6 +823,7 @@
      */
     public void setIncrementalStatesCallback(IncrementalStates.Callback callback) {
         incrementalStates.setCallback(callback);
+        onChanged();
     }
 
     /**
@@ -791,6 +832,7 @@
      */
     public void setLoadingProgress(float progress) {
         incrementalStates.setProgress(progress);
+        onChanged();
     }
 
     public long getFirstInstallTime() {
diff --git a/services/core/java/com/android/server/pm/PackageSignatures.java b/services/core/java/com/android/server/pm/PackageSignatures.java
index 394cdee..83f54f1 100644
--- a/services/core/java/com/android/server/pm/PackageSignatures.java
+++ b/services/core/java/com/android/server/pm/PackageSignatures.java
@@ -174,7 +174,16 @@
                                 if (index >= 0 && index < readSignatures.size()) {
                                     Signature sig = readSignatures.get(index);
                                     if (sig != null) {
-                                        signatures.add(sig);
+                                        // An app using a shared signature in its signing lineage
+                                        // can have unique capabilities assigned to this previous
+                                        // signer; create a new instance of this Signature to ensure
+                                        // its flags do not overwrite those of the instance from
+                                        // readSignatures.
+                                        if (isPastSigs) {
+                                            signatures.add(new Signature(sig));
+                                        } else {
+                                            signatures.add(sig);
+                                        }
                                         signatureParsed = true;
                                     } else {
                                         PackageManagerService.reportSettingsProblem(Log.WARN,
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index fcbf40e..62d6717 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1664,6 +1664,19 @@
         mContext.enforceCallingPermission(permission, message);
     }
 
+    private void verifyCallerUserId(@UserIdInt int userId) {
+        if (isCallerSystem()) {
+            return; // no check
+        }
+
+        final int callingUid = injectBinderCallingUid();
+
+        // Otherwise, make sure the arguments are valid.
+        if (UserHandle.getUserId(callingUid) != userId) {
+            throw new SecurityException("Invalid user-ID");
+        }
+    }
+
     private void verifyCaller(@NonNull String packageName, @UserIdInt int userId) {
         Preconditions.checkStringNotEmpty(packageName, "packageName");
 
@@ -2847,6 +2860,8 @@
 
     @Override
     public boolean isRequestPinItemSupported(int callingUserId, int requestType) {
+        verifyCallerUserId(callingUserId);
+
         final long token = injectClearCallingIdentity();
         try {
             return mShortcutRequestPinProcessor
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 2a5a721..c11ffb4 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -217,6 +217,8 @@
 
     /** All storage permissions */
     private static final List<String> STORAGE_PERMISSIONS = new ArrayList<>();
+    /** All nearby devices permissions */
+    private static final List<String> NEARBY_DEVICES_PERMISSIONS = new ArrayList<>();
 
     /** If the permission of the value is granted, so is the key */
     private static final Map<String, String> FULLER_PERMISSION_MAP = new HashMap<>();
@@ -233,6 +235,9 @@
         STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE);
         STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
         STORAGE_PERMISSIONS.add(Manifest.permission.ACCESS_MEDIA_LOCATION);
+        NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_ADVERTISE);
+        NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_CONNECT);
+        NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_SCAN);
     }
 
     /** Set of source package names for Privileged Permission Allowlist */
@@ -1210,6 +1215,7 @@
 
     private boolean checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission(
             @NonNull String permName) {
+        final String permissionPackageName;
         final boolean isImmutablyRestrictedPermission;
         synchronized (mLock) {
             final Permission bp = mRegistry.getPermission(permName);
@@ -1217,15 +1223,25 @@
                 Slog.w(TAG, "No such permissions: " + permName);
                 return false;
             }
+            permissionPackageName = bp.getPackageName();
             isImmutablyRestrictedPermission = bp.isHardOrSoftRestricted()
                     && bp.isImmutablyRestricted();
         }
+
+        final int callingUid = getCallingUid();
+        final int callingUserId = UserHandle.getUserId(callingUid);
+        if (mPackageManagerInt.filterAppAccess(permissionPackageName, callingUid, callingUserId)) {
+            EventLog.writeEvent(0x534e4554, "186404356", callingUid, permName);
+            return false;
+        }
+
         if (isImmutablyRestrictedPermission && mContext.checkCallingOrSelfPermission(
                 Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS)
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Cannot modify allowlisting of an immutably "
                     + "restricted permission: " + permName);
         }
+
         return true;
     }
 
@@ -3076,13 +3092,26 @@
                 Permission bp = mRegistry.getPermission(permission);
                 if (bp != null && bp.isRuntime()) {
                     int flags = ps.getPermissionFlags(permission);
-
                     if ((flags & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) {
-
                         int flagsToRemove = FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
 
+                        // We're willing to preserve an implicit "Nearby devices"
+                        // permission grant if this app was already able to interact
+                        // with nearby devices via background location access
+                        boolean preserveGrant = false;
+                        if (ArrayUtils.contains(NEARBY_DEVICES_PERMISSIONS, permission)
+                                && ps.isPermissionGranted(
+                                        android.Manifest.permission.ACCESS_BACKGROUND_LOCATION)
+                                && (ps.getPermissionFlags(
+                                        android.Manifest.permission.ACCESS_BACKGROUND_LOCATION)
+                                        & (FLAG_PERMISSION_REVOKE_WHEN_REQUESTED
+                                                | FLAG_PERMISSION_REVOKED_COMPAT)) == 0) {
+                            preserveGrant = true;
+                        }
+
                         if ((flags & BLOCKING_PERMISSION_FLAGS) == 0
-                                && supportsRuntimePermissions) {
+                                && supportsRuntimePermissions
+                                && !preserveGrant) {
                             if (ps.revokePermission(bp)) {
                                 if (DEBUG_PERMISSIONS) {
                                     Slog.i(TAG, "Revoking runtime permission "
@@ -3347,13 +3376,15 @@
     }
 
     @Override
-    public void registerAttributionSource(@NonNull AttributionSource source) {
-        mAttributionSourceRegistry.registerAttributionSource(source);
+    public void registerAttributionSource(@NonNull AttributionSourceState source) {
+        mAttributionSourceRegistry
+                .registerAttributionSource(new AttributionSource(source));
     }
 
     @Override
-    public boolean isRegisteredAttributionSource(@NonNull AttributionSource source) {
-        return mAttributionSourceRegistry.isRegisteredAttributionSource(source);
+    public boolean isRegisteredAttributionSource(@NonNull AttributionSourceState source) {
+        return mAttributionSourceRegistry
+                .isRegisteredAttributionSource(new AttributionSource(source));
     }
 
     @Override
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index edd5f5f..27a16e9 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -41,6 +41,7 @@
 import com.android.server.devicestate.DeviceStateProvider;
 import com.android.server.policy.devicestate.config.Conditions;
 import com.android.server.policy.devicestate.config.DeviceStateConfig;
+import com.android.server.policy.devicestate.config.Flags;
 import com.android.server.policy.devicestate.config.LidSwitchCondition;
 import com.android.server.policy.devicestate.config.NumericRange;
 import com.android.server.policy.devicestate.config.SensorCondition;
@@ -81,13 +82,14 @@
 public final class DeviceStateProviderImpl implements DeviceStateProvider,
         InputManagerInternal.LidSwitchCallback, SensorEventListener {
     private static final String TAG = "DeviceStateProviderImpl";
+    private static final boolean DEBUG = false;
 
     private static final BooleanSupplier TRUE_BOOLEAN_SUPPLIER = () -> true;
     private static final BooleanSupplier FALSE_BOOLEAN_SUPPLIER = () -> false;
 
     @VisibleForTesting
     static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(MINIMUM_DEVICE_STATE,
-            "DEFAULT");
+            "DEFAULT", 0 /* flags */);
 
     private static final String VENDOR_CONFIG_FILE_PATH = "etc/devicestate/";
     private static final String DATA_CONFIG_FILE_PATH = "system/devicestate/";
@@ -131,7 +133,26 @@
                         config.getDeviceState()) {
                     final int state = stateConfig.getIdentifier().intValue();
                     final String name = stateConfig.getName() == null ? "" : stateConfig.getName();
-                    deviceStateList.add(new DeviceState(state, name));
+
+                    int flags = 0;
+                    final Flags configFlags = stateConfig.getFlags();
+                    if (configFlags != null) {
+                        List<String> configFlagStrings = configFlags.getFlag();
+                        for (int i = 0; i < configFlagStrings.size(); i++) {
+                            final String configFlagString = configFlagStrings.get(i);
+                            switch (configFlagString) {
+                                case "FLAG_CANCEL_STICKY_REQUESTS":
+                                    flags |= DeviceState.FLAG_CANCEL_STICKY_REQUESTS;
+                                    break;
+                                default:
+                                    Slog.w(TAG, "Parsed unknown flag with name: "
+                                            + configFlagString);
+                                    break;
+                            }
+                        }
+                    }
+
+                    deviceStateList.add(new DeviceState(state, name, flags));
 
                     final Conditions condition = stateConfig.getConditions();
                     conditionsList.add(condition);
@@ -193,6 +214,10 @@
 
         for (int i = 0; i < stateConditions.size(); i++) {
             final int state = deviceStates.get(i).getIdentifier();
+            if (DEBUG) {
+                Slog.d(TAG, "Evaluating conditions for device state " + state
+                        + " (" + deviceStates.get(i).getName() + ")");
+            }
             final Conditions conditions = stateConditions.get(i);
             if (conditions == null) {
                 mStateConditions.put(state, TRUE_BOOLEAN_SUPPLIER);
@@ -213,6 +238,9 @@
             if (lidSwitchCondition != null) {
                 suppliers.add(new LidSwitchBooleanSupplier(lidSwitchCondition.getOpen()));
                 lidSwitchRequired = true;
+                if (DEBUG) {
+                    Slog.d(TAG, "Lid switch required");
+                }
             }
 
             List<SensorCondition> sensorConditions = conditions.getSensor();
@@ -229,6 +257,11 @@
                     break;
                 }
 
+                if (DEBUG) {
+                    Slog.d(TAG, "Found sensor with type: " + expectedSensorType
+                            + " (" + expectedSensorName + ")");
+                }
+
                 suppliers.add(new SensorBooleanSupplier(foundSensor, sensorCondition.getValue()));
                 sensorsRequired.add(foundSensor);
             }
@@ -323,6 +356,10 @@
             int newState = mOrderedStates[0].getIdentifier();
             for (int i = 0; i < mOrderedStates.length; i++) {
                 int state = mOrderedStates[i].getIdentifier();
+                if (DEBUG) {
+                    Slog.d(TAG, "Checking conditions for " + mOrderedStates[i].getName() + "("
+                            + i + ")");
+                }
                 boolean conditionSatisfied;
                 try {
                     conditionSatisfied = mStateConditions.get(state).getAsBoolean();
@@ -330,10 +367,16 @@
                     // Failed to compute the current state based on current available data. Return
                     // with the expectation that notifyDeviceStateChangedIfNeeded() will be called
                     // when a callback with the missing data is triggered.
+                    if (DEBUG) {
+                        Slog.d(TAG, "Unable to check current state", e);
+                    }
                     return;
                 }
 
                 if (conditionSatisfied) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Device State conditions satisfied, transition to " + state);
+                    }
                     newState = state;
                     break;
                 }
@@ -355,6 +398,9 @@
         synchronized (mLock) {
             mIsLidOpen = lidOpen;
         }
+        if (DEBUG) {
+            Slog.d(TAG, "Lid switch state: " + (lidOpen ? "open" : "closed"));
+        }
         notifyDeviceStateChangedIfNeeded();
     }
 
@@ -440,6 +486,9 @@
         private boolean adheresToRange(float value, @NonNull NumericRange range) {
             final BigDecimal min = range.getMin_optional();
             if (min != null) {
+                if (DEBUG) {
+                    Slog.d(TAG, "value: " + value + ", constraint min: " + min.floatValue());
+                }
                 if (value <= min.floatValue()) {
                     return false;
                 }
@@ -447,6 +496,10 @@
 
             final BigDecimal minInclusive = range.getMinInclusive_optional();
             if (minInclusive != null) {
+                if (DEBUG) {
+                    Slog.d(TAG, "value: " + value + ", constraint min-inclusive: "
+                            + minInclusive.floatValue());
+                }
                 if (value < minInclusive.floatValue()) {
                     return false;
                 }
@@ -454,6 +507,9 @@
 
             final BigDecimal max = range.getMax_optional();
             if (max != null) {
+                if (DEBUG) {
+                    Slog.d(TAG, "value: " + value + ", constraint max: " + max.floatValue());
+                }
                 if (value >= max.floatValue()) {
                     return false;
                 }
@@ -461,6 +517,10 @@
 
             final BigDecimal maxInclusive = range.getMaxInclusive_optional();
             if (maxInclusive != null) {
+                if (DEBUG) {
+                    Slog.d(TAG, "value: " + value + ", constraint max-inclusive: "
+                            + maxInclusive.floatValue());
+                }
                 if (value > maxInclusive.floatValue()) {
                     return false;
                 }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index d1df989..45f5daa 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -484,6 +484,7 @@
     int mLidNavigationAccessibility;
     int mShortPressOnPowerBehavior;
     int mLongPressOnPowerBehavior;
+    long mLongPressOnPowerAssistantTimeoutMs;
     int mVeryLongPressOnPowerBehavior;
     int mDoublePressOnPowerBehavior;
     int mTriplePressOnPowerBehavior;
@@ -732,6 +733,9 @@
                     Settings.Global.POWER_BUTTON_LONG_PRESS), false, this,
                     UserHandle.USER_ALL);
             resolver.registerContentObserver(Settings.Global.getUriFor(
+                    Settings.Global.POWER_BUTTON_LONG_PRESS_DURATION_MS), false, this,
+                    UserHandle.USER_ALL);
+            resolver.registerContentObserver(Settings.Global.getUriFor(
                     Settings.Global.POWER_BUTTON_VERY_LONG_PRESS), false, this,
                     UserHandle.USER_ALL);
             resolver.registerContentObserver(Settings.Global.getUriFor(
@@ -1732,6 +1736,8 @@
                 com.android.internal.R.integer.config_shortPressOnPowerBehavior);
         mLongPressOnPowerBehavior = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_longPressOnPowerBehavior);
+        mLongPressOnPowerAssistantTimeoutMs = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_longPressOnPowerDurationMs);
         mVeryLongPressOnPowerBehavior = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_veryLongPressOnPowerBehavior);
         mDoublePressOnPowerBehavior = mContext.getResources().getInteger(
@@ -1955,7 +1961,7 @@
      */
     private final class PowerKeyRule extends SingleKeyGestureDetector.SingleKeyRule {
         PowerKeyRule(int gestures) {
-            super(KEYCODE_POWER, gestures);
+            super(mContext, KEYCODE_POWER, gestures);
         }
 
         @Override
@@ -1970,6 +1976,15 @@
         }
 
         @Override
+        long getLongPressTimeoutMs() {
+            if (getResolvedLongPressOnPowerBehavior() == LONG_PRESS_POWER_ASSISTANT) {
+                return mLongPressOnPowerAssistantTimeoutMs;
+            } else {
+                return super.getLongPressTimeoutMs();
+            }
+        }
+
+        @Override
         void onLongPress(long eventTime) {
             if (mSingleKeyGestureDetector.beganFromNonInteractive()
                     && !mSupportLongPressPowerWhenNonInteractive) {
@@ -1997,7 +2012,7 @@
      */
     private final class BackKeyRule extends SingleKeyGestureDetector.SingleKeyRule {
         BackKeyRule(int gestures) {
-            super(KEYCODE_BACK, gestures);
+            super(mContext, KEYCODE_BACK, gestures);
         }
 
         @Override
@@ -2017,7 +2032,7 @@
     }
 
     private void initSingleKeyGestureRules() {
-        mSingleKeyGestureDetector = new SingleKeyGestureDetector(mContext);
+        mSingleKeyGestureDetector = new SingleKeyGestureDetector();
 
         int powerKeyGestures = 0;
         if (hasVeryLongPressOnPowerBehavior()) {
@@ -2115,6 +2130,11 @@
                     Settings.Global.POWER_BUTTON_LONG_PRESS,
                     mContext.getResources().getInteger(
                             com.android.internal.R.integer.config_longPressOnPowerBehavior));
+            mLongPressOnPowerAssistantTimeoutMs = Settings.Global.getLong(
+                    mContext.getContentResolver(),
+                    Settings.Global.POWER_BUTTON_LONG_PRESS_DURATION_MS,
+                    mContext.getResources().getInteger(
+                            com.android.internal.R.integer.config_longPressOnPowerDurationMs));
             mVeryLongPressOnPowerBehavior = Settings.Global.getInt(resolver,
                     Settings.Global.POWER_BUTTON_VERY_LONG_PRESS,
                     mContext.getResources().getInteger(
@@ -3845,9 +3865,13 @@
         }
         mCameraGestureTriggered = false;
         final MutableBoolean outLaunched = new MutableBoolean(false);
-        mGestureLauncherService.interceptPowerKeyDown(event, interactive, outLaunched);
+        final boolean intercept =
+                mGestureLauncherService.interceptPowerKeyDown(event, interactive, outLaunched);
         if (!outLaunched.value) {
-            return false;
+            // If GestureLauncherService intercepted the power key, but didn't launch camera app,
+            // we should still return the intercept result. This prevents the single key gesture
+            // detector from processing the power key later on.
+            return intercept;
         }
         mCameraGestureTriggered = true;
         if (mRequestedOrSleepingDefaultDisplay) {
@@ -5327,6 +5351,9 @@
                 pw.print("mLongPressOnPowerBehavior=");
                 pw.println(longPressOnPowerBehaviorToString(mLongPressOnPowerBehavior));
         pw.print(prefix);
+        pw.print("mLongPressOnPowerAssistantTimeoutMs=");
+        pw.println(mLongPressOnPowerAssistantTimeoutMs);
+        pw.print(prefix);
                 pw.print("mVeryLongPressOnPowerBehavior=");
                 pw.println(veryLongPressOnPowerBehaviorToString(mVeryLongPressOnPowerBehavior));
         pw.print(prefix);
diff --git a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
index 1ef2bf9..6fee69b 100644
--- a/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
+++ b/services/core/java/com/android/server/policy/SingleKeyGestureDetector.java
@@ -44,9 +44,6 @@
     private static final int MSG_KEY_VERY_LONG_PRESS = 1;
     private static final int MSG_KEY_DELAYED_PRESS = 2;
 
-    private final long mLongPressTimeout;
-    private final long mVeryLongPressTimeout;
-
     private volatile int mKeyPressCounter;
     private boolean mBeganFromNonInteractive = false;
 
@@ -86,12 +83,19 @@
      *  </pre>
      */
     abstract static class SingleKeyRule {
+
         private final int mKeyCode;
         private final int mSupportedGestures;
+        private final long mDefaultLongPressTimeout;
+        private final long mDefaultVeryLongPressTimeout;
 
-        SingleKeyRule(int keyCode, @KeyGestureFlag int supportedGestures) {
+        SingleKeyRule(Context context, int keyCode, @KeyGestureFlag int supportedGestures) {
             mKeyCode = keyCode;
             mSupportedGestures = supportedGestures;
+            mDefaultLongPressTimeout =
+                ViewConfiguration.get(context).getDeviceGlobalActionKeyTimeout();
+            mDefaultVeryLongPressTimeout = context.getResources().getInteger(
+                com.android.internal.R.integer.config_veryLongPressTimeout);
         }
 
         /**
@@ -134,10 +138,28 @@
          */
         void onMultiPress(long downTime, int count) {}
         /**
+         *  Returns the timeout in milliseconds for a long press.
+         *
+         *  If multipress is also supported, this should always be greater than the multipress
+         *  timeout. If very long press is supported, this should always be less than the very long
+         *  press timeout.
+         */
+        long getLongPressTimeoutMs() {
+            return mDefaultLongPressTimeout;
+        }
+        /**
          *  Callback when long press has been detected.
          */
         void onLongPress(long eventTime) {}
         /**
+         *  Returns the timeout in milliseconds for a very long press.
+         *
+         *  If long press is supported, this should always be longer than the long press timeout.
+         */
+        long getVeryLongPressTimeoutMs() {
+            return mDefaultVeryLongPressTimeout;
+        }
+        /**
          *  Callback when very long press has been detected.
          */
         void onVeryLongPress(long eventTime) {}
@@ -151,10 +173,7 @@
         }
     }
 
-    public SingleKeyGestureDetector(Context context) {
-        mLongPressTimeout = ViewConfiguration.get(context).getDeviceGlobalActionKeyTimeout();
-        mVeryLongPressTimeout = context.getResources().getInteger(
-                com.android.internal.R.integer.config_veryLongPressTimeout);
+    public SingleKeyGestureDetector() {
         mHandler = new KeyHandler();
     }
 
@@ -225,14 +244,14 @@
                 final Message msg = mHandler.obtainMessage(MSG_KEY_LONG_PRESS, keyCode, 0,
                         eventTime);
                 msg.setAsynchronous(true);
-                mHandler.sendMessageDelayed(msg, mLongPressTimeout);
+                mHandler.sendMessageDelayed(msg, mActiveRule.getLongPressTimeoutMs());
             }
 
             if (mActiveRule.supportVeryLongPress()) {
                 final Message msg = mHandler.obtainMessage(MSG_KEY_VERY_LONG_PRESS, keyCode, 0,
                         eventTime);
                 msg.setAsynchronous(true);
-                mHandler.sendMessageDelayed(msg, mVeryLongPressTimeout);
+                mHandler.sendMessageDelayed(msg, mActiveRule.getVeryLongPressTimeoutMs());
             }
         } else {
             mHandler.removeMessages(MSG_KEY_LONG_PRESS);
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
index 855a1cc..051f555 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
@@ -276,4 +276,4 @@
     public void dump(String prefix, PrintWriter pw) {
         mKeyguardStateMonitor.dump(prefix, pw);
     }
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 7555a7f..c91d8de 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -96,6 +96,7 @@
     private static final int MSG_BROADCAST_ENHANCED_PREDICTION = 4;
     private static final int MSG_PROFILE_TIMED_OUT = 5;
     private static final int MSG_WIRED_CHARGING_STARTED = 6;
+    private static final int MSG_SCREEN_POLICY = 7;
 
     private static final long[] CHARGING_VIBRATION_TIME = {
             40, 40, 40, 40, 40, 40, 40, 40, 40, // ramp-up sampling rate = 40ms
@@ -120,6 +121,7 @@
     private final SuspendBlocker mSuspendBlocker;
     private final WindowManagerPolicy mPolicy;
     private final FaceDownDetector mFaceDownDetector;
+    private final ScreenUndimDetector mScreenUndimDetector;
     private final ActivityManagerInternal mActivityManagerInternal;
     private final InputManagerInternal mInputManagerInternal;
     private final InputMethodManagerInternal mInputMethodManagerInternal;
@@ -167,13 +169,14 @@
 
     public Notifier(Looper looper, Context context, IBatteryStats batteryStats,
             SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
-            FaceDownDetector faceDownDetector) {
+            FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) {
         mContext = context;
         mBatteryStats = batteryStats;
         mAppOps = mContext.getSystemService(AppOpsManager.class);
         mSuspendBlocker = suspendBlocker;
         mPolicy = policy;
         mFaceDownDetector = faceDownDetector;
+        mScreenUndimDetector = screenUndimDetector;
         mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
         mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
         mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class);
@@ -620,6 +623,22 @@
     }
 
     /**
+     * Called when the screen policy changes.
+     */
+    public void onScreenPolicyUpdate(int newPolicy) {
+        if (DEBUG) {
+            Slog.d(TAG, "onScreenPolicyUpdate: newPolicy=" + newPolicy);
+        }
+
+        synchronized (mLock) {
+            Message msg = mHandler.obtainMessage(MSG_SCREEN_POLICY);
+            msg.arg1 = newPolicy;
+            msg.setAsynchronous(true);
+            mHandler.sendMessage(msg);
+        }
+    }
+
+    /**
      * Dumps data for bugreports.
      *
      * @param pw The stream to print to.
@@ -659,6 +678,7 @@
         tm.notifyUserActivity();
         mPolicy.userActivity();
         mFaceDownDetector.userActivity(event);
+        mScreenUndimDetector.userActivity();
     }
 
     void postEnhancedDischargePredictionBroadcast(long delayMs) {
@@ -812,6 +832,10 @@
         mSuspendBlocker.release();
     }
 
+    private void screenPolicyChanging(int screenPolicy) {
+        mScreenUndimDetector.recordScreenPolicy(screenPolicy);
+    }
+
     private void lockProfile(@UserIdInt int userId) {
         mTrustManager.setDeviceLockedForUser(userId, true /*locked*/);
     }
@@ -852,6 +876,9 @@
                 case MSG_WIRED_CHARGING_STARTED:
                     showWiredChargingStarted(msg.arg1);
                     break;
+                case MSG_SCREEN_POLICY:
+                    screenPolicyChanging(msg.arg1);
+                    break;
             }
         }
     }
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 9638255..688a3b2 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -129,6 +129,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.NoSuchElementException;
 import java.util.Objects;
 
 /**
@@ -273,6 +274,7 @@
     private final BatterySavingStats mBatterySavingStats;
     private final AttentionDetector mAttentionDetector;
     private final FaceDownDetector mFaceDownDetector;
+    private final ScreenUndimDetector mScreenUndimDetector;
     private final BinderService mBinderService;
     private final LocalService mLocalService;
     private final NativeWrapper mNativeWrapper;
@@ -532,6 +534,11 @@
     // True if the proximity sensor reads a positive result.
     private boolean mProximityPositive;
 
+    // Indicates that we have already intercepted the power key to temporarily ignore the proximity
+    // wake lock and turn the screen back on. This should get reset when prox reads 'far' again
+    // (when {@link #mProximityPositive} is set to false).
+    private boolean mInterceptedPowerKeyForProximity;
+
     // Screen brightness setting limits.
     public final float mScreenBrightnessMinimum;
     public final float mScreenBrightnessMaximum;
@@ -831,9 +838,10 @@
     static class Injector {
         Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
                 SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
-                FaceDownDetector faceDownDetector) {
+                FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) {
             return new Notifier(
-                    looper, context, batteryStats, suspendBlocker, policy, faceDownDetector);
+                    looper, context, batteryStats, suspendBlocker, policy, faceDownDetector,
+                    screenUndimDetector);
         }
 
         SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) {
@@ -954,6 +962,7 @@
                 mInjector.createAmbientDisplaySuppressionController(context);
         mAttentionDetector = new AttentionDetector(this::onUserAttention, mLock);
         mFaceDownDetector = new FaceDownDetector(this::onFlip);
+        mScreenUndimDetector = new ScreenUndimDetector();
 
         mBatterySavingStats = new BatterySavingStats(mLock);
         mBatterySaverPolicy =
@@ -1140,7 +1149,7 @@
             mBatteryStats = BatteryStatsService.getService();
             mNotifier = mInjector.createNotifier(Looper.getMainLooper(), mContext, mBatteryStats,
                     mInjector.createSuspendBlocker(this, "PowerManagerService.Broadcasts"),
-                    mPolicy, mFaceDownDetector);
+                    mPolicy, mFaceDownDetector, mScreenUndimDetector);
 
             mWirelessChargerDetector = mInjector.createWirelessChargerDetector(sensorManager,
                     mInjector.createSuspendBlocker(
@@ -1175,6 +1184,7 @@
         mBatterySaverController.systemReady();
         mBatterySaverPolicy.systemReady();
         mFaceDownDetector.systemReady(mContext);
+        mScreenUndimDetector.systemReady(mContext);
 
         // Register for settings changes.
         resolver.registerContentObserver(Settings.Secure.getUriFor(
@@ -1490,7 +1500,11 @@
                 mRequestWaitForNegativeProximity = true;
             }
 
-            wakeLock.mLock.unlinkToDeath(wakeLock, 0);
+            try {
+                wakeLock.mLock.unlinkToDeath(wakeLock, 0);
+            } catch (NoSuchElementException e) {
+                Slog.wtf(TAG, "Failed to unlink wakelock", e);
+            }
             removeWakeLockLocked(wakeLock, index);
         }
     }
@@ -3180,6 +3194,7 @@
 
                 final boolean ready = mDisplayManagerInternal.requestPowerState(groupId,
                         displayPowerRequest, mRequestWaitForNegativeProximity);
+                mNotifier.onScreenPolicyUpdate(displayPowerRequest.policy);
 
                 if (DEBUG_SPEW) {
                     Slog.d(TAG, "updateDisplayPowerStateLocked: displayReady=" + ready
@@ -3313,6 +3328,7 @@
         public void onProximityNegative() {
             synchronized (mLock) {
                 mProximityPositive = false;
+                mInterceptedPowerKeyForProximity = false;
                 mDirty |= DIRTY_PROXIMITY_POSITIVE;
                 userActivityNoUpdateLocked(Display.DEFAULT_DISPLAY_GROUP, mClock.uptimeMillis(),
                         PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
@@ -4153,6 +4169,8 @@
             }
             pw.println();
             pw.println("  mRequestWaitForNegativeProximity=" + mRequestWaitForNegativeProximity);
+            pw.println("  mInterceptedPowerKeyForProximity="
+                    + mInterceptedPowerKeyForProximity);
             pw.println("  mSandmanScheduled=" + mSandmanScheduled);
             pw.println("  mBatteryLevelLow=" + mBatteryLevelLow);
             pw.println("  mLightDeviceIdleMode=" + mLightDeviceIdleMode);
@@ -5984,8 +6002,9 @@
             final DisplayPowerRequest displayPowerRequest =
                     mDisplayGroupPowerStateMapper.getPowerRequestLocked(
                             Display.DEFAULT_DISPLAY_GROUP);
-            if (displayPowerRequest.useProximitySensor && mProximityPositive) {
+            if (mProximityPositive && !mInterceptedPowerKeyForProximity) {
                 mDisplayManagerInternal.ignoreProximitySensorUntilChanged();
+                mInterceptedPowerKeyForProximity = true;
                 return true;
             }
         }
diff --git a/services/core/java/com/android/server/power/ScreenUndimDetector.java b/services/core/java/com/android/server/power/ScreenUndimDetector.java
new file mode 100644
index 0000000..951bc1f
--- /dev/null
+++ b/services/core/java/com/android/server/power/ScreenUndimDetector.java
@@ -0,0 +1,297 @@
+/*
+ * 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 com.android.server.power;
+
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DIM;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF;
+import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.provider.DeviceConfig;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Detects when user manually undims the screen (x times) and acquires a wakelock to keep the screen
+ * on temporarily (without changing the screen timeout setting).
+ */
+public class ScreenUndimDetector {
+    private static final String TAG = "ScreenUndimDetector";
+    private static final boolean DEBUG = false;
+
+    private static final String UNDIM_DETECTOR_WAKE_LOCK = "UndimDetectorWakeLock";
+
+    /** DeviceConfig flag: is keep screen on feature enabled. */
+    static final String KEY_KEEP_SCREEN_ON_ENABLED = "keep_screen_on_enabled";
+    private static final boolean DEFAULT_KEEP_SCREEN_ON_ENABLED = true;
+    private static final int OUTCOME_POWER_BUTTON =
+            FrameworkStatsLog.TIMEOUT_AUTO_EXTENDED_REPORTED__OUTCOME__POWER_BUTTON;
+    private static final int OUTCOME_TIMEOUT =
+            FrameworkStatsLog.TIMEOUT_AUTO_EXTENDED_REPORTED__OUTCOME__TIMEOUT;
+    private boolean mKeepScreenOnEnabled;
+
+    /** DeviceConfig flag: how long should we keep the screen on. */
+    @VisibleForTesting
+    static final String KEY_KEEP_SCREEN_ON_FOR_MILLIS = "keep_screen_on_for_millis";
+    @VisibleForTesting
+    static final long DEFAULT_KEEP_SCREEN_ON_FOR_MILLIS = TimeUnit.MINUTES.toMillis(10);
+    private long mKeepScreenOnForMillis;
+
+    /** DeviceConfig flag: how many user undims required to trigger keeping the screen on. */
+    @VisibleForTesting
+    static final String KEY_UNDIMS_REQUIRED = "undims_required";
+    @VisibleForTesting
+    static final int DEFAULT_UNDIMS_REQUIRED = 2;
+    private int mUndimsRequired;
+
+    /**
+     * DeviceConfig flag: what is the maximum duration between undims to still consider them
+     * consecutive.
+     */
+    @VisibleForTesting
+    static final String KEY_MAX_DURATION_BETWEEN_UNDIMS_MILLIS =
+            "max_duration_between_undims_millis";
+    @VisibleForTesting
+    static final long DEFAULT_MAX_DURATION_BETWEEN_UNDIMS_MILLIS = TimeUnit.MINUTES.toMillis(5);
+    private long mMaxDurationBetweenUndimsMillis;
+
+    @VisibleForTesting
+    PowerManager.WakeLock mWakeLock;
+
+    @VisibleForTesting
+    int mCurrentScreenPolicy;
+    @VisibleForTesting
+    int mUndimCounter = 0;
+    @VisibleForTesting
+    long mUndimCounterStartedMillis;
+    private long mUndimOccurredTime = -1;
+    private long mInteractionAfterUndimTime = -1;
+    private InternalClock mClock;
+
+    public ScreenUndimDetector() {
+        mClock = new InternalClock();
+    }
+
+    ScreenUndimDetector(InternalClock clock) {
+        mClock = clock;
+    }
+
+    static class InternalClock {
+        public long getCurrentTime() {
+            return SystemClock.elapsedRealtime();
+        }
+    }
+
+    /** Should be called in parent's systemReady() */
+    public void systemReady(Context context) {
+        readValuesFromDeviceConfig();
+        DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                context.getMainExecutor(),
+                (properties) -> onDeviceConfigChange(properties.getKeyset()));
+
+        final PowerManager powerManager = context.getSystemService(PowerManager.class);
+        mWakeLock = powerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK
+                        | PowerManager.ON_AFTER_RELEASE,
+                UNDIM_DETECTOR_WAKE_LOCK);
+    }
+
+    /**
+     * Launches a message that figures out the screen transitions and detects user undims. Must be
+     * called by the parent that is trying to update the screen policy.
+     */
+    public void recordScreenPolicy(int newPolicy) {
+        if (newPolicy == mCurrentScreenPolicy) {
+            return;
+        }
+
+        if (DEBUG) {
+            Slog.d(TAG,
+                    "Screen policy transition: " + mCurrentScreenPolicy + " -> " + newPolicy);
+        }
+
+        // update the current policy with the new one immediately so we don't accidentally get
+        // into a loop (which is possible if the switch below triggers a new policy).
+        final int currentPolicy = mCurrentScreenPolicy;
+        mCurrentScreenPolicy = newPolicy;
+
+        if (!mKeepScreenOnEnabled) {
+            return;
+        }
+
+        switch (currentPolicy) {
+            case POLICY_DIM:
+                if (newPolicy == POLICY_BRIGHT) {
+                    final long now = mClock.getCurrentTime();
+                    final long timeElapsedSinceFirstUndim = now - mUndimCounterStartedMillis;
+                    if (timeElapsedSinceFirstUndim >= mMaxDurationBetweenUndimsMillis) {
+                        reset();
+                    }
+                    if (mUndimCounter == 0) {
+                        mUndimCounterStartedMillis = now;
+                    }
+
+                    mUndimCounter++;
+
+                    if (DEBUG) {
+                        Slog.d(TAG, "User undim, counter=" + mUndimCounter
+                                + " (required=" + mUndimsRequired + ")"
+                                + ", timeElapsedSinceFirstUndim=" + timeElapsedSinceFirstUndim
+                                + " (max=" + mMaxDurationBetweenUndimsMillis + ")");
+                    }
+                    if (mUndimCounter >= mUndimsRequired) {
+                        reset();
+                        if (DEBUG) {
+                            Slog.d(TAG, "Acquiring a wake lock for " + mKeepScreenOnForMillis);
+                        }
+                        if (mWakeLock != null) {
+                            mUndimOccurredTime = mClock.getCurrentTime();
+                            mWakeLock.acquire(mKeepScreenOnForMillis);
+                        }
+                    }
+                } else {
+                    if (newPolicy == POLICY_OFF || newPolicy == POLICY_DOZE) {
+                        checkAndLogUndim(OUTCOME_TIMEOUT);
+                    }
+                    reset();
+                }
+                break;
+            case POLICY_BRIGHT:
+                if (newPolicy == POLICY_OFF || newPolicy == POLICY_DOZE) {
+                    checkAndLogUndim(OUTCOME_POWER_BUTTON);
+                }
+                if (newPolicy != POLICY_DIM) {
+                    reset();
+                }
+                break;
+        }
+    }
+
+    @VisibleForTesting
+    void reset() {
+        if (DEBUG) {
+            Slog.d(TAG, "Resetting the undim detector");
+        }
+        mUndimCounter = 0;
+        mUndimCounterStartedMillis = 0;
+        if (mWakeLock != null && mWakeLock.isHeld()) {
+            mWakeLock.release();
+        }
+    }
+
+    private boolean readKeepScreenOnNotificationEnabled() {
+        return DeviceConfig.getBoolean(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_KEEP_SCREEN_ON_ENABLED,
+                DEFAULT_KEEP_SCREEN_ON_ENABLED);
+    }
+
+    private long readKeepScreenOnForMillis() {
+        return DeviceConfig.getLong(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_KEEP_SCREEN_ON_FOR_MILLIS,
+                DEFAULT_KEEP_SCREEN_ON_FOR_MILLIS);
+    }
+
+    private int readUndimsRequired() {
+        int undimsRequired = DeviceConfig.getInt(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_UNDIMS_REQUIRED,
+                DEFAULT_UNDIMS_REQUIRED);
+
+        if (undimsRequired < 1 || undimsRequired > 5) {
+            Slog.e(TAG, "Provided undimsRequired=" + undimsRequired
+                    + " is not allowed [1, 5]; using the default=" + DEFAULT_UNDIMS_REQUIRED);
+            return DEFAULT_UNDIMS_REQUIRED;
+        }
+
+        return undimsRequired;
+    }
+
+    private long readMaxDurationBetweenUndimsMillis() {
+        return DeviceConfig.getLong(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_MAX_DURATION_BETWEEN_UNDIMS_MILLIS,
+                DEFAULT_MAX_DURATION_BETWEEN_UNDIMS_MILLIS);
+    }
+
+    private void onDeviceConfigChange(@NonNull Set<String> keys) {
+        for (String key : keys) {
+            Slog.i(TAG, "onDeviceConfigChange; key=" + key);
+            switch (key) {
+                case KEY_KEEP_SCREEN_ON_ENABLED:
+                case KEY_KEEP_SCREEN_ON_FOR_MILLIS:
+                case KEY_UNDIMS_REQUIRED:
+                case KEY_MAX_DURATION_BETWEEN_UNDIMS_MILLIS:
+                    readValuesFromDeviceConfig();
+                    return;
+                default:
+                    Slog.i(TAG, "Ignoring change on " + key);
+            }
+        }
+    }
+
+    @VisibleForTesting
+    void readValuesFromDeviceConfig() {
+        mKeepScreenOnEnabled = readKeepScreenOnNotificationEnabled();
+        mKeepScreenOnForMillis = readKeepScreenOnForMillis();
+        mUndimsRequired = readUndimsRequired();
+        mMaxDurationBetweenUndimsMillis = readMaxDurationBetweenUndimsMillis();
+
+        Slog.i(TAG, "readValuesFromDeviceConfig():"
+                + "\nmKeepScreenOnForMillis=" + mKeepScreenOnForMillis
+                + "\nmKeepScreenOnNotificationEnabled=" + mKeepScreenOnEnabled
+                + "\nmUndimsRequired=" + mUndimsRequired);
+
+    }
+
+    /**
+     * The user interacted with the screen after an undim, indicating the phone is in use.
+     * We use this event for logging.
+     */
+    public void userActivity() {
+        if (mUndimOccurredTime != 1 && mInteractionAfterUndimTime == -1) {
+            mInteractionAfterUndimTime = mClock.getCurrentTime();
+        }
+    }
+
+    /**
+     * Checks and logs if an undim occurred.
+     *
+     * A log will occur if an undim seems to have resulted in a timeout or a direct screen off such
+     * as from a power button. Some outcomes may not be correctly assigned to a
+     * TIMEOUT_AUTO_EXTENDED_REPORTED__OUTCOME value.
+     */
+    private void checkAndLogUndim(int outcome) {
+        if (mUndimOccurredTime != -1) {
+            long now = mClock.getCurrentTime();
+            FrameworkStatsLog.write(FrameworkStatsLog.TIMEOUT_AUTO_EXTENDED_REPORTED,
+                    outcome,
+                    /* time_to_outcome_millis=*/  now - mUndimOccurredTime,
+                    /* time_to_first_interaction_millis= */
+                    mInteractionAfterUndimTime != -1 ? now - mInteractionAfterUndimTime : -1
+            );
+            mUndimOccurredTime = -1;
+            mInteractionAfterUndimTime = -1;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index 6014d0c..2491565d 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -255,7 +255,11 @@
     private boolean checkTidValid(int uid, int tgid, int [] tids) {
         // Make sure all tids belongs to the same UID (including isolated UID),
         // tids can belong to different application processes.
-        List<Integer> eligiblePids = mAmInternal.getIsolatedProcesses(uid);
+        List<Integer> eligiblePids = null;
+        // To avoid deadlock, do not call into AMS if the call is from system.
+        if (uid != Process.SYSTEM_UID) {
+            eligiblePids = mAmInternal.getIsolatedProcesses(uid);
+        }
         if (eligiblePids == null) {
             eligiblePids = new ArrayList<>();
         }
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
index 2a95416..06253a0 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
@@ -124,12 +124,8 @@
         @Override
         public void read(InputStream in) throws IOException {
             while (in.available() > 0) {
-                try {
-                    DataElement dataElement = new DataElement(in);
-                    mCallback.onReadDataElement(dataElement.getData());
-                } catch (IOException e) {
-                    Slog.e(TAG, "Failed to read from storage. " + e.getMessage());
-                }
+                DataElement dataElement = new DataElement(in);
+                mCallback.onReadDataElement(dataElement.getData());
             }
         }
     }
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index 9c3721b1..382398a 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -596,7 +596,12 @@
     /** Retrieves the network score for a VCN Network */
     // Package visibility for use in VcnGatewayConnection and VcnNetworkProvider
     static NetworkScore getNetworkScore() {
-        return new NetworkScore.Builder().setLegacyInt(VCN_LEGACY_SCORE_INT).build();
+        // TODO(b/193687515): Stop setting TRANSPORT_PRIMARY, define a TRANSPORT_VCN, and set in
+        //                    NetworkOffer/NetworkAgent.
+        return new NetworkScore.Builder()
+                .setLegacyInt(VCN_LEGACY_SCORE_INT)
+                .setTransportPrimary(true)
+                .build();
     }
 
     /** Callback used for passing status signals from a VcnGatewayConnection to its managing Vcn. */
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index e85fa23..0f4f58b 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -228,17 +228,20 @@
 
             Vibration.Status status = null;
             while (!mStepQueue.isEmpty()) {
-                long waitTime = mStepQueue.calculateWaitTime();
-                if (waitTime <= 0) {
-                    mStepQueue.consumeNext();
-                } else {
-                    synchronized (mLock) {
+                long waitTime;
+                synchronized (mLock) {
+                    waitTime = mStepQueue.calculateWaitTime();
+                    if (waitTime > 0) {
                         try {
                             mLock.wait(waitTime);
                         } catch (InterruptedException e) {
                         }
                     }
                 }
+                // If we waited, the queue may have changed, so let the loop run again.
+                if (waitTime <= 0) {
+                    mStepQueue.consumeNext();
+                }
                 Vibration.Status currentStatus = mStop ? Vibration.Status.CANCELLED
                         : mStepQueue.calculateVibrationStatus(sequentialEffectSize);
                 if (status == null && currentStatus != Vibration.Status.RUNNING) {
@@ -387,15 +390,13 @@
         }
 
         /** Returns the time in millis to wait before calling {@link #consumeNext()}. */
+        @GuardedBy("mLock")
         public long calculateWaitTime() {
-            Step nextStep;
-            synchronized (mLock) {
-                if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
-                    // Steps anticipated by vibrator complete callback should be played right away.
-                    return 0;
-                }
-                nextStep = mNextSteps.peek();
+            if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
+                // Steps anticipated by vibrator complete callback should be played right away.
+                return 0;
             }
+            Step nextStep = mNextSteps.peek();
             return nextStep == null ? 0 : nextStep.calculateWaitTime();
         }
 
@@ -603,7 +604,10 @@
             return false;
         }
 
-        /** Returns the time in millis to wait before playing this step. */
+        /**
+         * Returns the time in millis to wait before playing this step. This is performed
+         * while holding the queue lock, so should not rely on potentially slow operations.
+         */
         public long calculateWaitTime() {
             if (startTime == Long.MAX_VALUE) {
                 // This step don't have a predefined start time, it's just marked to be executed
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 7713320..80bc16a 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wallpaper;
 
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.app.WallpaperManager.COMMAND_REAPPLY;
 import static android.app.WallpaperManager.FLAG_LOCK;
 import static android.app.WallpaperManager.FLAG_SYSTEM;
@@ -2045,7 +2046,21 @@
         }
     }
 
+    private boolean hasCrossUserPermission() {
+        final int interactPermission =
+                mContext.checkCallingPermission(INTERACT_ACROSS_USERS_FULL);
+        return interactPermission == PackageManager.PERMISSION_GRANTED;
+    }
+
+    @Override
     public boolean hasNamedWallpaper(String name) {
+        final int callingUser = UserHandle.getCallingUserId();
+        final boolean allowCrossUser = hasCrossUserPermission();
+        if (DEBUG) {
+            Slog.d(TAG, "hasNamedWallpaper() caller " + Binder.getCallingUid()
+                    + " cross-user?: " + allowCrossUser);
+        }
+
         synchronized (mLock) {
             List<UserInfo> users;
             final long ident = Binder.clearCallingIdentity();
@@ -2055,6 +2070,11 @@
                 Binder.restoreCallingIdentity(ident);
             }
             for (UserInfo user: users) {
+                if (!allowCrossUser && callingUser != user.id) {
+                    // No cross-user information for callers without permission
+                    continue;
+                }
+
                 // ignore managed profiles
                 if (user.isManagedProfile()) {
                     continue;
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 54d97ee..a4d2348 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -88,6 +88,7 @@
 import android.view.ViewConfiguration;
 import android.view.WindowInfo;
 import android.view.WindowManager;
+import android.view.WindowManagerPolicyConstants;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 
@@ -755,6 +756,7 @@
             if (magnifying) {
                 switch (transition) {
                     case WindowManager.TRANSIT_OLD_ACTIVITY_OPEN:
+                    case WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN:
                     case WindowManager.TRANSIT_OLD_TASK_OPEN:
                     case WindowManager.TRANSIT_OLD_TASK_TO_FRONT:
                     case WindowManager.TRANSIT_OLD_WALLPAPER_OPEN:
@@ -1200,7 +1202,7 @@
                     final SurfaceControl.Transaction t = mService.mTransactionFactory.get();
                     final int layer =
                             mService.mPolicy.getWindowLayerFromTypeLw(TYPE_MAGNIFICATION_OVERLAY) *
-                                    WindowManagerService.TYPE_LAYER_MULTIPLIER;
+                                    WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;
                     t.setLayer(mSurfaceControl, layer).setPosition(mSurfaceControl, 0, 0);
                     InputMonitor.setTrustedOverlayInputInfo(mSurfaceControl, t,
                             mDisplayContent.getDisplayId(), "Magnification Overlay");
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 331b8de..cc0db1d 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -21,6 +21,8 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.os.Process.INVALID_UID;
+import static android.os.Process.SYSTEM_UID;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_APPLICATION;
 import static android.view.Display.DEFAULT_DISPLAY;
@@ -57,6 +59,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
@@ -68,6 +71,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.Trace;
+import android.os.UserHandle;
 import android.service.voice.VoiceInteractionManagerInternal;
 import android.util.Slog;
 import android.view.RemoteAnimationDefinition;
@@ -79,6 +83,7 @@
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.server.LocalServices;
 import com.android.server.Watchdog;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.uri.NeededUriGrants;
 import com.android.server.vr.VrManagerInternal;
 
@@ -549,7 +554,9 @@
                 if (ar == null) {
                     return null;
                 }
-                final ActivityRecord below = ar.getTask().getActivityBelow(ar);
+                // Exclude finishing activity.
+                final ActivityRecord below = ar.getTask().getActivity((r) -> !r.finishing,
+                        ar, false /*includeBoundary*/, true /*traverseTopToBottom*/);
                 if (below != null && below.getUid() == ar.getUid()) {
                     return below.appToken.asBinder();
                 }
@@ -583,20 +590,45 @@
 
     @Override
     public int getLaunchedFromUid(IBinder token) {
+        if (!canGetLaunchedFrom()) {
+            return INVALID_UID;
+        }
         synchronized (mGlobalLock) {
             final ActivityRecord r = ActivityRecord.forTokenLocked(token);
-            return r != null ? r.launchedFromUid : android.os.Process.INVALID_UID;
+            return r != null ? r.launchedFromUid : INVALID_UID;
         }
     }
 
     @Override
     public String getLaunchedFromPackage(IBinder token) {
+        if (!canGetLaunchedFrom()) {
+            return null;
+        }
         synchronized (mGlobalLock) {
             final ActivityRecord r = ActivityRecord.forTokenLocked(token);
             return r != null ? r.launchedFromPackage : null;
         }
     }
 
+    /** Whether the caller can get the package or uid that launched its activity. */
+    private boolean canGetLaunchedFrom() {
+        final int uid = Binder.getCallingUid();
+        if (UserHandle.getAppId(uid) == SYSTEM_UID) {
+            return true;
+        }
+        final PackageManagerInternal pm = mService.mWindowManager.mPmInternal;
+        final AndroidPackage callingPkg = pm.getPackage(uid);
+        if (callingPkg == null) {
+            return false;
+        }
+        if (callingPkg.isSignedWithPlatformKey()) {
+            return true;
+        }
+        final String[] installerNames = pm.getKnownPackageNames(
+                PackageManagerInternal.PACKAGE_INSTALLER, UserHandle.getUserId(uid));
+        return installerNames.length > 0 && callingPkg.getPackageName().equals(installerNames[0]);
+    }
+
     @Override
     public void setRequestedOrientation(IBinder token, int requestedOrientation) {
         final long origId = Binder.clearCallingIdentity();
@@ -1197,10 +1229,10 @@
 
                 final Task task = r.getTask();
                 isLastRunningActivity = task.topRunningActivity() == r;
-                final Intent baseIntent = task.getBaseIntent();
-                final boolean activityIsBaseActivity = baseIntent != null
-                        && r.mActivityComponent.equals(baseIntent.getComponent());
-                baseActivityIntent = activityIsBaseActivity ? r.intent : null;
+
+                final boolean isBaseActivity = r.mActivityComponent.equals(task.realActivity);
+                baseActivityIntent = isBaseActivity ? r.intent : null;
+
                 launchedFromHome = r.isLaunchSourceType(ActivityRecord.LAUNCH_SOURCE_TYPE_HOME);
             }
 
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 005a62a..c715c39 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -59,6 +59,8 @@
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_REPORTED_DRAWN_NO_BUNDLE;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_REPORTED_DRAWN_WITH_BUNDLE;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_WARM_LAUNCH;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
 import static com.android.server.am.MemoryStatUtil.MemoryStat;
 import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_METRICS;
@@ -89,6 +91,7 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 
@@ -157,6 +160,9 @@
     private final ArrayList<TransitionInfo> mTransitionInfoList = new ArrayList<>();
     /** Map : Last launched activity => {@link TransitionInfo} */
     private final ArrayMap<ActivityRecord, TransitionInfo> mLastTransitionInfo = new ArrayMap<>();
+    /** SparseArray : Package UID => {@link PackageCompatStateInfo} */
+    private final SparseArray<PackageCompatStateInfo> mPackageUidToCompatStateInfo =
+            new SparseArray<>(0);
 
     private ArtManagerInternal mArtManagerInternal;
     private final StringBuilder mStringBuilder = new StringBuilder();
@@ -187,6 +193,10 @@
             return mAssociatedTransitionInfo != null && mAssociatedTransitionInfo.allDrawn();
         }
 
+        boolean hasActiveTransitionInfo() {
+            return mAssociatedTransitionInfo != null;
+        }
+
         boolean contains(ActivityRecord r) {
             return mAssociatedTransitionInfo != null && mAssociatedTransitionInfo.contains(r);
         }
@@ -336,10 +346,11 @@
         }
 
         /** Only keep the records which can be drawn. */
-        void updatePendingDraw() {
+        void updatePendingDraw(boolean keepInitializing) {
             for (int i = mPendingDrawActivities.size() - 1; i >= 0; i--) {
                 final ActivityRecord r = mPendingDrawActivities.get(i);
-                if (!r.mVisibleRequested) {
+                if (!r.mVisibleRequested
+                        && !(keepInitializing && r.isState(ActivityRecord.State.INITIALIZING))) {
                     if (DEBUG_METRICS) Slog.i(TAG, "Discard pending draw " + r);
                     mPendingDrawActivities.remove(i);
                 }
@@ -447,6 +458,15 @@
         }
     }
 
+    /** Information about the App Compat state logging associated with a package UID . */
+    private static final class PackageCompatStateInfo {
+        /** All activities that have a visible state. */
+        final ArrayList<ActivityRecord> mVisibleActivities = new ArrayList<>();
+        /** The last logged state. */
+        int mLastLoggedState = APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
+        @Nullable ActivityRecord mLastLoggedActivity;
+    }
+
     ActivityMetricsLogger(ActivityTaskSupervisor supervisor, Looper looper) {
         mLastLogTimeSecs = SystemClock.elapsedRealtime() / 1000;
         mSupervisor = supervisor;
@@ -633,6 +653,7 @@
             if (crossPackage) {
                 startLaunchTrace(info);
             }
+            scheduleCheckActivityToBeDrawnIfSleeping(launchedActivity);
             return;
         }
 
@@ -654,13 +675,7 @@
             // As abort for no process switch.
             launchObserverNotifyIntentFailed();
         }
-        if (launchedActivity.mDisplayContent.isSleeping()) {
-            // It is unknown whether the activity can be drawn or not, e.g. it depends on the
-            // keyguard states and the attributes or flags set by the activity. If the activity
-            // keeps invisible in the grace period, the tracker will be cancelled so it won't get
-            // a very long launch time that takes unlocking as the end of launch.
-            scheduleCheckActivityToBeDrawn(launchedActivity, UNKNOWN_VISIBILITY_CHECK_DELAY_MS);
-        }
+        scheduleCheckActivityToBeDrawnIfSleeping(launchedActivity);
 
         // If the previous transitions are no longer visible, abort them to avoid counting the
         // launch time when resuming from back stack. E.g. launch 2 independent tasks in a short
@@ -668,13 +683,23 @@
         // visible such as after the top task is finished.
         for (int i = mTransitionInfoList.size() - 2; i >= 0; i--) {
             final TransitionInfo prevInfo = mTransitionInfoList.get(i);
-            prevInfo.updatePendingDraw();
+            prevInfo.updatePendingDraw(false /* keepInitializing */);
             if (prevInfo.allDrawn()) {
                 abort(prevInfo, "nothing will be drawn");
             }
         }
     }
 
+    private void scheduleCheckActivityToBeDrawnIfSleeping(@NonNull ActivityRecord r) {
+        if (r.mDisplayContent.isSleeping()) {
+            // It is unknown whether the activity can be drawn or not, e.g. it depends on the
+            // keyguard states and the attributes or flags set by the activity. If the activity
+            // keeps invisible in the grace period, the tracker will be cancelled so it won't get
+            // a very long launch time that takes unlocking as the end of launch.
+            scheduleCheckActivityToBeDrawn(r, UNKNOWN_VISIBILITY_CHECK_DELAY_MS);
+        }
+    }
+
     /**
      * Notifies the tracker that all windows of the app have been drawn.
      *
@@ -693,6 +718,7 @@
         // Always calculate the delay because the caller may need to know the individual drawn time.
         info.mWindowsDrawnDelayMs = info.calculateDelay(timestampNs);
         info.removePendingDrawActivity(r);
+        info.updatePendingDraw(false /* keepInitializing */);
         final TransitionInfoSnapshot infoSnapshot = new TransitionInfoSnapshot(info);
         if (info.mLoggedTransitionStarting && info.allDrawn()) {
             done(false /* abort */, info, "notifyWindowsDrawn - all windows drawn", timestampNs);
@@ -744,7 +770,9 @@
             info.mCurrentTransitionDelayMs = info.calculateDelay(timestampNs);
             info.mReason = activityToReason.valueAt(index);
             info.mLoggedTransitionStarting = true;
-            info.updatePendingDraw();
+            // Do not remove activity in initializing state because the transition may be started
+            // by starting window. The initializing activity may be requested to visible soon.
+            info.updatePendingDraw(true /* keepInitializing */);
             if (info.allDrawn()) {
                 done(false /* abort */, info, "notifyTransitionStarting - all windows drawn",
                         timestampNs);
@@ -762,6 +790,21 @@
     /** Makes sure that the reference to the removed activity is cleared. */
     void notifyActivityRemoved(@NonNull ActivityRecord r) {
         mLastTransitionInfo.remove(r);
+
+        final int packageUid = r.info.applicationInfo.uid;
+        final PackageCompatStateInfo compatStateInfo = mPackageUidToCompatStateInfo.get(packageUid);
+        if (compatStateInfo == null) {
+            return;
+        }
+
+        compatStateInfo.mVisibleActivities.remove(r);
+        if (compatStateInfo.mLastLoggedActivity == r) {
+            compatStateInfo.mLastLoggedActivity = null;
+        }
+        if (compatStateInfo.mVisibleActivities.isEmpty()) {
+            // No need to keep the entry if there are no visible activities.
+            mPackageUidToCompatStateInfo.remove(packageUid);
+        }
     }
 
     /**
@@ -1258,6 +1301,115 @@
                 memoryStat.swapInBytes);
     }
 
+    /**
+     * Logs the current App Compat state of the given {@link ActivityRecord} with its package
+     * UID, if all of the following hold:
+     * <ul>
+     *   <li>The current state is different than the last logged state for the package UID of the
+     *   activity.
+     *   <li>If the current state is NOT_VISIBLE, there is a previously logged state for the
+     *   package UID and there are no other visible activities with the same package UID.
+     *   <li>The last logged activity with the same package UID is either {@code activity} or the
+     *   last logged state is NOT_VISIBLE or NOT_LETTERBOXED.
+     * </ul>
+     *
+     * <p>If the current state is NOT_VISIBLE and the previous state which was logged by {@code
+     * activity} wasn't, looks for the first visible activity with the same package UID that has
+     * a letterboxed state, or a non-letterboxed state if there isn't one, and logs that state.
+     *
+     * <p>This method assumes that the caller is wrapping the call with a synchronized block so
+     * that there won't be a race condition between two activities with the same package.
+     */
+    void logAppCompatState(@NonNull ActivityRecord activity) {
+        final int packageUid = activity.info.applicationInfo.uid;
+        final int state = activity.getAppCompatState();
+
+        if (!mPackageUidToCompatStateInfo.contains(packageUid)) {
+            mPackageUidToCompatStateInfo.put(packageUid, new PackageCompatStateInfo());
+        }
+        final PackageCompatStateInfo compatStateInfo = mPackageUidToCompatStateInfo.get(packageUid);
+        final int lastLoggedState = compatStateInfo.mLastLoggedState;
+        final ActivityRecord lastLoggedActivity = compatStateInfo.mLastLoggedActivity;
+
+        final boolean isVisible = state != APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
+        final ArrayList<ActivityRecord> visibleActivities = compatStateInfo.mVisibleActivities;
+        if (isVisible && !visibleActivities.contains(activity)) {
+            visibleActivities.add(activity);
+        } else if (!isVisible) {
+            visibleActivities.remove(activity);
+            if (visibleActivities.isEmpty()) {
+                // No need to keep the entry if there are no visible activities.
+                mPackageUidToCompatStateInfo.remove(packageUid);
+            }
+        }
+
+        if (state == lastLoggedState) {
+            // We don’t want to log the same state twice or log DEFAULT_NOT_VISIBLE before any
+            // visible state was logged.
+            return;
+        }
+
+        if (!isVisible && !visibleActivities.isEmpty()) {
+            // There is another visible activity for this package UID.
+            if (activity == lastLoggedActivity) {
+                // Make sure a new visible state is logged if needed.
+                findAppCompatStateToLog(compatStateInfo, packageUid);
+            }
+            return;
+        }
+
+        if (activity != lastLoggedActivity
+                && lastLoggedState != APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE
+                && lastLoggedState != APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED) {
+            // Another visible activity for this package UID has logged a letterboxed state.
+            return;
+        }
+
+        logAppCompatStateInternal(activity, state, packageUid, compatStateInfo);
+    }
+
+    /**
+     * Looks for the first visible activity in {@code compatStateInfo} that has a letterboxed
+     * state, or a non-letterboxed state if there isn't one, and logs that state for the given
+     * {@code packageUid}.
+     */
+    private void findAppCompatStateToLog(PackageCompatStateInfo compatStateInfo, int packageUid) {
+        final ArrayList<ActivityRecord> visibleActivities = compatStateInfo.mVisibleActivities;
+
+        ActivityRecord activityToLog = null;
+        int stateToLog = APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
+        for (int i = 0; i < visibleActivities.size(); i++) {
+            ActivityRecord activity = visibleActivities.get(i);
+            int state = activity.getAppCompatState();
+            if (state == APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE) {
+                // This shouldn't happen.
+                Slog.w(TAG, "Visible activity with NOT_VISIBLE App Compat state for package UID: "
+                        + packageUid);
+                continue;
+            }
+            if (stateToLog == APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE || (
+                    stateToLog == APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED
+                            && state != APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED)) {
+                activityToLog = activity;
+                stateToLog = state;
+            }
+        }
+        if (activityToLog != null && stateToLog != APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE) {
+            logAppCompatStateInternal(activityToLog, stateToLog, packageUid, compatStateInfo);
+        }
+    }
+
+    private void logAppCompatStateInternal(@NonNull ActivityRecord activity, int state,
+            int packageUid, PackageCompatStateInfo compatStateInfo) {
+        compatStateInfo.mLastLoggedState = state;
+        compatStateInfo.mLastLoggedActivity = activity;
+        FrameworkStatsLog.write(FrameworkStatsLog.APP_COMPAT_STATE_CHANGED, packageUid, state);
+
+        if (DEBUG_METRICS) {
+            Slog.i(TAG, String.format("APP_COMPAT_STATE_CHANGED(%s, %s)", packageUid, state));
+        }
+    }
+
     private ArtManagerInternal getArtManagerInternal() {
         if (mArtManagerInternal == null) {
             // Note that this may be null.
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 5970f62..67043db 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -127,6 +127,11 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SWITCH;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_FIXED_ORIENTATION;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.ActivityRecord.State.DESTROYED;
@@ -330,8 +335,6 @@
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.ToBooleanFunction;
 import com.android.internal.util.XmlUtils;
-import com.android.internal.util.function.pooled.PooledFunction;
-import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
 import com.android.server.am.AppTimeTracker;
 import com.android.server.am.PendingIntentRecord;
@@ -678,6 +681,12 @@
     boolean mLastImeShown;
 
     /**
+     * When set to true, the IME insets will be frozen until the next app becomes IME input target.
+     * @see InsetsPolicy#adjustVisibilityForIme
+     */
+    boolean mImeInsetsFrozenUntilStartInput;
+
+    /**
      * A flag to determine if this AR is in the process of closing or entering PIP. This is needed
      * to help AR know that the app is in the process of closing but hasn't yet started closing on
      * the WM side.
@@ -704,8 +713,6 @@
     private boolean mInSizeCompatModeForBounds = false;
 
     // Whether the aspect ratio restrictions applied to the activity bounds in applyAspectRatio().
-    // TODO(b/182268157): Aspect ratio can also be applie in resolveFixedOrientationConfiguration
-    // but that isn't reflected in this boolean.
     private boolean mIsAspectRatioApplied = false;
 
     // Bounds populated in resolveFixedOrientationConfiguration when this activity is letterboxed
@@ -746,6 +753,13 @@
     boolean startingDisplayed;
     boolean startingMoved;
 
+    /**
+     * If it is non-null, it requires all activities who have the same starting data to be drawn
+     * to remove the starting window.
+     * TODO(b/189385912): Remove starting window related fields after migrating them to task.
+     */
+    private StartingData mSharedStartingData;
+
     boolean mHandleExitSplashScreen;
     @TransferSplashScreenState
     int mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_IDLE;
@@ -1085,6 +1099,9 @@
             pw.print(" firstWindowDrawn="); pw.print(firstWindowDrawn);
             pw.print(" mIsExiting="); pw.println(mIsExiting);
         }
+        if (mSharedStartingData != null) {
+            pw.println(prefix + "mSharedStartingData=" + mSharedStartingData);
+        }
         if (mStartingWindow != null || mStartingSurface != null
                 || startingDisplayed || startingMoved || mVisibleSetFromTransferredStartingWindow) {
             pw.print(prefix); pw.print("startingWindow="); pw.print(mStartingWindow);
@@ -1173,7 +1190,7 @@
             header.run();
         }
 
-        String innerPrefix = prefix + "      ";
+        String innerPrefix = prefix + "  ";
         String[] args = new String[0];
         if (lastTask != r.getTask()) {
             lastTask = r.getTask();
@@ -1379,9 +1396,11 @@
     }
 
     @Override
-    void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
-        final Task oldTask = oldParent != null ? ((TaskFragment) oldParent).getTask() : null;
-        final Task newTask = newParent != null ? ((TaskFragment) newParent).getTask() : null;
+    void onParentChanged(ConfigurationContainer rawNewParent, ConfigurationContainer rawOldParent) {
+        final TaskFragment oldParent = (TaskFragment) rawOldParent;
+        final TaskFragment newParent = (TaskFragment) rawNewParent;
+        final Task oldTask = oldParent != null ? oldParent.getTask() : null;
+        final Task newTask = newParent != null ? newParent.getTask() : null;
         this.task = newTask;
 
         super.onParentChanged(newParent, oldParent);
@@ -1440,11 +1459,19 @@
         updateColorTransform();
 
         if (oldParent != null) {
-            ((TaskFragment) oldParent).cleanUpActivityReferences(this);
+            oldParent.cleanUpActivityReferences(this);
         }
 
         if (newParent != null && isState(RESUMED)) {
-            ((TaskFragment) newParent).setResumedActivity(this, "onParentChanged");
+            newParent.setResumedActivity(this, "onParentChanged");
+            if (mStartingWindow != null && mStartingData != null
+                    && mStartingData.mAssociatedTask == null && newParent.isEmbedded()) {
+                // The starting window should keep covering its task when the activity is
+                // reparented to a task fragment that may not fill the task bounds.
+                associateStartingDataWithTask();
+                overrideConfigurationPropagation(mStartingWindow, task);
+            }
+            mImeInsetsFrozenUntilStartInput = false;
         }
 
         if (rootTask != null && rootTask.topRunningActivity() == this) {
@@ -2005,7 +2032,7 @@
     @VisibleForTesting
     boolean addStartingWindow(String pkg, int resolvedTheme, CompatibilityInfo compatInfo,
             CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
-            IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
+            ActivityRecord from, boolean newTask, boolean taskSwitch, boolean processRunning,
             boolean allowTaskSnapshot, boolean activityCreated, boolean useEmpty) {
         // If the display is frozen, we won't do anything until the actual window is
         // displayed so there is no reason to put in the starting window.
@@ -2064,7 +2091,7 @@
         }
         applyStartingWindowTheme(pkg, resolvedTheme);
 
-        if (transferStartingWindow(transferFrom)) {
+        if (from != null && transferStartingWindow(from)) {
             return true;
         }
 
@@ -2089,6 +2116,11 @@
 
         ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Creating SnapshotStartingData");
         mStartingData = new SnapshotStartingData(mWmService, snapshot, typeParams);
+        if (task.forAllLeafTaskFragments(TaskFragment::isEmbedded)) {
+            // Associate with the task so if this activity is resized by task fragment later, the
+            // starting window can keep the same bounds as the task.
+            associateStartingDataWithTask();
+        }
         scheduleAddStartingWindow();
         return true;
     }
@@ -2337,6 +2369,23 @@
         }
     }
 
+    void attachStartingWindow(@NonNull WindowState startingWindow) {
+        mStartingWindow = startingWindow;
+        if (mStartingData != null && mStartingData.mAssociatedTask != null) {
+            // Associate the configuration of starting window with the task.
+            overrideConfigurationPropagation(startingWindow, mStartingData.mAssociatedTask);
+        }
+    }
+
+    private void associateStartingDataWithTask() {
+        mStartingData.mAssociatedTask = task;
+        task.forAllActivities(r -> {
+            if (r.mVisibleRequested && !r.firstWindowDrawn) {
+                r.mSharedStartingData = mStartingData;
+            }
+        });
+    }
+
     void removeStartingWindow() {
         if (transferSplashScreenIfNeeded()) {
             return;
@@ -2346,6 +2395,11 @@
 
     void removeStartingWindowAnimation(boolean prepareAnimation) {
         mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_IDLE;
+        if (mSharedStartingData != null) {
+            mSharedStartingData.mAssociatedTask.forAllActivities(r -> {
+                r.mSharedStartingData = null;
+            });
+        }
         if (mStartingWindow == null) {
             if (mStartingData != null) {
                 // Starting window has not been added yet, but it is scheduled to be added.
@@ -2402,20 +2456,6 @@
         }
     }
 
-    private void removeAppTokenFromDisplay() {
-        if (mWmService.mRoot == null) return;
-
-        final DisplayContent dc = mWmService.mRoot.getDisplayContent(getDisplayId());
-        if (dc == null) {
-            Slog.w(TAG, "removeAppTokenFromDisplay: Attempted to remove token: "
-                    + appToken + " from non-existing displayId=" + getDisplayId());
-            return;
-        }
-        // Resume key dispatching if it is currently paused before we remove the container.
-        resumeKeyDispatchingLocked();
-        dc.removeAppToken(appToken.asBinder());
-    }
-
     /**
      * Reparents this activity into {@param newTaskFrag} at the provided {@param position}. The
      * caller should ensure that the {@param newTaskFrag} is not already the parent of this
@@ -2826,7 +2866,7 @@
     /**
      * @return Whether AppOps allows this package to enter picture-in-picture.
      */
-    private boolean checkEnterPictureInPictureAppOpsState() {
+    boolean checkEnterPictureInPictureAppOpsState() {
         return mAtmService.getAppOpsManager().checkOpNoThrow(
                 OP_PICTURE_IN_PICTURE, info.applicationInfo.uid, packageName) == MODE_ALLOWED;
     }
@@ -2966,7 +3006,7 @@
     @interface FinishRequest {}
 
     /**
-     * See {@link #finishIfPossible(int, Intent, String, boolean)}
+     * See {@link #finishIfPossible(int, Intent, NeededUriGrants, String, boolean)}
      */
     @FinishRequest int finishIfPossible(String reason, boolean oomAdj) {
         return finishIfPossible(Activity.RESULT_CANCELED,
@@ -3206,6 +3246,20 @@
         // TODO(b/137329632): find the next activity directly underneath this one, not just anywhere
         final ActivityRecord next = getDisplayArea().topRunningActivity(
                 true /* considerKeyguardState */);
+
+        // If the finishing activity is the last activity of a organized TaskFragment and has an
+        // adjacent TaskFragment, check if the activity removal should be delayed.
+        boolean delayRemoval = false;
+        final TaskFragment taskFragment = getTaskFragment();
+        if (next != null && taskFragment != null && taskFragment.isEmbedded()) {
+            final TaskFragment organized = taskFragment.getOrganizedTaskFragment();
+            final TaskFragment adjacent =
+                    organized != null ? organized.getAdjacentTaskFragment() : null;
+            if (adjacent != null && organized.topRunningActivity() == null) {
+                delayRemoval = organized.isDelayLastActivityRemoval();
+            }
+        }
+
         // isNextNotYetVisible is to check if the next activity is invisible, or it has been
         // requested to be invisible but its windows haven't reported as invisible.  If so, it
         // implied that the current finishing activity should be added into stopping list rather
@@ -3220,7 +3274,7 @@
         }
 
         if (isCurrentVisible) {
-            if (isNextNotYetVisible) {
+            if (isNextNotYetVisible || delayRemoval) {
                 // Add this activity to the list of stopping activities. It will be processed and
                 // destroyed when the next activity reports idle.
                 addToStopping(false /* scheduleIdle */, false /* idleDelayed */,
@@ -3428,7 +3482,9 @@
         setState(DESTROYED, "removeFromHistory");
         if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during remove for activity " + this);
         detachFromProcess();
-        removeAppTokenFromDisplay();
+        // Resume key dispatching if it is currently paused before we remove the container.
+        resumeKeyDispatchingLocked();
+        mDisplayContent.removeAppToken(appToken);
 
         cleanUpActivityServices();
         removeUriPermissionsLocked();
@@ -3453,7 +3509,13 @@
         if (stopped) {
             abortAndClearOptionsAnimation();
         }
-        mAtmService.getTransitionController().requestTransitionIfNeeded(TRANSIT_CLOSE, this);
+        if (mAtmService.getTransitionController().isCollecting()) {
+            // We don't want the finishing to change the transition ready state since there will not
+            // be corresponding setReady for finishing.
+            mAtmService.getTransitionController().collectExistenceChange(this);
+        } else {
+            mAtmService.getTransitionController().requestTransitionIfNeeded(TRANSIT_CLOSE, this);
+        }
     }
 
     /**
@@ -3633,18 +3695,33 @@
         }
         cleanUp(true /* cleanServices */, true /* setState */);
         if (remove) {
+            if (mStartingData != null && mVisible && task != null) {
+                // A corner case that the app terminates its trampoline activity on a separated
+                // process by killing itself. Transfer the starting window to the next activity
+                // which will be visible, so the dead activity can be removed immediately (no
+                // longer animating) and the reveal animation can play normally on next activity.
+                final ActivityRecord top = task.topRunningActivity();
+                if (top != null && !top.mVisible && top.shouldBeVisible()) {
+                    top.transferStartingWindow(this);
+                }
+            }
             removeFromHistory("appDied");
         }
     }
 
     @Override
     void removeImmediately() {
-        if (!finishing) {
+        if (mState != DESTROYED) {
+            Slog.w(TAG, "Force remove immediately " + this + " state=" + mState);
             // If Task#removeImmediately is called directly with alive activities, ensure that the
             // activities are destroyed and detached from process.
             destroyImmediately("removeImmediately");
+            // Complete the destruction immediately because this activity will not be found in
+            // hierarchy, it is unable to report completion.
+            destroyed("removeImmediately");
+        } else {
+            onRemovedFromDisplay();
         }
-        onRemovedFromDisplay();
         super.removeImmediately();
     }
 
@@ -3858,17 +3935,14 @@
         }
     }
 
-    boolean transferStartingWindow(IBinder transferFrom) {
-        final ActivityRecord fromActivity = getDisplayContent().getActivityRecord(transferFrom);
-        if (fromActivity == null) {
-            return false;
-        }
-
+    private boolean transferStartingWindow(@NonNull ActivityRecord fromActivity) {
         final WindowState tStartingWindow = fromActivity.mStartingWindow;
         if (tStartingWindow != null && fromActivity.mStartingSurface != null) {
             // In this case, the starting icon has already been displayed, so start
             // letting windows get shown immediately without any more transitions.
-            getDisplayContent().mSkipAppTransitionAnimation = true;
+            if (fromActivity.mVisible) {
+                mDisplayContent.mSkipAppTransitionAnimation = true;
+            }
 
             ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Moving existing starting %s"
                     + " from %s to %s", tStartingWindow, fromActivity, this);
@@ -3884,6 +3958,7 @@
 
                 // Transfer the starting window over to the new token.
                 mStartingData = fromActivity.mStartingData;
+                mSharedStartingData = fromActivity.mSharedStartingData;
                 mStartingSurface = fromActivity.mStartingSurface;
                 startingDisplayed = fromActivity.startingDisplayed;
                 fromActivity.startingDisplayed = false;
@@ -3946,6 +4021,7 @@
             ProtoLog.v(WM_DEBUG_STARTING_WINDOW,
                     "Moving pending starting from %s to %s", fromActivity, this);
             mStartingData = fromActivity.mStartingData;
+            mSharedStartingData = fromActivity.mSharedStartingData;
             fromActivity.mStartingData = null;
             fromActivity.startingMoved = true;
             scheduleAddStartingWindow();
@@ -3964,16 +4040,10 @@
      * immediately finishes after, so we have to transfer T to M.
      */
     void transferStartingWindowFromHiddenAboveTokenIfNeeded() {
-        final PooledFunction p = PooledLambda.obtainFunction(ActivityRecord::transferStartingWindow,
-                this, PooledLambda.__(ActivityRecord.class));
-        task.forAllActivities(p);
-        p.recycle();
-    }
-
-    private boolean transferStartingWindow(ActivityRecord fromActivity) {
-        if (fromActivity == this) return true;
-
-        return !fromActivity.mVisibleRequested && transferStartingWindow(fromActivity.token);
+        task.forAllActivities(fromActivity -> {
+            if (fromActivity == this) return true;
+            return !fromActivity.mVisibleRequested && transferStartingWindow(fromActivity);
+        });
     }
 
     void checkKeyguardFlagsChanged() {
@@ -4627,6 +4697,7 @@
                 ActivityTaskManagerService.LAYOUT_REASON_VISIBILITY_CHANGED);
         mTaskSupervisor.getActivityMetricsLogger().notifyVisibilityChanged(this);
         mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause = true;
+        logAppCompatState();
     }
 
     @VisibleForTesting
@@ -4882,6 +4953,7 @@
                     && imeInputTarget.getWindow().mActivityRecord == this
                     && mDisplayContent.mInputMethodWindow != null
                     && mDisplayContent.mInputMethodWindow.isVisible();
+            mImeInsetsFrozenUntilStartInput = true;
         }
 
         final DisplayContent displayContent = getDisplayContent();
@@ -5154,6 +5226,7 @@
     void notifyAppStopped() {
         ProtoLog.v(WM_DEBUG_ADD_REMOVE, "notifyAppStopped: %s", this);
         mAppStopped = true;
+        firstWindowDrawn = false;
         // This is to fix the edge case that auto-enter-pip is finished in Launcher but app calls
         // setAutoEnterEnabled(false) and transitions to STOPPED state, see b/191930787.
         // Clear any surface transactions and content overlay in this case.
@@ -5917,7 +5990,7 @@
         }
     }
 
-    void onFirstWindowDrawn(WindowState win, WindowStateAnimator winAnimator) {
+    void onFirstWindowDrawn(WindowState win) {
         firstWindowDrawn = true;
         // stop tracking
         mSplashScreenStyleEmpty = true;
@@ -5933,7 +6006,22 @@
             // own stuff.
             win.cancelAnimation();
         }
-        removeStartingWindow();
+
+        // Remove starting window directly if is in a pure task. Otherwise if it is associated with
+        // a task (e.g. nested task fragment), then remove only if all visible windows in the task
+        // are drawn.
+        final Task associatedTask =
+                mSharedStartingData != null ? mSharedStartingData.mAssociatedTask : null;
+        if (associatedTask == null) {
+            removeStartingWindow();
+        } else if (associatedTask.getActivity(
+                r -> r.mVisibleRequested && !r.firstWindowDrawn) == null) {
+            // The last drawn activity may not be the one that owns the starting window.
+            final ActivityRecord r = associatedTask.topActivityContainsStartingWindow();
+            if (r != null) {
+                r.removeStartingWindow();
+            }
+        }
         updateReportedVisibilityLocked();
     }
 
@@ -5997,6 +6085,14 @@
             // closing activity having to wait until idle timeout to be stopped or destroyed if the
             // next activity won't report idle (e.g. repeated view animation).
             mTaskSupervisor.scheduleProcessStoppingAndFinishingActivitiesIfNeeded();
+
+            // If the activity is visible, but no windows are eligible to start input, unfreeze
+            // to avoid permanently frozen IME insets.
+            if (mImeInsetsFrozenUntilStartInput && getWindow(
+                    win -> WindowManager.LayoutParams.mayUseInputMethod(win.mAttrs.flags))
+                    == null) {
+                mImeInsetsFrozenUntilStartInput = false;
+            }
         }
     }
 
@@ -6504,8 +6600,7 @@
 
         final boolean scheduled = addStartingWindow(packageName, resolvedTheme,
                 compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
-                prev != null ? prev.appToken : null,
-                newTask || newSingleActivity, taskSwitch, isProcessRunning(),
+                prev, newTask || newSingleActivity, taskSwitch, isProcessRunning(),
                 allowTaskSnapshot(), activityCreated, mSplashScreenStyleEmpty);
         if (DEBUG_STARTING_WINDOW_VERBOSE && scheduled) {
             Slog.d(TAG, "Scheduled starting window for " + this);
@@ -6570,17 +6665,6 @@
         }
     }
 
-    boolean hasWindowsAlive() {
-        for (int i = mChildren.size() - 1; i >= 0; i--) {
-            // No need to loop through child windows as the answer should be the same as that of the
-            // parent window.
-            if (!(mChildren.get(i)).mAppDied) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     void setWillReplaceWindows(boolean animate) {
         ProtoLog.d(WM_DEBUG_ADD_REMOVE,
                 "Marking app token %s with replacing windows.", this);
@@ -7030,7 +7114,7 @@
             return;
         }
 
-        mDisplayContent.mPinnedTaskController.onCancelFixedRotationTransform(task);
+        mDisplayContent.mPinnedTaskController.onCancelFixedRotationTransform();
         // Perform rotation animation according to the rotation of this activity.
         startFreezingScreen(originalDisplayRotation);
         // This activity may relaunch or perform configuration change so once it has reported drawn,
@@ -7360,6 +7444,8 @@
             }
             resolvedConfig.windowConfiguration.setMaxBounds(mTmpBounds);
         }
+
+        logAppCompatState();
     }
 
     /**
@@ -7369,18 +7455,55 @@
      * LetterboxUiController#shouldShowLetterboxUi} for more context.
      */
     boolean areBoundsLetterboxed() {
+        return getAppCompatState(/* ignoreVisibility= */ true)
+                != APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED;
+    }
+
+    /**
+     * Logs the current App Compat state via {@link ActivityMetricsLogger#logAppCompatState}.
+     */
+    private void logAppCompatState() {
+        mTaskSupervisor.getActivityMetricsLogger().logAppCompatState(this);
+    }
+
+    /**
+     * Returns the current App Compat state of this activity.
+     *
+     * <p>The App Compat state indicates whether the activity is visible and letterboxed, and if so
+     * what is the reason for letterboxing. The state is used for logging the time spent in
+     * letterbox (sliced by the reason) vs non-letterbox per app.
+     */
+    int getAppCompatState() {
+        return getAppCompatState(/* ignoreVisibility= */ false);
+    }
+
+    /**
+     * Same as {@link #getAppCompatState()} except when {@code ignoreVisibility} the visibility
+     * of the activity is ignored.
+     *
+     * @param ignoreVisibility whether to ignore the visibility of the activity and not return
+     *                         NOT_VISIBLE if {@code mVisibleRequested} is false.
+     */
+    private int getAppCompatState(boolean ignoreVisibility) {
+        if (!ignoreVisibility && !mVisibleRequested) {
+            return APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
+        }
         if (mInSizeCompatModeForBounds) {
-            return true;
+            return APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE;
         }
         // Letterbox for fixed orientation. This check returns true only when an activity is
         // letterboxed for fixed orientation. Aspect ratio restrictions are also applied if
         // present. But this doesn't return true when the activity is letterboxed only because
         // of aspect ratio restrictions.
         if (isLetterboxedForFixedOrientationAndAspectRatio()) {
-            return true;
+            return APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_FIXED_ORIENTATION;
         }
         // Letterbox for limited aspect ratio.
-        return mIsAspectRatioApplied;
+        if (mIsAspectRatioApplied) {
+            return APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO;
+        }
+
+        return APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED;
     }
 
     /**
@@ -7448,11 +7571,14 @@
      * requested orientation. If not, it may be necessary to letterbox the window.
      * @param parentBounds are the new parent bounds passed down to the activity and should be used
      *                     to compute the stable bounds.
-     * @param outBounds will store the stable bounds, which are the bounds with insets applied.
-     *                  These should be used to compute letterboxed bounds if orientation is not
-     *                  respected when insets are applied.
+     * @param outStableBounds will store the stable bounds, which are the bounds with insets
+     *                        applied, if orientation is not respected when insets are applied.
+     *                        Otherwise outStableBounds will be empty. Stable bounds should be used
+     *                        to compute letterboxed bounds if orientation is not respected when
+     *                        insets are applied.
      */
-    private boolean orientationRespectedWithInsets(Rect parentBounds, Rect outBounds) {
+    private boolean orientationRespectedWithInsets(Rect parentBounds, Rect outStableBounds) {
+        outStableBounds.setEmpty();
         if (mDisplayContent == null) {
             return true;
         }
@@ -7467,17 +7593,21 @@
         // Compute orientation from stable parent bounds (= parent bounds with insets applied)
         final Task task = getTask();
         task.calculateInsetFrames(mTmpOutNonDecorBounds /* outNonDecorBounds */,
-                outBounds /* outStableBounds */, parentBounds /* bounds */,
+                outStableBounds /* outStableBounds */, parentBounds /* bounds */,
                 mDisplayContent.getDisplayInfo());
-        final int orientationWithInsets = outBounds.height() >= outBounds.width()
+        final int orientationWithInsets = outStableBounds.height() >= outStableBounds.width()
                 ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
         // If orientation does not match the orientation with insets applied, then a
         // display rotation will not be enough to respect orientation. However, even if they do
         // not match but the orientation with insets applied matches the requested orientation, then
         // there is no need to modify the bounds because when insets are applied, the activity will
         // have the desired orientation.
-        return orientation == orientationWithInsets
+        final boolean orientationRespectedWithInsets = orientation == orientationWithInsets
                 || orientationWithInsets == requestedOrientation;
+        if (orientationRespectedWithInsets) {
+            outStableBounds.setEmpty();
+        }
+        return orientationRespectedWithInsets;
     }
 
     /**
@@ -7495,9 +7625,10 @@
             int windowingMode) {
         mLetterboxBoundsForFixedOrientationAndAspectRatio = null;
         final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
-        final Rect containerBounds = new Rect(parentBounds);
+        final Rect stableBounds = new Rect();
+        // If orientation is respected when insets are applied, then stableBounds will be empty.
         boolean orientationRespectedWithInsets =
-                orientationRespectedWithInsets(parentBounds, containerBounds);
+                orientationRespectedWithInsets(parentBounds, stableBounds);
         if (handlesOrientationChangeFromDescendant() && orientationRespectedWithInsets) {
             // No need to letterbox because of fixed orientation. Display will handle
             // fixed-orientation requests and a display rotation is enough to respect requested
@@ -7534,71 +7665,68 @@
             return;
         }
 
-        // TODO(b/182268157) merge aspect ratio logic here and in
-        // {@link ActivityRecord#applyAspectRatio}
-        // if no aspect ratio constraints are provided, parent aspect ratio is used
-        float aspectRatio = computeAspectRatio(parentBounds);
+        // TODO(b/182268157): Explore using only one type of parentBoundsWithInsets, either app
+        // bounds or stable bounds to unify aspect ratio logic.
+        final Rect parentBoundsWithInsets = orientationRespectedWithInsets
+                ? newParentConfig.windowConfiguration.getAppBounds() : stableBounds;
+        final Rect containingBounds = new Rect();
+        final Rect containingBoundsWithInsets = new Rect();
+        // Need to shrink the containing bounds into a square because the parent orientation
+        // does not match the activity requested orientation.
+        if (forcedOrientation == ORIENTATION_LANDSCAPE) {
+            // Landscape is defined as width > height. Make the container respect landscape
+            // orientation by shrinking height to one less than width. Landscape activity will be
+            // vertically centered within parent bounds with insets, so position vertical bounds
+            // within parent bounds with insets to prevent insets from unnecessarily trimming
+            // vertical bounds.
+            final int bottom = Math.min(parentBoundsWithInsets.top + parentBounds.width() - 1,
+                    parentBoundsWithInsets.bottom);
+            containingBounds.set(parentBounds.left, parentBoundsWithInsets.top, parentBounds.right,
+                    bottom);
+            containingBoundsWithInsets.set(parentBoundsWithInsets.left, parentBoundsWithInsets.top,
+                    parentBoundsWithInsets.right, bottom);
+        } else {
+            // Portrait is defined as width <= height. Make the container respect portrait
+            // orientation by shrinking width to match height. Portrait activity will be
+            // horizontally centered within parent bounds with insets, so position horizontal bounds
+            // within parent bounds with insets to prevent insets from unnecessarily trimming
+            // horizontal bounds.
+            final int right = Math.min(parentBoundsWithInsets.left + parentBounds.height(),
+                    parentBoundsWithInsets.right);
+            containingBounds.set(parentBoundsWithInsets.left, parentBounds.top, right,
+                    parentBounds.bottom);
+            containingBoundsWithInsets.set(parentBoundsWithInsets.left, parentBoundsWithInsets.top,
+                    right, parentBoundsWithInsets.bottom);
+        }
+
+        // Store the current bounds to be able to revert to size compat mode values below if needed.
+        final Rect prevResolvedBounds = new Rect(resolvedBounds);
+        resolvedBounds.set(containingBounds);
 
         // Override from config_fixedOrientationLetterboxAspectRatio or via ADB with
         // set-fixed-orientation-letterbox-aspect-ratio.
         final float letterboxAspectRatioOverride =
                 mWmService.mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio();
-        aspectRatio = letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
-                ? letterboxAspectRatioOverride : aspectRatio;
+        final float desiredAspectRatio =
+                letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
+                        ? letterboxAspectRatioOverride : computeAspectRatio(parentBounds);
+        // Apply aspect ratio to resolved bounds
+        mIsAspectRatioApplied = applyAspectRatio(resolvedBounds, containingBoundsWithInsets,
+                containingBounds, desiredAspectRatio, true);
 
-        // Adjust the fixed orientation letterbox bounds to fit the app request aspect ratio in
-        // order to use the extra available space.
-        final float maxAspectRatio = info.getMaxAspectRatio();
-        final float minAspectRatio = info.getMinAspectRatio();
-        if (aspectRatio > maxAspectRatio && maxAspectRatio != 0) {
-            aspectRatio = maxAspectRatio;
-        } else if (aspectRatio < minAspectRatio) {
-            aspectRatio = minAspectRatio;
-        }
-
-        // Store the current bounds to be able to revert to size compat mode values below if needed.
-        final Rect prevResolvedBounds = new Rect(resolvedBounds);
-
-        // Compute other dimension based on aspect ratio. Use bounds intersected with insets, stored
-        // in containerBounds after calling {@link ActivityRecord#orientationRespectedWithInsets()},
-        // to ensure that aspect ratio is respected after insets are applied.
-        int activityWidth;
-        int activityHeight;
+        // Vertically center if orientation is landscape. Center within parent bounds with insets
+        // to ensure that insets do not trim height. Bounds will later be horizontally centered in
+        // {@link updateResolvedBoundsHorizontalPosition()} regardless of orientation.
         if (forcedOrientation == ORIENTATION_LANDSCAPE) {
-            activityWidth = parentBounds.width();
-            // Compute height from stable bounds width to ensure orientation respected after insets.
-            activityHeight = (int) Math.rint(containerBounds.width() / aspectRatio);
-            // Landscape is defined as width > height. To ensure activity is landscape when aspect
-            // ratio is close to 1, reduce the height by one pixel.
-            if (activityWidth == activityHeight) {
-                activityHeight -= 1;
-            }
-            // Center vertically within stable bounds in landscape to ensure insets do not trim
-            // height.
-            final int top = containerBounds.centerY() - activityHeight / 2;
-            resolvedBounds.set(parentBounds.left, top, parentBounds.right, top + activityHeight);
-        } else {
-            activityHeight = parentBounds.height();
-            // Compute width from stable bounds height to ensure orientation respected after insets.
-            activityWidth = (int) Math.rint(containerBounds.height() / aspectRatio);
-            // Center horizontally in portrait. For now, align to left and allow
-            // {@link ActivityRecord#updateResolvedBoundsHorizontalPosition()} to center
-            // horizontally. Exclude left insets from parent to ensure cutout does not trim width.
-            final Rect parentAppBounds = newParentConfig.windowConfiguration.getAppBounds();
-            resolvedBounds.set(parentAppBounds.left, parentBounds.top,
-                    parentAppBounds.left + activityWidth, parentBounds.bottom);
+            final int offsetY = parentBoundsWithInsets.centerY() - resolvedBounds.centerY();
+            resolvedBounds.offset(0, offsetY);
         }
 
         if (mCompatDisplayInsets != null) {
             mCompatDisplayInsets.getBoundsByRotation(
                     mTmpBounds, newParentConfig.windowConfiguration.getRotation());
-            // Insets may differ between different rotations, for example in the case of a display
-            // cutout. To ensure consistent bounds across rotations, compare the activity dimensions
-            // minus insets from the rotation the compat bounds were computed in.
-            Task.intersectWithInsetsIfFits(mTmpBounds, parentBounds,
-                    mCompatDisplayInsets.mStableInsets[mCompatDisplayInsets.mOriginalRotation]);
-            if (activityWidth != mTmpBounds.width()
-                    || activityHeight != mTmpBounds.height()) {
+            if (resolvedBounds.width() != mTmpBounds.width()
+                    || resolvedBounds.height() != mTmpBounds.height()) {
                 // The app shouldn't be resized, we only do fixed orientation letterboxing if the
                 // compat bounds are also from the same fixed orientation letterbox. Otherwise,
                 // clear the fixed orientation bounds to show app in size compat mode.
@@ -7634,8 +7762,6 @@
         // then they should be aligned later in #updateResolvedBoundsHorizontalPosition().
         if (!mTmpBounds.isEmpty()) {
             resolvedBounds.set(mTmpBounds);
-            // Exclude the horizontal decor area.
-            resolvedBounds.left = parentAppBounds.left;
         }
         if (!resolvedBounds.isEmpty() && !resolvedBounds.equals(parentBounds)) {
             // Compute the configuration based on the resolved bounds. If aspect ratio doesn't
@@ -7696,13 +7822,6 @@
             mIsAspectRatioApplied =
                     applyAspectRatio(resolvedBounds, containingAppBounds, containingBounds);
         }
-        // If the bounds are restricted by fixed aspect ratio, the resolved bounds should be put in
-        // the container app bounds. Otherwise the entire container bounds are available.
-        final boolean fillContainer = resolvedBounds.equals(containingBounds);
-        if (!fillContainer) {
-            // The horizontal position should not cover insets.
-            resolvedBounds.left = containingAppBounds.left;
-        }
 
         // Use resolvedBounds to compute other override configurations such as appBounds. The bounds
         // are calculated in compat container space. The actual position on screen will be applied
@@ -7769,6 +7888,7 @@
         // Align to top of parent (bounds) - this is a UX choice and exclude the horizontal decor
         // if needed. Horizontal position is adjusted in updateResolvedBoundsHorizontalPosition.
         // Above coordinates are in "@" space, now place "*" and "#" to screen space.
+        final boolean fillContainer = resolvedBounds.equals(containingBounds);
         final int screenPosX = fillContainer ? containerBounds.left : containerAppBounds.left;
         final int screenPosY = containerBounds.top;
         if (screenPosX != 0 || screenPosY != 0) {
@@ -7996,6 +8116,13 @@
         }
     }
 
+    @Override
+    void onResize() {
+        // Reset freezing IME insets flag when the activity resized.
+        mImeInsetsFrozenUntilStartInput = false;
+        super.onResize();
+    }
+
     /** Returns true if the configuration is compatible with this activity. */
     boolean isConfigurationCompatible(Configuration config) {
         final int orientation = getRequestedOrientation();
@@ -8010,6 +8137,12 @@
         return true;
     }
 
+    private boolean applyAspectRatio(Rect outBounds, Rect containingAppBounds,
+            Rect containingBounds) {
+        return applyAspectRatio(outBounds, containingAppBounds, containingBounds,
+                0 /* desiredAspectRatio */, false /* fixedOrientationLetterboxed */);
+    }
+
     /**
      * Applies aspect ratio restrictions to outBounds. If no restrictions, then no change is
      * made to outBounds.
@@ -8018,17 +8151,19 @@
      */
     // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
     private boolean applyAspectRatio(Rect outBounds, Rect containingAppBounds,
-            Rect containingBounds) {
+            Rect containingBounds, float desiredAspectRatio, boolean fixedOrientationLetterboxed) {
         final float maxAspectRatio = info.getMaxAspectRatio();
         final Task rootTask = getRootTask();
         final float minAspectRatio = info.getMinAspectRatio();
 
         if (task == null || rootTask == null
-                || (inMultiWindowMode() && !shouldCreateCompatDisplayInsets())
-                || (maxAspectRatio == 0 && minAspectRatio == 0)
+                || (inMultiWindowMode() && !shouldCreateCompatDisplayInsets()
+                && !fixedOrientationLetterboxed)
+                || (maxAspectRatio < 1 && minAspectRatio < 1 && desiredAspectRatio < 1)
                 || isInVrUiMode(getConfiguration())) {
-            // We don't enforce aspect ratio if the activity task is in multiwindow unless it
-            // is in size-compat mode. We also don't set it if we are in VR mode.
+            // We don't enforce aspect ratio if the activity task is in multiwindow unless it is in
+            // size-compat mode or is letterboxed from fixed orientation. We also don't set it if we
+            // are in VR mode.
             return false;
         }
 
@@ -8036,20 +8171,30 @@
         final int containingAppHeight = containingAppBounds.height();
         final float containingRatio = computeAspectRatio(containingAppBounds);
 
+        if (desiredAspectRatio < 1) {
+            desiredAspectRatio = containingRatio;
+        }
+
+        if (maxAspectRatio >= 1 && desiredAspectRatio > maxAspectRatio) {
+            desiredAspectRatio = maxAspectRatio;
+        } else if (minAspectRatio >= 1 && desiredAspectRatio < minAspectRatio) {
+            desiredAspectRatio = minAspectRatio;
+        }
+
         int activityWidth = containingAppWidth;
         int activityHeight = containingAppHeight;
 
-        if (containingRatio > maxAspectRatio && maxAspectRatio != 0) {
+        if (containingRatio > desiredAspectRatio) {
             if (containingAppWidth < containingAppHeight) {
                 // Width is the shorter side, so we use that to figure-out what the max. height
                 // should be given the aspect ratio.
-                activityHeight = (int) ((activityWidth * maxAspectRatio) + 0.5f);
+                activityHeight = (int) ((activityWidth * desiredAspectRatio) + 0.5f);
             } else {
                 // Height is the shorter side, so we use that to figure-out what the max. width
                 // should be given the aspect ratio.
-                activityWidth = (int) ((activityHeight * maxAspectRatio) + 0.5f);
+                activityWidth = (int) ((activityHeight * desiredAspectRatio) + 0.5f);
             }
-        } else if (containingRatio < minAspectRatio) {
+        } else if (containingRatio < desiredAspectRatio) {
             boolean adjustWidth;
             switch (getRequestedConfigurationOrientation()) {
                 case ORIENTATION_LANDSCAPE:
@@ -8077,9 +8222,9 @@
                     break;
             }
             if (adjustWidth) {
-                activityWidth = (int) ((activityHeight / minAspectRatio) + 0.5f);
+                activityWidth = (int) ((activityHeight / desiredAspectRatio) + 0.5f);
             } else {
-                activityHeight = (int) ((activityWidth / minAspectRatio) + 0.5f);
+                activityHeight = (int) ((activityWidth / desiredAspectRatio) + 0.5f);
             }
         }
 
@@ -8103,6 +8248,13 @@
         }
         outBounds.set(containingBounds.left, containingBounds.top, right, bottom);
 
+        // If the bounds are restricted by fixed aspect ratio, then out bounds should be put in the
+        // container app bounds. Otherwise the entire container bounds are available.
+        if (!outBounds.equals(containingBounds)) {
+            // The horizontal position should not cover insets (e.g. display cutout).
+            outBounds.left = containingAppBounds.left;
+        }
+
         return true;
     }
 
@@ -9069,7 +9221,7 @@
                 record.mAdapter.mRootTaskBounds, task.getWindowConfiguration(),
                 false /*isNotInRecents*/,
                 record.mThumbnailAdapter != null ? record.mThumbnailAdapter.mCapturedLeash : null,
-                record.mStartBounds, task.getTaskInfo());
+                record.mStartBounds, task.getTaskInfo(), checkEnterPictureInPictureAppOpsState());
     }
 
     @Override
@@ -9113,6 +9265,11 @@
         super.finishSync(outMergedTransaction, cancel);
     }
 
+    @Override
+    boolean canBeAnimationTarget() {
+        return true;
+    }
+
     static class Builder {
         private final ActivityTaskManagerService mAtmService;
         private WindowProcessController mCallerApp;
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 4e73bf3..af538272 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -194,7 +194,6 @@
     private Task mTargetTask;
     private boolean mMovedToFront;
     private boolean mNoAnimation;
-    private boolean mKeepCurTransition;
     private boolean mAvoidMoveToFront;
     private boolean mFrozeTaskList;
     private boolean mTransientLaunch;
@@ -593,7 +592,6 @@
         mTargetRootTask = starter.mTargetRootTask;
         mMovedToFront = starter.mMovedToFront;
         mNoAnimation = starter.mNoAnimation;
-        mKeepCurTransition = starter.mKeepCurTransition;
         mAvoidMoveToFront = starter.mAvoidMoveToFront;
         mFrozeTaskList = starter.mFrozeTaskList;
 
@@ -1511,7 +1509,7 @@
         final Task targetTask = r.getTask() != null
                 ? r.getTask()
                 : mTargetTask;
-        if (startedActivityRootTask == null || targetTask == null) {
+        if (startedActivityRootTask == null || targetTask == null || !targetTask.isAttached()) {
             return;
         }
 
@@ -1708,6 +1706,8 @@
 
         mIntent.setFlags(mLaunchFlags);
 
+        // Get top task at beginning because the order may be changed when reusing existing task.
+        final Task prevTopTask = mPreferredTaskDisplayArea.getFocusedRootTask();
         final Task reusedTask = getReusableTask();
 
         // If requested, freeze the task list
@@ -1787,24 +1787,23 @@
                     UserHandle.getAppId(mStartActivity.info.applicationInfo.uid) /*recipient*/,
                     resultToUid /*visible*/, true /*direct*/);
         }
+        final Task startedTask = mStartActivity.getTask();
         if (newTask) {
-            EventLogTags.writeWmCreateTask(mStartActivity.mUserId,
-                    mStartActivity.getTask().mTaskId);
+            EventLogTags.writeWmCreateTask(mStartActivity.mUserId, startedTask.mTaskId);
         }
-        mStartActivity.logStartActivity(
-                EventLogTags.WM_CREATE_ACTIVITY, mStartActivity.getTask());
+        mStartActivity.logStartActivity(EventLogTags.WM_CREATE_ACTIVITY, startedTask);
 
         mStartActivity.getTaskFragment().clearLastPausedActivity();
 
         mRootWindowContainer.startPowerModeLaunchIfNeeded(
                 false /* forceSend */, mStartActivity);
 
+        final boolean isTaskSwitch = startedTask != prevTopTask && !startedTask.isEmbedded();
         mTargetRootTask.startActivityLocked(mStartActivity,
                 topRootTask != null ? topRootTask.getTopNonFinishingActivity() : null, newTask,
-                mKeepCurTransition, mOptions, sourceRecord);
+                isTaskSwitch, mOptions, sourceRecord);
         if (mDoResume) {
-            final ActivityRecord topTaskActivity =
-                    mStartActivity.getTask().topRunningActivityLocked();
+            final ActivityRecord topTaskActivity = startedTask.topRunningActivityLocked();
             if (!mTargetRootTask.isTopActivityFocusable()
                     || (topTaskActivity != null && topTaskActivity.isTaskOverlay()
                     && mStartActivity != topTaskActivity)) {
@@ -1838,8 +1837,8 @@
         mRootWindowContainer.updateUserRootTask(mStartActivity.mUserId, mTargetRootTask);
 
         // Update the recent tasks list immediately when the activity starts
-        mSupervisor.mRecentTasks.add(mStartActivity.getTask());
-        mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(),
+        mSupervisor.mRecentTasks.add(startedTask);
+        mSupervisor.handleNonResizableTaskIfNeeded(startedTask,
                 mPreferredWindowingMode, mPreferredTaskDisplayArea, mTargetRootTask);
 
         return START_SUCCESS;
@@ -2278,7 +2277,6 @@
         mTargetTask = null;
         mMovedToFront = false;
         mNoAnimation = false;
-        mKeepCurTransition = false;
         mAvoidMoveToFront = false;
         mFrozeTaskList = false;
         mTransientLaunch = false;
@@ -2386,6 +2384,11 @@
             }
             mTransientLaunch = mOptions.getTransientLaunch();
             mTargetRootTask = Task.fromWindowContainerToken(mOptions.getLaunchRootTask());
+
+            if (inTaskFragment == null) {
+                inTaskFragment = TaskFragment.fromTaskFragmentToken(
+                        mOptions.getLaunchTaskFragmentToken(), mService);
+            }
         }
 
         mNotTop = (mLaunchFlags & FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? sourceRecord : null;
@@ -2679,7 +2682,7 @@
                             && mTargetRootTask != intentTask.getParent().asTask()) {
                         intentTask.getParent().positionChildAt(POSITION_TOP, intentTask,
                                 false /* includingParents */);
-                        intentTask = intentTask.getParent().asTask();
+                        intentTask = intentTask.getParent().asTaskFragment().getTask();
                     }
                     // If the task is in multi-windowing mode, the activity may already be on
                     // the top (visible to user but not the global top), then the result code
@@ -2781,7 +2784,8 @@
             }
         } else {
             // Use the child TaskFragment (if any) as the new parent if the activity can be embedded
-            final ActivityRecord top = task.topRunningActivity();
+            final ActivityRecord top = task.topRunningActivity(false /* focusableOnly */,
+                    false /* includingEmbeddedTask */);
             newParent = top != null ? top.getTaskFragment() : task;
         }
 
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 83c83e7..081c618 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1909,15 +1909,15 @@
 
         if (r.moveFocusableActivityToTop("setFocusedTask")) {
             mRootWindowContainer.resumeFocusedTasksTopActivities();
-        } else if (touchedActivity != null && touchedActivity != r
-                && touchedActivity.getTask() == r.getTask()
-                && touchedActivity.getTaskFragment() != r.getTaskFragment()) {
-            // Set the focused app directly since the focused window is not on the
-            // top-most TaskFragment of the top-most Task
-            final DisplayContent displayContent = touchedActivity.getDisplayContent();
-            displayContent.setFocusedApp(touchedActivity);
-            mWindowManager.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
-                    true /* updateInputWindows */);
+        } else if (touchedActivity != null && touchedActivity.isFocusable()) {
+            final TaskFragment parent = touchedActivity.getTaskFragment();
+            if (parent != null && parent.isEmbedded()) {
+                // Set the focused app directly if the focused window is currently embedded
+                final DisplayContent displayContent = touchedActivity.getDisplayContent();
+                displayContent.setFocusedApp(touchedActivity);
+                mWindowManager.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
+                        true /* updateInputWindows */);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index f728a48..e593c1c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -352,6 +352,12 @@
      */
     private int mVisibilityTransactionDepth;
 
+    /**
+     * Whether to the visibility updates that started from {@code RootWindowContainer} should be
+     * deferred.
+     */
+    private boolean mDeferRootVisibilityUpdate;
+
     private ActivityMetricsLogger mActivityMetricsLogger;
 
     /** Check if placing task or activity on specified display is allowed. */
@@ -841,6 +847,10 @@
                         proc.getThread(), r.appToken);
 
                 final boolean isTransitionForward = r.isTransitionForward();
+                IBinder fragmentToken = null;
+                if (r.getTaskFragment().getTaskFragmentOrganizerPid() == r.getPid()) {
+                    fragmentToken = r.getTaskFragment().getFragmentToken();
+                }
                 clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
                         System.identityHashCode(r), r.info,
                         // TODO: Have this take the merged configuration instead of separate global
@@ -852,7 +862,7 @@
                         r.takeOptions(), isTransitionForward,
                         proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController,
                         r.createFixedRotationAdjustmentsIfNeeded(), r.shareableActivityToken,
-                        r.getLaunchedFromBubble()));
+                        r.getLaunchedFromBubble(), fragmentToken));
 
                 // Set desired final state.
                 final ActivityLifecycleItem lifecycleItem;
@@ -1551,7 +1561,13 @@
             return;
         }
         if (task.isVisible()) {
-            mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_CLOSE, task);
+            if (mService.getTransitionController().isCollecting()) {
+                // We don't want the finishing to change the transition ready state since there will
+                // not be corresponding setReady for finishing.
+                mService.getTransitionController().collectExistenceChange(task);
+            } else {
+                mService.getTransitionController().requestTransitionIfNeeded(TRANSIT_CLOSE, task);
+            }
         } else {
             // Removing a non-visible task doesn't require a transition, but if there is one
             // collecting, this should be a member just in case.
@@ -2287,6 +2303,14 @@
         return mVisibilityTransactionDepth > 0;
     }
 
+    void setDeferRootVisibilityUpdate(boolean deferUpdate) {
+        mDeferRootVisibilityUpdate = deferUpdate;
+    }
+
+    boolean isRootVisibilityUpdateDeferred() {
+        return mDeferRootVisibilityUpdate;
+    }
+
     /**
      * Called when the state or visibility of an attached activity is changed.
      *
@@ -2572,7 +2596,10 @@
         }
 
         boolean matches(ActivityRecord r) {
-            return mTargetComponent.equals(r.mActivityComponent) || mLaunchingState.contains(r);
+            if (!mLaunchingState.hasActiveTransitionInfo()) {
+                return mTargetComponent.equals(r.mActivityComponent);
+            }
+            return mLaunchingState.contains(r);
         }
 
         void dump(PrintWriter pw, String prefix) {
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 174b396..929ac56f 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -40,6 +40,9 @@
 import static android.view.WindowManager.TRANSIT_OLD_NONE;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_BACK;
@@ -474,6 +477,7 @@
         mNextAppTransitionAnimationsSpecsFuture = null;
         mDefaultNextAppTransitionAnimationSpec = null;
         mAnimationFinishedCallback = null;
+        mOverrideTaskTransition = false;
         mNextAppTransitionIsSync = false;
     }
 
@@ -939,7 +943,7 @@
                     "applyAnimation NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS: "
                             + "anim=%s transit=%s isEntrance=true Callers=%s",
                     a, appTransitionOldToString(transit), Debug.getCallers(3));
-        } else if (transit == TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE) {
+        } else if (isChangeTransitOld(transit)) {
             // In the absence of a specific adapter, we just want to keep everything stationary.
             a = new AlphaAnimation(1.f, 1.f);
             a.setDuration(WindowChangeAnimationSpec.ANIMATION_DURATION);
@@ -1005,6 +1009,21 @@
                     animAttr = enter
                             ? WindowAnimation_launchTaskBehindSourceAnimation
                             : WindowAnimation_launchTaskBehindTargetAnimation;
+                    break;
+                // TODO(b/189386466): Use activity transition as the fallback. Investigate if we
+                //  need new TaskFragment transition.
+                case TRANSIT_OLD_TASK_FRAGMENT_OPEN:
+                    animAttr = enter
+                            ? WindowAnimation_activityOpenEnterAnimation
+                            : WindowAnimation_activityOpenExitAnimation;
+                    break;
+                // TODO(b/189386466): Use activity transition as the fallback. Investigate if we
+                //  need new TaskFragment transition.
+                case TRANSIT_OLD_TASK_FRAGMENT_CLOSE:
+                    animAttr = enter
+                            ? WindowAnimation_activityCloseEnterAnimation
+                            : WindowAnimation_activityCloseExitAnimation;
+                    break;
             }
             a = animAttr != 0 ? loadAnimationAttr(lp, animAttr, transit) : null;
             ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
@@ -1315,6 +1334,15 @@
             case TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE: {
                 return "TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE";
             }
+            case TRANSIT_OLD_TASK_FRAGMENT_OPEN: {
+                return "TRANSIT_OLD_TASK_FRAGMENT_OPEN";
+            }
+            case TRANSIT_OLD_TASK_FRAGMENT_CLOSE: {
+                return "TRANSIT_OLD_TASK_FRAGMENT_CLOSE";
+            }
+            case TRANSIT_OLD_TASK_FRAGMENT_CHANGE: {
+                return "TRANSIT_OLD_TASK_FRAGMENT_CHANGE";
+            }
             default: {
                 return "<UNKNOWN: " + transition + ">";
             }
@@ -1572,7 +1600,8 @@
     }
 
     static boolean isChangeTransitOld(@TransitionOldType int transit) {
-        return transit == TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
+        return transit == TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE
+                || transit == TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
     }
 
     static boolean isClosingTransitOld(@TransitionOldType int transit) {
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index c869ec6..544cade 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -39,6 +39,9 @@
 import static android.view.WindowManager.TRANSIT_OLD_NONE;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_BACK;
@@ -68,6 +71,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.os.Trace;
 import android.util.ArrayMap;
@@ -86,6 +90,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.ProtoLog;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.function.Predicate;
@@ -102,6 +108,20 @@
     private RemoteAnimationDefinition mRemoteAnimationDefinition = null;
     private static final int KEYGUARD_GOING_AWAY_ANIMATION_DURATION = 400;
 
+    private static final int TYPE_NONE = 0;
+    private static final int TYPE_ACTIVITY = 1;
+    private static final int TYPE_TASK_FRAGMENT = 2;
+    private static final int TYPE_TASK = 3;
+
+    @IntDef(prefix = { "TYPE_" }, value = {
+            TYPE_NONE,
+            TYPE_ACTIVITY,
+            TYPE_TASK_FRAGMENT,
+            TYPE_TASK
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface TransitContainerType {}
+
     private final ArrayMap<WindowContainer, Integer> mTempTransitionReasons = new ArrayMap<>();
 
     AppTransitionController(WindowManagerService service, DisplayContent displayContent) {
@@ -185,8 +205,8 @@
                 mDisplayContent.mOpeningApps);
 
         final @TransitionOldType int transit = getTransitCompatType(
-                mDisplayContent.mAppTransition,
-                mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+                mDisplayContent.mAppTransition, mDisplayContent.mOpeningApps,
+                mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers,
                 mWallpaperControllerLocked.getWallpaperTarget(), getOldWallpaper(),
                 mDisplayContent.mSkipAppTransitionAnimation);
         mDisplayContent.mSkipAppTransitionAnimation = false;
@@ -267,6 +287,7 @@
      * @param appTransition {@link AppTransition} for managing app transition state.
      * @param openingApps {@link ActivityRecord}s which are becoming visible.
      * @param closingApps {@link ActivityRecord}s which are becoming invisible.
+     * @param changingContainers {@link WindowContainer}s which are changed in configuration.
      * @param wallpaperTarget If non-null, this is the currently visible window that is associated
      *                        with the wallpaper.
      * @param oldWallpaper The currently visible window that is associated with the wallpaper in
@@ -275,8 +296,8 @@
      */
     static @TransitionOldType int getTransitCompatType(AppTransition appTransition,
             ArraySet<ActivityRecord> openingApps, ArraySet<ActivityRecord> closingApps,
-            @Nullable WindowState wallpaperTarget, @Nullable WindowState oldWallpaper,
-            boolean skipAppTransitionAnimation) {
+            ArraySet<WindowContainer> changingContainers, @Nullable WindowState wallpaperTarget,
+            @Nullable WindowState oldWallpaper, boolean skipAppTransitionAnimation) {
 
         // Determine if closing and opening app token sets are wallpaper targets, in which case
         // special animations are needed.
@@ -309,8 +330,18 @@
 
         // Special transitions
         // TODO(new-app-transitions): Revisit if those can be rewritten by using flags.
-        if (appTransition.containsTransitRequest(TRANSIT_CHANGE)) {
-            return TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
+        if (appTransition.containsTransitRequest(TRANSIT_CHANGE) && !changingContainers.isEmpty()) {
+            @TransitContainerType int changingType =
+                    getTransitContainerType(changingContainers.valueAt(0));
+            switch (changingType) {
+                case TYPE_TASK:
+                    return TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
+                case TYPE_TASK_FRAGMENT:
+                    return TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
+                default:
+                    throw new IllegalStateException(
+                            "TRANSIT_CHANGE with unrecognized changing type=" + changingType);
+            }
         }
         if ((flags & TRANSIT_FLAG_APP_CRASHED) != 0) {
             return TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
@@ -387,33 +418,38 @@
                 openingApps, closingApps, true /* visible */);
         final ArraySet<WindowContainer> closingWcs = getAnimationTargets(
                 openingApps, closingApps, false /* visible */);
-        final boolean isActivityOpening = !openingWcs.isEmpty()
-                && openingWcs.valueAt(0).asActivityRecord() != null;
-        final boolean isActivityClosing = !closingWcs.isEmpty()
-                && closingWcs.valueAt(0).asActivityRecord() != null;
-        final boolean isTaskOpening = !openingWcs.isEmpty() && !isActivityOpening;
-        final boolean isTaskClosing = !closingWcs.isEmpty() && !isActivityClosing;
-
-        if (appTransition.containsTransitRequest(TRANSIT_TO_FRONT) && isTaskOpening) {
+        final WindowContainer<?> openingContainer = !openingWcs.isEmpty()
+                ? openingWcs.valueAt(0) : null;
+        final WindowContainer<?> closingContainer = !closingWcs.isEmpty()
+                ? closingWcs.valueAt(0) : null;
+        @TransitContainerType int openingType = getTransitContainerType(openingContainer);
+        @TransitContainerType int closingType = getTransitContainerType(closingContainer);
+        if (appTransition.containsTransitRequest(TRANSIT_TO_FRONT) && openingType == TYPE_TASK) {
             return TRANSIT_OLD_TASK_TO_FRONT;
         }
-        if (appTransition.containsTransitRequest(TRANSIT_TO_BACK) && isTaskClosing) {
+        if (appTransition.containsTransitRequest(TRANSIT_TO_BACK) && closingType == TYPE_TASK) {
             return TRANSIT_OLD_TASK_TO_BACK;
         }
         if (appTransition.containsTransitRequest(TRANSIT_OPEN)) {
-            if (isTaskOpening) {
+            if (openingType == TYPE_TASK) {
                 return (appTransition.getTransitFlags() & TRANSIT_FLAG_OPEN_BEHIND) != 0
                         ? TRANSIT_OLD_TASK_OPEN_BEHIND : TRANSIT_OLD_TASK_OPEN;
             }
-            if (isActivityOpening) {
+            if (openingType == TYPE_ACTIVITY) {
                 return TRANSIT_OLD_ACTIVITY_OPEN;
             }
+            if (openingType == TYPE_TASK_FRAGMENT) {
+                return TRANSIT_OLD_TASK_FRAGMENT_OPEN;
+            }
         }
         if (appTransition.containsTransitRequest(TRANSIT_CLOSE)) {
-            if (isTaskClosing) {
+            if (closingType == TYPE_TASK) {
                 return TRANSIT_OLD_TASK_CLOSE;
             }
-            if (isActivityClosing) {
+            if (closingType == TYPE_TASK_FRAGMENT) {
+                return TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
+            }
+            if (closingType == TYPE_ACTIVITY) {
                 for (int i = closingApps.size() - 1; i >= 0; i--) {
                     if (closingApps.valueAt(i).visibleIgnoringKeyguard) {
                         return TRANSIT_OLD_ACTIVITY_CLOSE;
@@ -430,6 +466,23 @@
         return TRANSIT_OLD_NONE;
     }
 
+    @TransitContainerType
+    private static int getTransitContainerType(@Nullable WindowContainer<?> container) {
+        if (container == null) {
+            return TYPE_NONE;
+        }
+        if (container.asTask() != null) {
+            return TYPE_TASK;
+        }
+        if (container.asTaskFragment() != null) {
+            return TYPE_TASK_FRAGMENT;
+        }
+        if (container.asActivityRecord() != null) {
+            return TYPE_ACTIVITY;
+        }
+        return TYPE_NONE;
+    }
+
     private static WindowManager.LayoutParams getAnimLp(ActivityRecord activity) {
         final WindowState mainWindow = activity != null ? activity.findMainWindow() : null;
         return mainWindow != null ? mainWindow.mAttrs : null;
@@ -464,7 +517,8 @@
         }
         final RemoteAnimationAdapter adapter =
                 getRemoteAnimationOverride(animLpActivity, transit, activityTypes);
-        if (adapter != null) {
+        if (adapter != null
+                && mDisplayContent.mAppTransition.getRemoteAnimationController() == null) {
             mDisplayContent.mAppTransition.overridePendingAppTransitionRemote(adapter);
         }
     }
@@ -632,6 +686,7 @@
             boolean canPromote = true;
 
             if (parent == null || !parent.canCreateRemoteAnimationTarget()
+                    || !parent.canBeAnimationTarget()
                     // We cannot promote the animation on Task's parent when the task is in
                     // clearing task in case the animating get stuck when performing the opening
                     // task that behind it.
diff --git a/services/core/java/com/android/server/wm/BlurController.java b/services/core/java/com/android/server/wm/BlurController.java
index 0363944..41d9dbf 100644
--- a/services/core/java/com/android/server/wm/BlurController.java
+++ b/services/core/java/com/android/server/wm/BlurController.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.os.PowerManager.THERMAL_STATUS_CRITICAL;
 import static android.view.CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED;
 
 import android.content.BroadcastReceiver;
@@ -45,6 +46,7 @@
     private final Object mLock = new Object();
     private volatile boolean mBlurEnabled;
     private boolean mInPowerSaveMode;
+    private boolean mCriticalThermalStatus;
     private boolean mBlurDisabledSetting;
     private boolean mTunnelModeEnabled = false;
 
@@ -89,6 +91,12 @@
                 });
         mBlurDisabledSetting = getBlurDisabledSetting();
 
+        powerManager.addThermalStatusListener((status) -> {
+            mCriticalThermalStatus = status >= THERMAL_STATUS_CRITICAL;
+            updateBlurEnabled();
+        });
+        mCriticalThermalStatus = powerManager.getCurrentThermalStatus() >= THERMAL_STATUS_CRITICAL;
+
         TunnelModeEnabledListener.register(mTunnelModeListener);
 
         updateBlurEnabled();
@@ -112,7 +120,7 @@
     private void updateBlurEnabled() {
         synchronized (mLock) {
             final boolean newEnabled = CROSS_WINDOW_BLUR_SUPPORTED && !mBlurDisabledSetting
-                    && !mInPowerSaveMode && !mTunnelModeEnabled;
+                    && !mInPowerSaveMode && !mTunnelModeEnabled && !mCriticalThermalStatus;
             if (mBlurEnabled == newEnabled) {
                 return;
             }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 369e58c..62a6f39 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -134,7 +134,6 @@
 import static com.android.server.wm.WindowManagerService.H.WINDOW_HIDE_TIMEOUT;
 import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
-import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_ASSIGN_LAYERS;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
 import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT;
@@ -299,6 +298,22 @@
      */
     private final SurfaceControl mWindowingLayer;
 
+    /**
+     * The window token of the layer of the hierarchy to mirror, or null if this DisplayContent
+     * is not being used for layer mirroring.
+     */
+    @VisibleForTesting IBinder mTokenToMirror = null;
+
+    /**
+     * The surface for mirroring the contents of this hierarchy.
+     */
+    private SurfaceControl mMirroredSurface = null;
+
+    /**
+     * The last bounds of the DisplayArea to mirror.
+     */
+    private Rect mLastMirroredDisplayAreaBounds = null;
+
     // Contains all IME window containers. Note that the z-ordering of the IME windows will depend
     // on the IME target. We mainly have this container grouping so we can keep track of all the IME
     // window containers together and move them in-sync if/when needed. We use a subclass of
@@ -434,6 +449,7 @@
     // Accessed directly by all users.
     private boolean mLayoutNeeded;
     int pendingLayoutChanges;
+    boolean mLayoutAndAssignWindowLayersScheduled;
 
     /**
      * Used to gate application window layout until we have sent the complete configuration.
@@ -734,13 +750,20 @@
 
         // When switching the app task, we keep the IME window visibility for better
         // transitioning experiences.
-        // However, in case IME created a child window without dismissing during the task
-        // switching to keep the window focus because IME window has higher window hierarchy,
-        // we don't give it focus if the next IME layering target doesn't request IME visible.
+        // However, in case IME created a child window or the IME selection dialog without
+        // dismissing during the task switching to keep the window focus because IME window has
+        // higher window hierarchy, we don't give it focus if the next IME layering target
+        // doesn't request IME visible.
         if (w.mIsImWindow && w.isChildWindow() && (mImeLayeringTarget == null
                 || !mImeLayeringTarget.getRequestedVisibility(ITYPE_IME))) {
             return false;
         }
+        if (w.mAttrs.type == TYPE_INPUT_METHOD_DIALOG && mImeLayeringTarget != null
+                && !mImeLayeringTarget.getRequestedVisibility(ITYPE_IME)
+                && mImeLayeringTarget.isAnimating(PARENTS | TRANSITION,
+                ANIMATION_TYPE_APP_TRANSITION)) {
+            return false;
+        }
 
         final ActivityRecord activity = w.mActivityRecord;
 
@@ -770,10 +793,19 @@
                 return true;
             }
 
-            if (focusedApp.getTask() == activity.getTask()
-                    && focusedApp.getTaskFragment() != activity.getTaskFragment()) {
-                // Do not use the activity window of another TaskFragment in the same leaf Task
-                return false;
+            // If the candidate activity is currently being embedded in the focused task, the
+            // activity cannot be focused unless it is on the same TaskFragment as the focusedApp's.
+            TaskFragment parent = activity.getTaskFragment();
+            if (parent != null && parent.isEmbedded()) {
+                Task hostTask = focusedApp.getTask();
+                if (hostTask.isEmbedded()) {
+                    // Use the hosting task if the current task is embedded.
+                    hostTask = hostTask.getParent().asTaskFragment().getTask();
+                }
+                if (activity.isDescendantOf(hostTask)
+                        && activity.getTaskFragment() != focusedApp.getTaskFragment()) {
+                    return false;
+                }
             }
         }
 
@@ -1110,6 +1142,10 @@
         if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Creating display=" + display);
 
         mWmService.mDisplayWindowSettings.applySettingsToDisplayLocked(this);
+
+        // Check if this DisplayContent is for a new VirtualDisplay, that should use layer mirroring
+        // to capture the contents of a DisplayArea.
+        startMirrorIfNeeded();
     }
 
     boolean isReady() {
@@ -1593,8 +1629,10 @@
             // If the transition has not started yet, the activity must be the top.
             return false;
         }
-        if (mLastWallpaperVisible && r.windowsCanBeWallpaperTarget()) {
-            // Use normal rotation animation for orientation change of visible wallpaper.
+        if (mLastWallpaperVisible && r.windowsCanBeWallpaperTarget()
+                && mFixedRotationTransitionListener.mAnimatingRecents == null) {
+            // Use normal rotation animation for orientation change of visible wallpaper if recents
+            // animation is not running (it may be swiping to home).
             return false;
         }
         final int rotation = rotationForActivityInDifferentOrientation(r);
@@ -2464,6 +2502,25 @@
         // Update IME parent if needed.
         updateImeParent();
 
+        // Update mirroring surface for MediaProjection, if this DisplayContent is being used
+        // for layer mirroring.
+        if (mMirroredSurface != null) {
+            // Retrieve the size of the DisplayArea to mirror, and continue with the update if the
+            // bounds have changed.
+            final WindowContainer wc = mWmService.mWindowContextListenerController.getContainer(
+                    mTokenToMirror);
+            if (wc != null && mLastMirroredDisplayAreaBounds != null) {
+                // Retrieve the size of the DisplayArea to mirror, and continue with the update
+                // if the bounds or orientation has changed.
+                final Rect displayAreaBounds = wc.getDisplayContent().getBounds();
+                int displayAreaOrientation = wc.getDisplayContent().getOrientation();
+                if (!mLastMirroredDisplayAreaBounds.equals(displayAreaBounds)
+                        || lastOrientation != displayAreaOrientation) {
+                    updateMirroredSurface(mWmService.mTransactionFactory.get(), displayAreaBounds);
+                }
+            }
+        }
+
         if (lastOrientation != getConfiguration().orientation) {
             getMetricsLogger().write(
                     new LogMaker(MetricsEvent.ACTION_PHONE_ORIENTATION_CHANGED)
@@ -3424,13 +3481,12 @@
             }
         }
 
-        int focusChanged = getDisplayPolicy().focusChangedLw(oldFocus, newFocus);
+        getDisplayPolicy().focusChangedLw(oldFocus, newFocus);
 
         if (imWindowChanged && oldFocus != mInputMethodWindow) {
             // Focus of the input method window changed. Perform layout if needed.
             if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
                 performLayout(true /*initial*/,  updateInputWindows);
-                focusChanged &= ~FINISH_LAYOUT_REDO_LAYOUT;
             } else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {
                 // Client will do the layout, but we need to assign layers
                 // for handleNewWindowLocked() below.
@@ -3438,16 +3494,6 @@
             }
         }
 
-        if ((focusChanged & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
-            // The change in focus caused us to need to do a layout.  Okay.
-            setLayoutNeeded();
-            if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
-                performLayout(true /*initial*/, updateInputWindows);
-            } else if (mode == UPDATE_FOCUS_REMOVING_FOCUS) {
-                mWmService.mRoot.performSurfacePlacement();
-            }
-        }
-
         if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
             // If we defer assigning layers, then the caller is responsible for doing this part.
             getInputMonitor().setInputFocusLw(newFocus, updateInputWindows);
@@ -3784,6 +3830,23 @@
         return mWmService.mForceDesktopModeOnExternalDisplays && !isDefaultDisplay && !isPrivate();
     }
 
+    /** @see WindowManagerInternal#onToggleImeRequested */
+    void onShowImeRequested() {
+        if (mImeLayeringTarget == null || mInputMethodWindow == null) {
+            return;
+        }
+        // If IME window will be shown on the rotated activity, share the transformed state to
+        // IME window so it can compute rotated frame with rotated configuration.
+        if (mImeLayeringTarget.mToken.isFixedRotationTransforming()) {
+            mInputMethodWindow.mToken.linkFixedRotationTransform(mImeLayeringTarget.mToken);
+            // Hide the window until the rotation is done to avoid intermediate artifacts if the
+            // parent surface of IME container is changed.
+            if (mFadeRotationAnimationController != null) {
+                mFadeRotationAnimationController.hideImmediately(mInputMethodWindow.mToken);
+            }
+        }
+    }
+
     @VisibleForTesting
     void setImeLayeringTarget(WindowState target) {
         mImeLayeringTarget = target;
@@ -3959,6 +4022,9 @@
     void updateImeInputAndControlTarget(WindowState target) {
         if (mImeInputTarget != target) {
             ProtoLog.i(WM_DEBUG_IME, "setInputMethodInputTarget %s", target);
+            if (target != null && target.mActivityRecord != null) {
+                target.mActivityRecord.mImeInsetsFrozenUntilStartInput = false;
+            }
             setImeInputTarget(target);
             updateImeControlTarget();
         }
@@ -3975,7 +4041,9 @@
         // Update Ime parent when IME insets leash created or the new IME layering target might
         // updated from setImeLayeringTarget, which is the best time that default IME visibility
         // has been settled down after IME control target changed.
-        if (prevImeControlTarget != mImeControlTarget || forceUpdateImeParent) {
+        final boolean imeParentChanged =
+                prevImeControlTarget != mImeControlTarget || forceUpdateImeParent;
+        if (imeParentChanged) {
             updateImeParent();
         }
 
@@ -3983,7 +4051,7 @@
         final IBinder token = win != null ? win.mClient.asBinder() : null;
         // Note: not allowed to call into IMMS with the WM lock held, hence the post.
         mWmService.mH.post(() ->
-                InputMethodManagerInternal.get().reportImeControl(token)
+                InputMethodManagerInternal.get().reportImeControl(token, imeParentChanged)
         );
     }
 
@@ -4331,6 +4399,7 @@
                     mTmpApplySurfaceChangesTransactionState.preferMinimalPostProcessing,
                     true /* inTraversal, must call performTraversalInTrans... below */);
         }
+        updateMirroring();
 
         final boolean wallpaperVisible = mWallpaperController.isWallpaperVisible();
         if (wallpaperVisible != mLastWallpaperVisible) {
@@ -4593,7 +4662,9 @@
                 return true;
             }
 
-            if (task.isOrganized()) {
+            // TODO(b/165794880): Freeform task organizer doesn't support drag-resize yet. Remove
+            // the special case when it does.
+            if (task.isOrganized() && task.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
                 return true;
             }
 
@@ -4946,10 +5017,18 @@
         }
     }
 
+    /**
+     * @deprecated new transition should use {@link #requestTransitionAndLegacyPrepare(int, int)}
+     */
+    @Deprecated
     void prepareAppTransition(@WindowManager.TransitionType int transit) {
         prepareAppTransition(transit, 0 /* flags */);
     }
 
+    /**
+     * @deprecated new transition should use {@link #requestTransitionAndLegacyPrepare(int, int)}
+     */
+    @Deprecated
     void prepareAppTransition(@WindowManager.TransitionType int transit,
             @WindowManager.TransitionFlags int flags) {
         final boolean prepared = mAppTransition.prepareAppTransition(transit, flags);
@@ -4962,7 +5041,8 @@
      * Helper that both requests a transition (using the new transition system) and prepares
      * the legacy transition system. Use this when both systems have the same start-point.
      *
-     * @see TransitionController#requestTransitionIfNeeded(int, int, WindowContainer)
+     * @see TransitionController#requestTransitionIfNeeded(int, int, WindowContainer,
+     *      WindowContainer)
      * @see AppTransition#prepareAppTransition
      */
     void requestTransitionAndLegacyPrepare(@WindowManager.TransitionType int transit,
@@ -5706,6 +5786,13 @@
         }
         mRemoved = true;
 
+        if (mMirroredSurface != null) {
+            // Do not wait for the mirrored surface to be garbage collected, but clean up
+            // immediately.
+            mWmService.mTransactionFactory.get().remove(mMirroredSurface).apply();
+            mMirroredSurface = null;
+        }
+
         // Only update focus/visibility for the last one because there may be many root tasks are
         // reparented and the intermediate states are unnecessary.
         if (lastReparentedRootTask != null) {
@@ -5883,6 +5970,133 @@
         return mSandboxDisplayApis;
     }
 
+    /**
+     * Start mirroring to this DisplayContent if it does not have its own content. Captures the
+     * content of a WindowContainer indicated by a WindowToken. If unable to start mirroring, falls
+     * back to original MediaProjection approach.
+     */
+    private void startMirrorIfNeeded() {
+        // Only mirror if this display does not have its own content.
+        if (mLastHasContent) {
+            return;
+        }
+        // Given the WindowToken of the DisplayArea to mirror, retrieve the associated
+        // SurfaceControl.
+        IBinder tokenToMirror = mWmService.mDisplayManagerInternal.getWindowTokenClientToMirror(
+                mDisplayId);
+
+        if (tokenToMirror == null) {
+            // This DisplayContent instance is not involved in layer mirroring. If the display
+            // has been created for capturing, fall back to prior MediaProjection approach.
+            return;
+        }
+        final WindowContainer wc = mWmService.mWindowContextListenerController.getContainer(
+                tokenToMirror);
+        if (wc == null) {
+            // Un-set the window token to mirror for this VirtualDisplay, to fall back to the
+            // original MediaProjection approach.
+            mWmService.mDisplayManagerInternal.setWindowTokenClientToMirror(mDisplayId, null);
+            return;
+        }
+        SurfaceControl sc = wc.getDisplayContent().getSurfaceControl();
+
+        // Create a mirrored hierarchy for the SurfaceControl of the DisplayArea to capture.
+        mMirroredSurface = SurfaceControl.mirrorSurface(sc);
+        SurfaceControl.Transaction transaction = mWmService.mTransactionFactory.get()
+                // Set the mMirroredSurface's parent to the root SurfaceControl for this
+                // DisplayContent. This brings the new mirrored hierarchy under this DisplayContent,
+                // so SurfaceControl will write the layers of this hierarchy to the output surface
+                // provided by the app.
+                .reparent(mMirroredSurface, mSurfaceControl)
+                // Reparent the SurfaceControl of this DisplayContent to null, to prevent content
+                // being added to it. This ensures that no app launched explicitly on the
+                // VirtualDisplay will show up as part of the mirrored content.
+                .reparent(mWindowingLayer, null);
+        // Retrieve the size of the DisplayArea to mirror.
+        updateMirroredSurface(transaction, wc.getDisplayContent().getBounds());
+        mTokenToMirror = tokenToMirror;
+
+        // No need to clean up. In SurfaceFlinger, parents hold references to their children. The
+        // mirrored SurfaceControl is alive since the parent DisplayContent SurfaceControl is
+        // holding a reference to it. Therefore, the mirrored SurfaceControl will be cleaned up
+        // when the VirtualDisplay is destroyed - which will clean up this DisplayContent.
+    }
+
+    /**
+     * Start or stop mirroring if this DisplayContent now has content, or no longer has content.
+     */
+    private void updateMirroring() {
+        if (mLastHasContent && mMirroredSurface != null) {
+            // Display now has content, so stop mirroring to it.
+            mWmService.mTransactionFactory.get()
+                    // Remove the reference to mMirroredSurface, to clean up associated memory.
+                    .remove(mMirroredSurface)
+                    // Reparent the SurfaceControl of this DisplayContent back to mSurfaceControl,
+                    // to allow content to be added to it. This allows this DisplayContent to stop
+                    // mirroring and show content normally.
+                    .reparent(mWindowingLayer, mSurfaceControl).apply();
+            // Stop mirroring by destroying the reference to the mirrored layer.
+            mMirroredSurface = null;
+            // Do not un-set the token, in case content is removed and mirroring should begin again.
+        } else if (!mLastHasContent && mMirroredSurface == null) {
+            // Display no longer has content, so start mirroring to it.
+            startMirrorIfNeeded();
+        }
+    }
+
+    /**
+     * Apply transformations to the mirrored surface to ensure the captured contents are scaled to
+     * fit and centred in the output surface.
+     *
+     * @param transaction            the transaction to include transformations of mMirroredSurface
+     *                               to. Transaction is not applied before returning.
+     * @param displayAreaBounds      bounds of the DisplayArea to mirror to the surface provided by
+     *                               the app.
+     */
+    @VisibleForTesting
+    void updateMirroredSurface(SurfaceControl.Transaction transaction,
+            Rect displayAreaBounds) {
+        // Retrieve the default size of the surface the app provided to
+        // MediaProjection#createVirtualDisplay. Note the app is the consumer of the surface,
+        // since it reads out buffers from the surface, and SurfaceFlinger is the producer since
+        // it writes the mirrored layers to the buffers.
+        final Point surfaceSize = mWmService.mDisplayManagerInternal.getDisplaySurfaceDefaultSize(
+                mDisplayId);
+
+        // Calculate the scale to apply to the root mirror SurfaceControl to fit the size of the
+        // output surface.
+        float scaleX = surfaceSize.x / (float) displayAreaBounds.width();
+        float scaleY = surfaceSize.y / (float) displayAreaBounds.height();
+        float scale = Math.min(scaleX, scaleY);
+        int scaledWidth = Math.round(scale * (float) displayAreaBounds.width());
+        int scaledHeight = Math.round(scale * (float) displayAreaBounds.height());
+
+        // Calculate the shift to apply to the root mirror SurfaceControl to centre the mirrored
+        // contents in the output surface.
+        int shiftedX = 0;
+        if (scaledWidth != surfaceSize.x) {
+            shiftedX = (surfaceSize.x - scaledWidth) / 2;
+        }
+        int shiftedY = 0;
+        if (scaledHeight != surfaceSize.y) {
+            shiftedY = (surfaceSize.y - scaledHeight) / 2;
+        }
+
+        transaction
+                // Crop the area to capture to exclude the 'extra' wallpaper that is used
+                // for parallax (b/189930234).
+                .setWindowCrop(mMirroredSurface, displayAreaBounds.width(),
+                        displayAreaBounds.height())
+                // Scale the root mirror SurfaceControl, based upon the size difference between the
+                // source (DisplayArea to capture) and output (surface the app reads images from).
+                .setMatrix(mMirroredSurface, scale, 0 /* dtdx */, 0 /* dtdy */, scale)
+                // Position needs to be updated when the mirrored DisplayArea has changed, since
+                // the content will no longer be centered in the output surface.
+                .setPosition(mMirroredSurface, shiftedX /* x */, shiftedY /* y */)
+                .apply();
+        mLastMirroredDisplayAreaBounds = new Rect(displayAreaBounds);
+    }
+
     /** The entry for proceeding to handle {@link #mFixedRotationLaunchingApp}. */
     class FixedRotationTransitionListener extends WindowManagerInternal.AppTransitionListener {
 
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 686472f..c1d6c17 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1984,11 +1984,7 @@
             mTopIsFullscreen = topIsFullscreen;
         }
 
-        if (updateSystemUiVisibilityLw()) {
-            // If the navigation bar has been hidden or shown, we need to do another
-            // layout pass to update that window.
-            changes |= FINISH_LAYOUT_REDO_LAYOUT;
-        }
+        updateSystemBarAttributes();
 
         if (mShowingDream != mLastShowingDream) {
             mLastShowingDream = mShowingDream;
@@ -2524,18 +2520,13 @@
     /**
      * A new window has been focused.
      */
-    public int focusChangedLw(WindowState lastFocus, WindowState newFocus) {
+    public void focusChangedLw(WindowState lastFocus, WindowState newFocus) {
         mFocusedWindow = newFocus;
         mLastFocusedWindow = lastFocus;
         if (mDisplayContent.isDefaultDisplay) {
             mService.mPolicy.onDefaultDisplayFocusChangedLw(newFocus);
         }
-        if (updateSystemUiVisibilityLw()) {
-            // If the navigation bar has been hidden or shown, we need to do another
-            // layout pass to update that window.
-            return FINISH_LAYOUT_REDO_LAYOUT;
-        }
-        return 0;
+        updateSystemBarAttributes();
     }
 
     private void requestTransientBars(WindowState swipeTarget) {
@@ -2611,21 +2602,18 @@
         return mDisplayContent.getInsetsPolicy();
     }
 
-    void resetSystemUiVisibilityLw() {
+    void resetSystemBarAttributes() {
         mLastDisableFlags = 0;
-        updateSystemUiVisibilityLw();
+        updateSystemBarAttributes();
     }
 
-    /**
-     * @return {@code true} if the update may affect the layout.
-     */
-    boolean updateSystemUiVisibilityLw() {
+    void updateSystemBarAttributes() {
         // If there is no window focused, there will be nobody to handle the events
         // anyway, so just hang on in whatever state we're in until things settle down.
         WindowState winCandidate = mFocusedWindow != null ? mFocusedWindow
                 : mTopFullscreenOpaqueWindowState;
         if (winCandidate == null) {
-            return false;
+            return;
         }
 
         // The immersive mode confirmation should never affect the system bar visibility, otherwise
@@ -2641,7 +2629,7 @@
                     : lastFocusCanReceiveKeys ? mLastFocusedWindow
                             : mTopFullscreenOpaqueWindowState;
             if (winCandidate == null) {
-                return false;
+                return;
             }
         }
         final WindowState win = winCandidate;
@@ -2680,7 +2668,7 @@
                 && Objects.equals(mFocusedApp, focusedApp)
                 && mLastFocusIsFullscreen == isFullscreen
                 && Arrays.equals(mLastStatusBarAppearanceRegions, appearanceRegions)) {
-            return false;
+            return;
         }
         if (mDisplayContent.isDefaultDisplay && mLastFocusIsFullscreen != isFullscreen
                 && ((mLastAppearance ^ appearance) & APPEARANCE_LOW_PROFILE_BARS) != 0) {
@@ -2698,7 +2686,6 @@
         callStatusBarSafely(statusBar -> statusBar.onSystemBarAttributesChanged(displayId,
                 appearance, appearanceRegions, isNavbarColorManagedByIme, behavior,
                 requestedVisibilities, focusedApp));
-        return true;
     }
 
     private int getStatusBarAppearance(WindowState opaque, WindowState opaqueOrDimming) {
@@ -2970,7 +2957,7 @@
                     return;
                 }
                 mPendingPanicGestureUptime = SystemClock.uptimeMillis();
-                updateSystemUiVisibilityLw();
+                updateSystemBarAttributes();
             }
         }
     };
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index 45f401b..fed4f62 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -23,6 +23,8 @@
 import android.annotation.Nullable;
 import android.util.Slog;
 
+import java.util.ArrayList;
+
 /** Helper class to ensure activities are in the right visible state for a container. */
 class EnsureActivitiesVisibleHelper {
     private final TaskFragment mTaskFragment;
@@ -98,17 +100,37 @@
                 && mTaskFragment.isTopActivityFocusable()
                 && (starting == null || !starting.isDescendantOf(mTaskFragment));
 
+        ArrayList<TaskFragment> adjacentTaskFragments = null;
         for (int i = mTaskFragment.mChildren.size() - 1; i >= 0; --i) {
             final WindowContainer child = mTaskFragment.mChildren.get(i);
             if (child.asTaskFragment() != null) {
                 final TaskFragment childTaskFragment = child.asTaskFragment();
                 childTaskFragment.updateActivityVisibilities(starting, configChanges,
                         preserveWindows, notifyClients);
-                mBehindFullyOccludedContainer = childTaskFragment.getBounds().equals(
+                mBehindFullyOccludedContainer |= childTaskFragment.getBounds().equals(
                         mTaskFragment.getBounds());
                 if (mAboveTop && mTop.getTaskFragment() == childTaskFragment) {
                     mAboveTop = false;
                 }
+
+                if (mBehindFullyOccludedContainer) {
+                    continue;
+                }
+
+                if (adjacentTaskFragments != null && adjacentTaskFragments.contains(
+                        childTaskFragment)) {
+                    // Everything behind two adjacent TaskFragments are occluded.
+                    mBehindFullyOccludedContainer = true;
+                    continue;
+                }
+
+                final TaskFragment adjacentTaskFrag = childTaskFragment.getAdjacentTaskFragment();
+                if (adjacentTaskFrag != null) {
+                    if (adjacentTaskFragments == null) {
+                        adjacentTaskFragments = new ArrayList<>();
+                    }
+                    adjacentTaskFragments.add(adjacentTaskFrag);
+                }
             } else if (child.asActivityRecord() != null) {
                 setActivityVisibilityState(child.asActivityRecord(), starting, resumeTopActivity);
             }
diff --git a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java b/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
index 53b6b41..52a7ac7 100644
--- a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
+++ b/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
@@ -42,6 +42,9 @@
     /** A runnable which gets called when the {@link #show()} is called. */
     private Runnable mOnShowRunnable;
 
+    /** Whether to use constant zero alpha animation. */
+    private boolean mHideImmediately;
+
     public FadeRotationAnimationController(DisplayContent displayContent) {
         super(displayContent);
         mService = displayContent.mWmService;
@@ -51,6 +54,10 @@
                 mService.mWindowPlacerLocked.performSurfacePlacement();
             }
         } : null;
+        if (mFrozenTimeoutRunnable != null) {
+            // Hide the windows immediately because screen should have been covered by screenshot.
+            mHideImmediately = true;
+        }
         final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
         final WindowState navigationBar = displayPolicy.getNavigationBar();
         if (navigationBar != null) {
@@ -66,10 +73,8 @@
         } else {
             mNavBarToken = null;
         }
-        // Do not fade notification shade when running fixed rotation (not frozen) because it may
-        // need to animate with the launching app.
-        final WindowState notificationShade = mFrozenTimeoutRunnable == null
-                ? displayPolicy.getNotificationShade() : null;
+        // Collect the target windows to fade out. The display won't wait for them to unfreeze.
+        final WindowState notificationShade = displayPolicy.getNotificationShade();
         displayContent.forAllWindows(w -> {
             if (w.mActivityRecord == null && w.mHasSurface && !w.mForceSeamlesslyRotate
                     && !w.mIsWallpaper && !w.mIsImWindow && w != navigationBar
@@ -122,6 +127,15 @@
         }
     }
 
+    /** Hides the window immediately until it is drawn in new rotation. */
+    void hideImmediately(WindowToken windowToken) {
+        final boolean original = mHideImmediately;
+        mHideImmediately = true;
+        mTargetWindowTokens.add(windowToken);
+        fadeWindowToken(false /* show */, windowToken, ANIMATION_TYPE_FIXED_TRANSFORM);
+        mHideImmediately = original;
+    }
+
     /** Returns {@code true} if the window is handled by this controller. */
     boolean isHandledToken(WindowToken token) {
         return token == mNavBarToken || isTargetToken(token);
@@ -147,8 +161,7 @@
 
     @Override
     public Animation getFadeOutAnimation() {
-        if (mFrozenTimeoutRunnable != null) {
-            // Hide the window immediately because screen should have been covered by screenshot.
+        if (mHideImmediately) {
             return new AlphaAnimation(0 /* fromAlpha */, 0 /* toAlpha */);
         }
         return super.getFadeOutAnimation();
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index aa7e6c9..18a2c60 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -30,6 +30,7 @@
 import android.view.InputApplicationHandle;
 import android.view.KeyEvent;
 import android.view.WindowManager;
+import android.view.WindowManagerPolicyConstants;
 
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.input.InputManagerService;
@@ -181,8 +182,8 @@
     @Override
     public int getPointerLayer() {
         return mService.mPolicy.getWindowLayerFromTypeLw(WindowManager.LayoutParams.TYPE_POINTER)
-                * WindowManagerService.TYPE_LAYER_MULTIPLIER
-                + WindowManagerService.TYPE_LAYER_OFFSET;
+                * WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER
+                + WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
     }
 
     /** Callback to get pointer display id. */
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 8c781a1..f5bfef9 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -39,6 +39,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
 import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
@@ -47,6 +48,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.LOGTAG_INPUT_FOCUS;
 
+import android.annotation.Nullable;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Handler;
@@ -66,6 +68,7 @@
 import com.android.internal.protolog.common.ProtoLog;
 
 import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
 import java.util.Set;
 import java.util.function.Consumer;
 
@@ -100,6 +103,15 @@
     private final ArrayMap<String, InputConsumerImpl> mInputConsumers = new ArrayMap();
 
     /**
+     * Set when recents (overview) is active as part of a shell transition. While set, any focus
+     * going to the recents activity will be redirected to the Recents input consumer. Since we
+     * draw the live-tile above the recents activity, we also need to provide that activity as a
+     * z-layering reference so that we can place the recents input consumer above it.
+     */
+    private WeakReference<ActivityRecord> mActiveRecentsActivity = null;
+    private WeakReference<ActivityRecord> mActiveRecentsLayerRef = null;
+
+    /**
      * Representation of a input consumer that the policy has added to the window manager to consume
      * input events going to windows below it.
      */
@@ -278,6 +290,7 @@
         inputWindowHandle.setInputFeatures(w.mAttrs.inputFeatures);
         inputWindowHandle.setPaused(w.mActivityRecord != null && w.mActivityRecord.paused);
         inputWindowHandle.setVisible(w.isVisible());
+        inputWindowHandle.setWindowToken(w.mClient);
 
         final boolean focusable = w.canReceiveKeys()
                 && (mService.mPerDisplayFocusEnabled || mDisplayContent.isOnTop());
@@ -307,7 +320,10 @@
         boolean useSurfaceCrop = false;
         final Task task = w.getTask();
         if (task != null) {
-            if (task.isOrganized() && task.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
+            // TODO(b/165794636): Remove the special case for freeform window once drag resizing is
+            // handled by WM shell.
+            if (task.isOrganized() && task.getWindowingMode() != WINDOWING_MODE_FULLSCREEN
+                        && !task.inFreeformWindowingMode()) {
                 // If the window is in a TaskManaged by a TaskOrganizer then most cropping will
                 // be applied using the SurfaceControl hierarchy from the Organizer. This means
                 // we need to make sure that these changes in crop are reflected in the input
@@ -392,6 +408,21 @@
     }
 
     /**
+     * Inform InputMonitor when recents is active so it can enable the recents input consumer.
+     * @param activity The active recents activity. {@code null} means recents is not active.
+     * @param layer An activity whose Z-layer is used as a reference for how to sort the consumer.
+     */
+    void setActiveRecents(@Nullable ActivityRecord activity, @Nullable ActivityRecord layer) {
+        final boolean clear = activity == null;
+        mActiveRecentsActivity = clear ? null : new WeakReference<>(activity);
+        mActiveRecentsLayerRef = clear ? null : new WeakReference<>(layer);
+    }
+
+    private static <T> T getWeak(WeakReference<T> ref) {
+        return ref != null ? ref.get() : null;
+    }
+
+    /**
      * Called when the current input focus changes.
      */
     private void updateInputFocusRequest(InputConsumerImpl recentsAnimationInputConsumer) {
@@ -401,8 +432,10 @@
         if (recentsAnimationInputConsumer != null && focus != null) {
             final RecentsAnimationController recentsAnimationController =
                     mService.getRecentsAnimationController();
-            final boolean shouldApplyRecentsInputConsumer = recentsAnimationController != null
-                    && recentsAnimationController.shouldApplyInputConsumer(focus.mActivityRecord);
+            final boolean shouldApplyRecentsInputConsumer = (recentsAnimationController != null
+                    && recentsAnimationController.shouldApplyInputConsumer(focus.mActivityRecord))
+                    // Shell transitions doesn't use RecentsAnimationController
+                    || getWeak(mActiveRecentsActivity) != null;
             if (shouldApplyRecentsInputConsumer) {
                 requestFocus(recentsAnimationInputConsumer.mWindowHandle.token,
                         recentsAnimationInputConsumer.mName);
@@ -502,6 +535,14 @@
             mInDrag = inDrag;
 
             resetInputConsumers(mInputTransaction);
+            // Update recents input consumer layer if active
+            if (mAddRecentsAnimationInputConsumerHandle
+                    && getWeak(mActiveRecentsActivity) != null) {
+                final WindowContainer layer = getWeak(mActiveRecentsLayerRef);
+                mRecentsAnimationInputConsumer.show(mInputTransaction,
+                        layer != null ? layer : getWeak(mActiveRecentsActivity));
+                mAddRecentsAnimationInputConsumerHandle = false;
+            }
             mDisplayContent.forAllWindows(this, true /* traverseTopToBottom */);
             updateInputFocusRequest(mRecentsAnimationInputConsumer);
 
@@ -536,10 +577,12 @@
 
             final int privateFlags = w.mAttrs.privateFlags;
 
+            // This only works for legacy transitions.
             if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) {
                 if (recentsAnimationController.updateInputConsumerForApp(
                         mRecentsAnimationInputConsumer.mWindowHandle)) {
-                    mRecentsAnimationInputConsumer.show(mInputTransaction, w.mActivityRecord);
+                    mRecentsAnimationInputConsumer.show(mInputTransaction,
+                            recentsAnimationController.getHighestLayerWindow());
                     mAddRecentsAnimationInputConsumerHandle = false;
                 }
             }
@@ -650,6 +693,7 @@
                 || type == TYPE_DOCK_DIVIDER
                 || type == TYPE_ACCESSIBILITY_OVERLAY
                 || type == TYPE_INPUT_CONSUMER
-                || type == TYPE_VOICE_INTERACTION;
+                || type == TYPE_VOICE_INTERACTION
+                || type == TYPE_STATUS_BAR_ADDITIONAL;
     }
 }
diff --git a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
index 7a4d13c..0a24d3c 100644
--- a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
+++ b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.graphics.Region;
 import android.os.IBinder;
+import android.view.IWindow;
 import android.view.InputApplicationHandle;
 import android.view.InputWindowHandle;
 import android.view.SurfaceControl;
@@ -275,6 +276,14 @@
         mChanged = true;
     }
 
+    void setWindowToken(IWindow windowToken) {
+        if (mHandle.getWindow() == windowToken) {
+            return;
+        }
+        mHandle.setWindowToken(windowToken);
+        mChanged = true;
+    }
+
     @Override
     public String toString() {
         return mHandle + ", changed=" + mChanged;
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 0a83784..869d351 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -213,7 +213,7 @@
     InsetsState getInsetsForWindow(WindowState target) {
         final InsetsState originalState = mStateController.getInsetsForWindow(target);
         final InsetsState state = adjustVisibilityForTransientTypes(originalState);
-        return target.mIsImWindow ? adjustVisibilityForIme(state, state == originalState) : state;
+        return adjustVisibilityForIme(target, state, state == originalState);
     }
 
     /**
@@ -243,16 +243,37 @@
         return state;
     }
 
-    // Navigation bar insets is always visible to IME.
-    private static InsetsState adjustVisibilityForIme(InsetsState originalState,
+    private InsetsState adjustVisibilityForIme(WindowState w, InsetsState originalState,
             boolean copyState) {
-        final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR);
-        if (originalNavSource != null && !originalNavSource.isVisible()) {
-            final InsetsState state = copyState ? new InsetsState(originalState) : originalState;
-            final InsetsSource navSource = new InsetsSource(originalNavSource);
-            navSource.setVisible(true);
-            state.addSource(navSource);
-            return state;
+        if (w.mIsImWindow) {
+            // Navigation bar insets is always visible to IME.
+            final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR);
+            if (originalNavSource != null && !originalNavSource.isVisible()) {
+                final InsetsState state = copyState ? new InsetsState(originalState)
+                        : originalState;
+                final InsetsSource navSource = new InsetsSource(originalNavSource);
+                navSource.setVisible(true);
+                state.addSource(navSource);
+                return state;
+            }
+        } else if (w.mActivityRecord != null && w.mActivityRecord.mImeInsetsFrozenUntilStartInput) {
+            // During switching tasks with gestural navigation, if the IME is attached to
+            // one app window on that time, even the next app window is behind the IME window,
+            // conceptually the window should not receive the IME insets if the next window is
+            // not eligible IME requester and ready to show IME on top of it.
+            final boolean shouldImeAttachedToApp = mDisplayContent.shouldImeAttachedToApp();
+            final InsetsSource originalImeSource = originalState.peekSource(ITYPE_IME);
+
+            if (shouldImeAttachedToApp && originalImeSource != null) {
+                final boolean imeVisibility =
+                        w.mActivityRecord.mLastImeShown || w.getRequestedVisibility(ITYPE_IME);
+                final InsetsState state = copyState ? new InsetsState(originalState)
+                        : originalState;
+                final InsetsSource imeSource = new InsetsSource(originalImeSource);
+                imeSource.setVisible(imeVisibility);
+                state.addSource(imeSource);
+                return state;
+            }
         }
         return originalState;
     }
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index cbd1314..f3e52f2 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -376,8 +376,11 @@
             return;
         }
         mClientVisible = clientVisible;
-        mDisplayContent.mWmService.mH.obtainMessage(
-                LAYOUT_AND_ASSIGN_WINDOW_LAYERS_IF_NEEDED, mDisplayContent).sendToTarget();
+        if (!mDisplayContent.mLayoutAndAssignWindowLayersScheduled) {
+            mDisplayContent.mLayoutAndAssignWindowLayersScheduled = true;
+            mDisplayContent.mWmService.mH.obtainMessage(
+                    LAYOUT_AND_ASSIGN_WINDOW_LAYERS_IF_NEEDED, mDisplayContent).sendToTarget();
+        }
         updateVisibility();
     }
 
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 655007c..2c4adcb 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -385,7 +385,7 @@
         if (changed) {
             notifyInsetsChanged();
             mDisplayContent.updateSystemGestureExclusion();
-            mDisplayContent.getDisplayPolicy().updateSystemUiVisibilityLw();
+            mDisplayContent.getDisplayPolicy().updateSystemBarAttributes();
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 9cd8c2d..6f3edbc 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -20,6 +20,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
@@ -27,6 +28,7 @@
 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
 import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_SUBTLE_WINDOW_ANIMATIONS;
 import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
@@ -235,8 +237,14 @@
                     mAodShowing ? 1 : 0,
                     1 /* keyguardGoingAway */,
                     "keyguardGoingAway");
-            mRootWindowContainer.getDefaultDisplay().requestTransitionAndLegacyPrepare(
-                    TRANSIT_KEYGUARD_GOING_AWAY, convertTransitFlags(flags));
+            final int transitFlags = convertTransitFlags(flags);
+            final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
+            dc.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, transitFlags);
+            // We are deprecating TRANSIT_KEYGUARD_GOING_AWAY for Shell transition and use
+            // TRANSIT_FLAG_KEYGUARD_GOING_AWAY to indicate that it should animate keyguard going
+            // away.
+            dc.mAtmService.getTransitionController().requestTransitionIfNeeded(
+                    TRANSIT_TO_BACK, transitFlags, null /* trigger */, dc);
             updateKeyguardSleepToken();
 
             // Some stack visibility might change (e.g. docked stack)
@@ -276,7 +284,7 @@
     }
 
     private int convertTransitFlags(int keyguardGoingAwayFlags) {
-        int result = 0;
+        int result = TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
         if ((keyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_TO_SHADE) != 0) {
             result |= TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
         }
@@ -378,15 +386,17 @@
             mService.deferWindowLayout();
             try {
                 mRootWindowContainer.getDefaultDisplay()
-                        .prepareAppTransition(
+                        .requestTransitionAndLegacyPrepare(
                                 isDisplayOccluded(DEFAULT_DISPLAY)
                                         ? TRANSIT_KEYGUARD_OCCLUDE
-                                        : TRANSIT_KEYGUARD_UNOCCLUDE);
+                                        : TRANSIT_KEYGUARD_UNOCCLUDE, 0 /* flags */);
                 // When the occluding activity also turns on the display, visibility of the activity
                 // can be committed before KEYGUARD_OCCLUDE transition is handled.
                 // Set mRequestForceTransition flag to make sure that the app transition animation
                 // is applied for such case.
-                if (topActivity != null) {
+                // TODO(b/194243906): Fix this before enabling the remote keyguard animation.
+                if (WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation
+                        && topActivity != null) {
                     topActivity.mRequestForceTransition = true;
                 }
                 updateKeyguardSleepToken(DEFAULT_DISPLAY);
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index eb7087c..34b834b 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.Color;
 
@@ -63,7 +64,10 @@
     private int mLetterboxActivityCornersRadius;
 
     // Color for {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} letterbox background type.
-    private Color mLetterboxBackgroundColor;
+    @Nullable private Color mLetterboxBackgroundColorOverride;
+
+    // Color resource id for {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} letterbox background type.
+    @Nullable private Integer mLetterboxBackgroundColorResourceIdOverride;
 
     @LetterboxBackgroundType
     private int mLetterboxBackgroundType;
@@ -81,20 +85,18 @@
     // side of the screen and 1.0 to the right side.
     private float mLetterboxHorizontalPositionMultiplier;
 
-    LetterboxConfiguration(Context context) {
-        mContext = context;
-        mFixedOrientationLetterboxAspectRatio = context.getResources().getFloat(
+    LetterboxConfiguration(Context systemUiContext) {
+        mContext = systemUiContext;
+        mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat(
                 R.dimen.config_fixedOrientationLetterboxAspectRatio);
-        mLetterboxActivityCornersRadius = context.getResources().getInteger(
+        mLetterboxActivityCornersRadius = mContext.getResources().getInteger(
                 R.integer.config_letterboxActivityCornersRadius);
-        mLetterboxBackgroundColor = Color.valueOf(context.getResources().getColor(
-                R.color.config_letterboxBackgroundColor));
-        mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(context);
-        mLetterboxBackgroundWallpaperBlurRadius = context.getResources().getDimensionPixelSize(
+        mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext);
+        mLetterboxBackgroundWallpaperBlurRadius = mContext.getResources().getDimensionPixelSize(
                 R.dimen.config_letterboxBackgroundWallpaperBlurRadius);
-        mLetterboxBackgroundWallpaperDarkScrimAlpha = context.getResources().getFloat(
+        mLetterboxBackgroundWallpaperDarkScrimAlpha = mContext.getResources().getFloat(
                 R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha);
-        mLetterboxHorizontalPositionMultiplier = context.getResources().getFloat(
+        mLetterboxHorizontalPositionMultiplier = mContext.getResources().getFloat(
                 R.dimen.config_letterboxHorizontalPositionMultiplier);
     }
 
@@ -158,12 +160,20 @@
     }
 
     /**
-     * Gets color of letterbox background which is  used when {@link
+     * 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;
+        if (mLetterboxBackgroundColorOverride != null) {
+            return mLetterboxBackgroundColorOverride;
+        }
+        int colorId = mLetterboxBackgroundColorResourceIdOverride != null
+                ? mLetterboxBackgroundColorResourceIdOverride
+                : R.color.config_letterboxBackgroundColor;
+        // Query color dynamically because material colors extracted from wallpaper are updated
+        // when wallpaper is changed.
+        return Color.valueOf(mContext.getResources().getColor(colorId));
     }
 
 
@@ -173,7 +183,16 @@
      * fallback for other backfround types.
      */
     void setLetterboxBackgroundColor(Color color) {
-        mLetterboxBackgroundColor = color;
+        mLetterboxBackgroundColorOverride = color;
+    }
+
+    /**
+     * Sets color ID of letterbox background which is used when {@link
+     * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as
+     * fallback for other backfround types.
+     */
+    void setLetterboxBackgroundColorResourceId(int colorId) {
+        mLetterboxBackgroundColorResourceIdOverride = colorId;
     }
 
     /**
@@ -181,8 +200,8 @@
      * com.android.internal.R.color.config_letterboxBackgroundColor}.
      */
     void resetLetterboxBackgroundColor() {
-        mLetterboxBackgroundColor = Color.valueOf(mContext.getResources().getColor(
-                com.android.internal.R.color.config_letterboxBackgroundColor));
+        mLetterboxBackgroundColorOverride = null;
+        mLetterboxBackgroundColorResourceIdOverride = null;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
index d230936..88941eb 100644
--- a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
@@ -138,7 +138,7 @@
         mTarget = new RemoteAnimationTarget(-1, -1, getLeash(), false,
                 new Rect(), null, mWindowContainer.getPrefixOrderIndex(),
                 mWindowContainer.getLastSurfacePosition(), mWindowContainer.getBounds(), null,
-                mWindowContainer.getWindowConfiguration(), true, null, null, null,
+                mWindowContainer.getWindowConfiguration(), true, null, null, null, false,
                 mWindowContainer.getWindowType());
         return mTarget;
     }
diff --git a/services/core/java/com/android/server/wm/PinnedTaskController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java
index 4f7c9a4..6014a87 100644
--- a/services/core/java/com/android/server/wm/PinnedTaskController.java
+++ b/services/core/java/com/android/server/wm/PinnedTaskController.java
@@ -319,15 +319,11 @@
     }
 
     /** Resets the states which were used to perform fixed rotation with PiP task. */
-    void onCancelFixedRotationTransform(Task task) {
+    void onCancelFixedRotationTransform() {
         mFreezingTaskConfig = false;
         mDeferOrientationChanging = false;
         mDestRotatedBounds = null;
         mPipTransaction = null;
-        if (!task.isOrganized()) {
-            // Force clearing Task#mForceNotOrganized because the display didn't rotate.
-            task.onConfigurationChanged(task.getParent().getConfiguration());
-        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index ede4c2e..dca0bbd 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -1346,7 +1346,8 @@
                     + " activityType=" + task.getActivityType()
                     + " windowingMode=" + task.getWindowingMode()
                     + " isAlwaysOnTopWhenVisible=" + task.isAlwaysOnTopWhenVisible()
-                    + " intentFlags=" + task.getBaseIntent().getFlags());
+                    + " intentFlags=" + task.getBaseIntent().getFlags()
+                    + " isEmbedded=" + task.isEmbedded());
         }
 
         switch (task.getActivityType()) {
@@ -1392,6 +1393,11 @@
             return false;
         }
 
+        // Ignore the task if it is a embedded task
+        if (task.isEmbedded()) {
+            return false;
+        }
+
         return true;
     }
 
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index e346e3e..e8d9c15b 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -1102,6 +1102,23 @@
         return mTargetActivityRecord.findMainWindow();
     }
 
+    /**
+     * Returns the window with the highest layer, or null if none is found.
+     */
+    public WindowState getHighestLayerWindow() {
+        int highestLayer = Integer.MIN_VALUE;
+        Task highestLayerTask = null;
+        for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
+            TaskAnimationAdapter adapter = mPendingAnimations.get(i);
+            int layer = adapter.mTask.getPrefixOrderIndex();
+            if (layer > highestLayer) {
+                highestLayer = layer;
+                highestLayerTask = adapter.mTask;
+            }
+        }
+        return highestLayerTask.getTopMostActivity().getTopChild();
+    }
+
     boolean isAnimatingTask(Task task) {
         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
             if (task == mPendingAnimations.get(i).mTask) {
@@ -1201,7 +1218,8 @@
                     !topApp.fillsParent(), new Rect(),
                     insets, mTask.getPrefixOrderIndex(), new Point(mBounds.left, mBounds.top),
                     mLocalBounds, mBounds, mTask.getWindowConfiguration(),
-                    mIsRecentTaskInvisible, null, null, mTask.getTaskInfo());
+                    mIsRecentTaskInvisible, null, null, mTask.getTaskInfo(),
+                    topApp.checkEnterPictureInPictureAppOpsState());
             return mTarget;
         }
 
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 1a429f8..6b57ede 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -396,6 +396,7 @@
         RemoteAnimationTarget mTarget;
         final WindowContainer mWindowContainer;
         final Rect mStartBounds;
+        private @RemoteAnimationTarget.Mode int mMode = RemoteAnimationTarget.MODE_CHANGING;
 
         RemoteAnimationRecord(WindowContainer windowContainer, Point endPos, Rect localBounds,
                 Rect endBounds, Rect startBounds) {
@@ -428,18 +429,12 @@
             return mTarget;
         }
 
+        void setMode(@RemoteAnimationTarget.Mode int mode) {
+            mMode = mode;
+        }
+
         int getMode() {
-            final DisplayContent dc = mWindowContainer.getDisplayContent();
-            final ActivityRecord topActivity = mWindowContainer.getTopMostActivity();
-            // Note that opening/closing transitions are per-activity while changing transitions
-            // are per-task.
-            if (dc.mOpeningApps.contains(topActivity)) {
-                return RemoteAnimationTarget.MODE_OPENING;
-            } else if (dc.mChangingContainers.contains(mWindowContainer)) {
-                return RemoteAnimationTarget.MODE_CHANGING;
-            } else {
-                return RemoteAnimationTarget.MODE_CLOSING;
-            }
+            return mMode;
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 24c5c82..079868d 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -365,8 +365,26 @@
                 return false;
             }
 
+            if (matchingCandidate(task)) {
+                return true;
+            }
+
+            // Looking for the embedded tasks (if any)
+            return !task.isLeafTaskFragment() && task.forAllLeafTaskFragments(
+                    this::matchingCandidate);
+        }
+
+        boolean matchingCandidate(TaskFragment taskFragment) {
+            final Task task = taskFragment.asTask();
+            if (task == null) {
+                return false;
+            }
+
             // Overlays should not be considered as the task's logical top activity.
-            final ActivityRecord r = task.getTopNonFinishingActivity(false /* includeOverlays */);
+            // Activities of the tasks that embedded from this one should not be used.
+            final ActivityRecord r = task.getTopNonFinishingActivity(false /* includeOverlays */,
+                    false /* includingEmbeddedTask */);
+
             if (r == null || r.finishing || r.mUserId != userId
                     || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
                 ProtoLog.d(WM_DEBUG_TASKS, "Skipping %s: mismatch root %s", task, r);
@@ -1980,7 +1998,8 @@
      */
     void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
             boolean preserveWindows, boolean notifyClients) {
-        if (mTaskSupervisor.inActivityVisibilityUpdate()) {
+        if (mTaskSupervisor.inActivityVisibilityUpdate()
+                || mTaskSupervisor.isRootVisibilityUpdateDeferred()) {
             // Don't do recursive work.
             return;
         }
@@ -2811,7 +2830,6 @@
         Slog.w(TAG, "  Force finishing activity "
                 + r.intent.getComponent().flattenToShortString());
         r.detachFromProcess();
-        r.mDisplayContent.prepareAppTransition(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED);
         r.mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_CLOSE,
                 TRANSIT_FLAG_APP_CRASHED);
         r.destroyIfPossible("handleAppCrashed");
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 58363f2..d440a14 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -18,6 +18,7 @@
 
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.util.RotationUtils.deltaRotation;
+import static android.view.WindowManagerPolicyConstants.SCREEN_FREEZE_LAYER_BASE;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC;
@@ -30,8 +31,6 @@
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_SCREEN_ROTATION;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
-import static com.android.server.wm.WindowStateAnimator.WINDOW_FREEZE_LAYER;
 
 import android.animation.ArgbEvaluator;
 import android.content.Context;
@@ -91,13 +90,6 @@
 class ScreenRotationAnimation {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ScreenRotationAnimation" : TAG_WM;
 
-    /*
-     * Layers for screen rotation animation. We put these layers above
-     * WINDOW_FREEZE_LAYER so that screen freeze will cover all windows.
-     */
-    private static final int SCREEN_FREEZE_LAYER_BASE = WINDOW_FREEZE_LAYER + TYPE_LAYER_MULTIPLIER;
-    private static final int SCREEN_FREEZE_LAYER_ENTER = SCREEN_FREEZE_LAYER_BASE;
-
     private final Context mContext;
     private final DisplayContent mDisplayContent;
     private final float[] mTmpFloats = new float[9];
@@ -423,7 +415,7 @@
                         finalWidth * 2, finalHeight * 2);
                 Rect inner = new Rect(0, 0, finalWidth, finalHeight);
                 mEnteringBlackFrame = new BlackFrame(mService.mTransactionFactory, t, outer, inner,
-                        SCREEN_FREEZE_LAYER_ENTER, mDisplayContent, false, mEnterBlackFrameLayer);
+                        SCREEN_FREEZE_LAYER_BASE, mDisplayContent, false, mEnterBlackFrameLayer);
             } catch (OutOfResourcesException e) {
                 Slog.w(TAG, "Unable to allocate black surface", e);
             }
diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java
index c671e38..8b1befb 100644
--- a/services/core/java/com/android/server/wm/StartingData.java
+++ b/services/core/java/com/android/server/wm/StartingData.java
@@ -32,6 +32,12 @@
      */
     boolean mIsTransitionForward;
 
+    /**
+     * Non-null if the starting window should cover the bounds of associated task. It is assigned
+     * when the parent activity of starting window may be put in a partial area of the task.
+     */
+    Task mAssociatedTask;
+
     protected StartingData(WindowManagerService service, int typeParams) {
         mService = service;
         mTypeParams = typeParams;
diff --git a/services/core/java/com/android/server/wm/StrictModeFlash.java b/services/core/java/com/android/server/wm/StrictModeFlash.java
index 48a7bde..cdf6b08 100644
--- a/services/core/java/com/android/server/wm/StrictModeFlash.java
+++ b/services/core/java/com/android/server/wm/StrictModeFlash.java
@@ -27,6 +27,7 @@
 import android.view.Surface;
 import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl;
+import android.view.WindowManagerPolicyConstants;
 
 class StrictModeFlash {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "StrictModeFlash" : TAG_WM;
@@ -52,7 +53,7 @@
                     .build();
 
             // one more than Watermark? arbitrary.
-            t.setLayer(ctrl, WindowManagerService.TYPE_LAYER_MULTIPLIER * 101);
+            t.setLayer(ctrl, WindowManagerPolicyConstants.STRICT_MODE_LAYER);
             t.setPosition(ctrl, 0, 0);
             t.show(ctrl);
             // Ensure we aren't considered as obscuring for Input purposes.
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 2355dde..567936d 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -125,7 +125,6 @@
 import static com.android.server.wm.TaskProto.SURFACE_WIDTH;
 import static com.android.server.wm.TaskProto.TASK_FRAGMENT;
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
-import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowContainerChildProto.TASK;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
@@ -591,16 +590,6 @@
     // false.
     private boolean mDeferTaskAppear;
 
-    /**
-     * Forces this task to be unorganized. Currently it is used for deferring the control of
-     * organizer when windowing mode is changing from PiP to fullscreen with orientation change.
-     * It is true only during Task#setWindowingMode ~ DisplayRotation#continueRotation.
-     *
-     * TODO(b/179235349): Remove this field by making surface operations from task organizer sync
-     *                    with display rotation.
-     */
-    private boolean mForceNotOrganized;
-
     // Tracking cookie for the creation of this task.
     IBinder mLaunchCookie;
 
@@ -628,7 +617,7 @@
             IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor,
             boolean _createdByOrganizer, IBinder _launchCookie, boolean _deferTaskAppear,
             boolean _removeWithTaskOrganizer) {
-        super(atmService, null /* fragmentToken */, _createdByOrganizer);
+        super(atmService, null /* fragmentToken */, _createdByOrganizer, false /* isEmbedded */);
 
         mTaskId = _taskId;
         mUserId = _userId;
@@ -707,13 +696,13 @@
         return this;
     }
 
-    private void cleanUpResourcesForDestroy(ConfigurationContainer oldParent) {
+    private void cleanUpResourcesForDestroy(WindowContainer<?> oldParent) {
         if (hasChild()) {
             return;
         }
 
         // This task is going away, so save the last state if necessary.
-        saveLaunchingStateIfNeeded(((WindowContainer) oldParent).getDisplayContent());
+        saveLaunchingStateIfNeeded(oldParent.getDisplayContent());
 
         // TODO: VI what about activity?
         final boolean isVoiceSession = voiceSession != null;
@@ -723,7 +712,7 @@
             } catch (RemoteException e) {
             }
         }
-        if (autoRemoveFromRecents() || isVoiceSession) {
+        if (autoRemoveFromRecents(oldParent.asTaskFragment()) || isVoiceSession) {
             // Task creator asked to remove this when done, or this task was a voice
             // interaction, so it should not remain on the recent tasks list.
             mTaskSupervisor.mRecentTasks.remove(this);
@@ -1144,11 +1133,11 @@
     }
 
     @Override
-    void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
-        final DisplayContent display = newParent != null
-                ? ((WindowContainer) newParent).getDisplayContent() : null;
-        final DisplayContent oldDisplay = oldParent != null
-                ? ((WindowContainer) oldParent).getDisplayContent() : null;
+    void onParentChanged(ConfigurationContainer rawNewParent, ConfigurationContainer rawOldParent) {
+        final WindowContainer<?> newParent = (WindowContainer<?>) rawNewParent;
+        final WindowContainer<?> oldParent = (WindowContainer<?>) rawOldParent;
+        final DisplayContent display = newParent != null ? newParent.getDisplayContent() : null;
+        final DisplayContent oldDisplay = oldParent != null ? oldParent.getDisplayContent() : null;
 
         mPrevDisplayId = (oldDisplay != null) ? oldDisplay.mDisplayId : INVALID_DISPLAY;
 
@@ -1189,7 +1178,7 @@
         }
 
         if (oldParent != null) {
-            final Task oldParentTask = ((WindowContainer) oldParent).asTask();
+            final Task oldParentTask = oldParent.asTask();
             if (oldParentTask != null) {
                 final PooledConsumer c = PooledLambda.obtainConsumer(
                         Task::cleanUpActivityReferences, oldParentTask,
@@ -1394,14 +1383,6 @@
         return mFindRootHelper.findRoot(ignoreRelinquishIdentity, setToBottomIfNone);
     }
 
-    ActivityRecord getTopNonFinishingActivity() {
-        return getTopNonFinishingActivity(true /* includeOverlays */);
-    }
-
-    ActivityRecord getTopNonFinishingActivity(boolean includeOverlays) {
-        return getTopActivity(false /*includeFinishing*/, includeOverlays);
-    }
-
     ActivityRecord topRunningActivityLocked() {
         if (getParent() == null) {
             return null;
@@ -1521,7 +1502,7 @@
         if (DEBUG_TASK_MOVEMENT) {
             Slog.d(TAG_WM, "removeChild: child=" + r + " reason=" + reason);
         }
-        super.removeChild(r);
+        super.removeChild(r, false /* removeSelfIfPossible */);
 
         if (inPinnedWindowingMode()) {
             // We normally notify listeners of task stack changes on pause, however root pinned task
@@ -1551,7 +1532,10 @@
             // Remove entire task if it doesn't have any activity left and it isn't marked for reuse
             // or created by task organizer.
             if (!isRootTask()) {
-                getRootTask().removeChild(this, reason);
+                final WindowContainer<?> parent = getParent();
+                if (parent != null) {
+                    parent.asTaskFragment().removeChild(this);
+                }
             }
             EventLogTags.writeWmTaskRemoved(mTaskId,
                     "removeChild:" + reason + " last r=" + r + " in t=" + this);
@@ -1583,11 +1567,12 @@
         return count > 0;
     }
 
-    private boolean autoRemoveFromRecents() {
+    private boolean autoRemoveFromRecents(TaskFragment oldParentFragment) {
         // We will automatically remove the task either if it has explicitly asked for
         // this, or it is empty and has never contained an activity that got shown to
-        // the user.
-        return autoRemoveRecents || (!hasChild() && !getHasBeenVisible());
+        // the user, or it was being embedded in another Task.
+        return autoRemoveRecents || (!hasChild() && !getHasBeenVisible()
+                || (oldParentFragment != null && oldParentFragment.isEmbedded()));
     }
 
     private void clearPinnedTaskIfNeed() {
@@ -1937,19 +1922,16 @@
             }
         }
 
-        if (pipChanging) {
-            // If the top activity is using fixed rotation, it should be changing from PiP to
-            // fullscreen with display orientation change. Do not notify fullscreen task organizer
-            // because the restoration of task surface and the transformation of activity surface
-            // need to be done synchronously.
+        if (pipChanging && wasInPictureInPicture) {
+            // If the top activity is changing from PiP to fullscreen with fixed rotation,
+            // clear the crop and rotation matrix of task because fixed rotation will handle
+            // the transformation on activity level. This also avoids flickering caused by the
+            // latency of fullscreen task organizer configuring the surface.
             final ActivityRecord r = topRunningActivity();
             if (r != null && mDisplayContent.isFixedRotationLaunchingApp(r)) {
-                mForceNotOrganized = true;
+                getSyncTransaction().setWindowCrop(mSurfaceControl, null)
+                        .setMatrix(mSurfaceControl, Matrix.IDENTITY_MATRIX, new float[9]);
             }
-        } else {
-            // If the display orientation change is done, let the corresponding task organizer take
-            // back the control of this task.
-            mForceNotOrganized = false;
         }
 
         saveLaunchingStateIfNeeded();
@@ -2184,16 +2166,6 @@
         bounds.offset(horizontalDiff, verticalDiff);
     }
 
-    /**
-     * Initializes a change transition. See {@link SurfaceFreezer} for more information.
-     */
-    private void initializeChangeTransition(Rect startBounds) {
-        mDisplayContent.prepareAppTransition(TRANSIT_CHANGE);
-        mDisplayContent.mChangingContainers.add(this);
-
-        mSurfaceFreezer.freeze(getPendingTransaction(), startBounds);
-    }
-
     private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) {
         if (mWmService.mDisableTransitionAnimation
                 || !isVisible()
@@ -2602,25 +2574,12 @@
         }
     }
 
-    @VisibleForTesting
-    boolean hasWindowsAlive() {
-        return getActivity(ActivityRecord::hasWindowsAlive) != null;
-    }
-
-    @VisibleForTesting
-    boolean shouldDeferRemoval() {
-        if (mChildren.isEmpty()) {
-            // No reason to defer removal of a Task that doesn't have any child.
-            return false;
-        }
-        return hasWindowsAlive() && getRootTask().isAnimating(TRANSITION | CHILDREN);
-    }
-
     @Override
     void removeImmediately() {
         removeImmediately("removeTask");
     }
 
+    @Override
     void removeImmediately(String reason) {
         if (DEBUG_ROOT_TASK) Slog.i(TAG, "removeTask:" + reason + " removing taskId=" + mTaskId);
         if (mRemoving) {
@@ -2976,11 +2935,54 @@
     /** Returns the top-most activity that occludes the given one, or {@code null} if none. */
     @Nullable
     ActivityRecord getOccludingActivityAbove(ActivityRecord activity) {
-        final ActivityRecord top = getActivity(ActivityRecord::occludesParent,
-                true /* traverseTopToBottom */, activity);
+        final ActivityRecord top = getActivity(r -> {
+            if (r == activity) {
+                // Reached the given activity, return the activity to stop searching.
+                return true;
+            }
+
+            if (!r.occludesParent()) {
+                return false;
+            }
+
+            TaskFragment parent = r.getTaskFragment();
+            if (parent == activity.getTaskFragment()) {
+                // Found it. This activity on top of the given activity on the same TaskFragment.
+                return true;
+            }
+            if (isSelfOrNonEmbeddedTask(parent.asTask())) {
+                // Found it. This activity is the direct child of a leaf Task without being
+                // embedded.
+                return true;
+            }
+            // The candidate activity is being embedded. Checking if the bounds of the containing
+            // TaskFragment equals to the outer TaskFragment.
+            TaskFragment grandParent = parent.getParent().asTaskFragment();
+            while (grandParent != null) {
+                if (!parent.getBounds().equals(grandParent.getBounds())) {
+                    // Not occluding the grandparent.
+                    break;
+                }
+                if (isSelfOrNonEmbeddedTask(grandParent.asTask())) {
+                    // Found it. The activity occludes its parent TaskFragment and the parent
+                    // TaskFragment also occludes its parent all the way up.
+                    return true;
+                }
+                parent = grandParent;
+                grandParent = parent.getParent().asTaskFragment();
+            }
+            return false;
+        });
         return top != activity ? top : null;
     }
 
+    private boolean isSelfOrNonEmbeddedTask(Task task) {
+        if (task == this) {
+            return true;
+        }
+        return task != null && !task.isEmbedded();
+    }
+
     @Override
     public SurfaceControl.Builder makeAnimationLeash() {
         return super.makeAnimationLeash().setMetadata(METADATA_TASK_ID, mTaskId);
@@ -3089,10 +3091,6 @@
         });
     }
 
-    boolean isFocusableAndVisible() {
-        return isTopActivityFocusable() && shouldBeVisible(null /* starting */);
-    }
-
     void positionChildAtTop(ActivityRecord child) {
         positionChildAt(child, POSITION_TOP);
     }
@@ -3217,7 +3215,7 @@
             boolean consumed = false;
             if (traverseTopToBottom) {
                 for (int i = task.mChildren.size() - 1; i >= 0; --i) {
-                    final WindowContainer child = mChildren.get(i);
+                    final WindowContainer child = task.mChildren.get(i);
                     if (child.asTaskFragment() != null) {
                         child.forAllLeafTaskFragments(callback, traverseTopToBottom);
                     } else if (child.asActivityRecord() != null && !consumed) {
@@ -3227,7 +3225,7 @@
                 }
             } else {
                 for (int i = 0; i < task.mChildren.size(); i++) {
-                    final WindowContainer child = mChildren.get(i);
+                    final WindowContainer child = task.mChildren.get(i);
                     if (child.asTaskFragment() != null) {
                         child.forAllLeafTaskFragments(callback, traverseTopToBottom);
                     } else if (child.asActivityRecord() != null && !consumed) {
@@ -3358,19 +3356,9 @@
     @Override
     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
         super.dump(pw, prefix, dumpAll);
-        pw.println(prefix + "bounds=" + getBounds().toShortString());
-        final String doublePrefix = prefix + "  ";
-        for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowContainer<?> child = mChildren.get(i);
-            pw.println(prefix + "* " + child);
-            // Only dump non-activity because full activity info is already printed by
-            // RootWindowContainer#dumpActivities.
-            if (child.asActivityRecord() == null) {
-                child.dump(pw, doublePrefix, dumpAll);
-            }
-        }
 
         if (!mExitingActivities.isEmpty()) {
+            final String doublePrefix = prefix + "  ";
             pw.println();
             pw.println(prefix + "Exiting application tokens:");
             for (int i = mExitingActivities.size() - 1; i >= 0; i--) {
@@ -3661,9 +3649,6 @@
         pw.print(" isResizeable="); pw.println(isResizeable());
         pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime);
         pw.println(" (inactive for " + (getInactiveDuration() / 1000) + "s)");
-        if (mForceNotOrganized) {
-            pw.print(prefix); pw.println("mForceNotOrganized=true");
-        }
     }
 
     @Override
@@ -4139,10 +4124,6 @@
     }
 
     private boolean canBeOrganized() {
-        if (mForceNotOrganized || !mAtmService.mTaskOrganizerController
-                .isSupportedWindowingMode(getWindowingMode())) {
-            return false;
-        }
         // All root tasks can be organized
         if (isRootTask()) {
             return true;
@@ -4295,9 +4276,8 @@
             return setTaskOrganizer(null);
         }
 
-        final int windowingMode = getWindowingMode();
         final TaskOrganizerController controller = mWmService.mAtmService.mTaskOrganizerController;
-        final ITaskOrganizer organizer = controller.getTaskOrganizer(windowingMode);
+        final ITaskOrganizer organizer = controller.getTaskOrganizer();
         if (!forceUpdate && mTaskOrganizer == organizer) {
             return false;
         }
@@ -4631,8 +4611,10 @@
             mAtmService.continueWindowLayout();
         }
 
-        mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
-        mRootWindowContainer.resumeFocusedTasksTopActivities();
+        if (!mTaskSupervisor.isRootVisibilityUpdateDeferred()) {
+            mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
+            mRootWindowContainer.resumeFocusedTasksTopActivities();
+        }
     }
 
     void resumeNextFocusAfterReparent() {
@@ -5021,7 +5003,10 @@
             if (topFragment == f) {
                 return;
             }
-
+            if (!f.isFocusableAndVisible()) {
+                // No need to resume activity in TaskFragment that is not visible.
+                return;
+            }
             resumed[0] |= f.resumeTopActivity(prev, options, deferPause);
         }, true);
         return resumed[0];
@@ -5058,7 +5043,7 @@
     }
 
     void startActivityLocked(ActivityRecord r, @Nullable ActivityRecord focusedTopActivity,
-            boolean newTask, boolean keepCurTransition, ActivityOptions options,
+            boolean newTask, boolean isTaskSwitch, ActivityOptions options,
             @Nullable ActivityRecord sourceRecord) {
         Task rTask = r.getTask();
         final boolean allowMoveToFront = options == null || !options.getAvoidMoveToFront();
@@ -5164,22 +5149,18 @@
                 // "has the same starting icon" as the next one.  This allows the
                 // window manager to keep the previous window it had previously
                 // created, if it still had one.
-                Task prevTask = r.getTask();
-                ActivityRecord prev = prevTask.getActivity(
-                        a -> a.mStartingData != null && a.okToShowLocked());
-                if (prev != null) {
-                    // We don't want to reuse the previous starting preview if:
-                    // (1) The current activity is in a different task.
-                    if (prev.getTask() != prevTask) {
-                        prev = null;
-                    }
-                    // (2) The current activity is already displayed.
-                    else if (prev.nowVisible) {
-                        prev = null;
-                    }
+                Task baseTask = r.getTask();
+                if (baseTask.isEmbedded()) {
+                    // If the task is embedded in a task fragment, there may have an existing
+                    // starting window in the parent task. This allows the embedded activities
+                    // to share the starting window and make sure that the window can have top
+                    // z-order by transferring to the top activity.
+                    baseTask = baseTask.getParent().asTaskFragment().getTask();
                 }
 
-                r.showStartingWindow(prev, newTask, isTaskSwitch(r, focusedTopActivity),
+                final ActivityRecord prev = baseTask.getActivity(
+                        a -> a.mStartingData != null && a.okToShowLocked());
+                r.showStartingWindow(prev, newTask, isTaskSwitch,
                         true /* startActivity */, sourceRecord);
             }
         } else {
@@ -5213,10 +5194,6 @@
         return true;
     }
 
-    private boolean isTaskSwitch(ActivityRecord r, ActivityRecord topFocusedActivity) {
-        return topFocusedActivity != null && r.getTask() != topFocusedActivity.getTask();
-    }
-
     /**
      * Reset the task by reparenting the activities that have same affinity to the task or
      * reparenting the activities that have different affinityies out of the task, while these
@@ -5274,7 +5251,6 @@
         Slog.w(TAG, "  Force finishing activity "
                 + r.intent.getComponent().flattenToShortString());
         Task finishedTask = r.getTask();
-        mDisplayContent.prepareAppTransition(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED);
         mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED);
         r.finishIfPossible(reason, false /* oomAdj */);
 
@@ -5332,18 +5308,6 @@
         return true;
     }
 
-    /** Finish all activities in the root task without waiting. */
-    void finishAllActivitiesImmediately() {
-        if (!hasChild()) {
-            removeIfPossible("finishAllActivitiesImmediately");
-            return;
-        }
-        forAllActivities((r) -> {
-            Slog.d(TAG, "finishAllActivitiesImmediatelyLocked: finishing " + r);
-            r.destroyIfPossible("finishAllActivitiesImmediately");
-        });
-    }
-
     /** @return true if the root task behind this one is a standard activity type. */
     private boolean inFrontOfStandardRootTask() {
         final TaskDisplayArea taskDisplayArea = getDisplayArea();
@@ -5654,7 +5618,6 @@
 
         // Skip the transition for pinned task.
         if (!inPinnedWindowingMode()) {
-            mDisplayContent.prepareAppTransition(TRANSIT_TO_BACK);
             mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_TO_BACK, tr);
         }
         moveToBack("moveTaskToBackLocked", tr);
@@ -5740,17 +5703,14 @@
 
     @Override
     void dumpInner(String prefix, PrintWriter pw, boolean dumpAll, String dumpPackage) {
-        pw.print(prefix); pw.print("* "); pw.println(this);
-        pw.println(prefix + "  mBounds=" + getRequestedOverrideBounds());
-        pw.println(prefix + "  mCreatedByOrganizer=" + mCreatedByOrganizer);
+        super.dumpInner(prefix, pw, dumpAll, dumpPackage);
+        if (mCreatedByOrganizer) {
+            pw.println(prefix + "  mCreatedByOrganizer=true");
+        }
         if (mLastNonFullscreenBounds != null) {
             pw.print(prefix); pw.print("  mLastNonFullscreenBounds=");
             pw.println(mLastNonFullscreenBounds);
         }
-        if (dumpAll) {
-            printThisActivity(pw, mLastPausedActivity, dumpPackage, false,
-                    prefix + "  mLastPausedActivity: ", null);
-        }
         if (isLeafTask()) {
             pw.println(prefix + "  isSleeping=" + shouldSleepActivities());
             printThisActivity(pw, getTopPausingActivity(), dumpPackage, false,
@@ -6147,16 +6107,6 @@
         getDisplayContent().getPinnedTaskController().setActions(actions);
     }
 
-    /** Returns true if a removal action is still being deferred. */
-    boolean handleCompleteDeferredRemoval() {
-        if (isAnimating(TRANSITION | CHILDREN)
-                || mAtmService.getTransitionController().inTransition(this)) {
-            return true;
-        }
-
-        return super.handleCompleteDeferredRemoval();
-    }
-
     public DisplayInfo getDisplayInfo() {
         return mDisplayContent.getDisplayInfo();
     }
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 6c8cde43..c7ca180 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -32,6 +32,7 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.server.wm.ActivityRecord.State.RESUMED;
@@ -112,7 +113,6 @@
     private final ArrayList<WindowContainer> mTmpNormalChildren = new ArrayList<>();
     private final ArrayList<WindowContainer> mTmpHomeChildren = new ArrayList<>();
     private final IntArray mTmpNeedsZBoostIndexes = new IntArray();
-    private int mTmpLayerForSplitScreenDividerAnchor;
     private int mTmpLayerForAnimationLayer;
 
     private ArrayList<Task> mTmpTasks = new ArrayList<>();
@@ -855,26 +855,23 @@
         layer = adjustRootTaskLayer(t, mTmpHomeChildren, layer, false /* normalRootTasks */);
         // The home animation layer is between the root home tasks and the normal root tasks.
         final int layerForHomeAnimationLayer = layer++;
-        mTmpLayerForSplitScreenDividerAnchor = layer++;
         mTmpLayerForAnimationLayer = layer++;
         layer = adjustRootTaskLayer(t, mTmpNormalChildren, layer, true /* normalRootTasks */);
 
         // The boosted animation layer is between the normal root tasks and the always on top
         // root tasks.
         final int layerForBoostedAnimationLayer = layer++;
+        // Always on top tasks layer should higher than split divider layer so set it as start.
+        layer = SPLIT_DIVIDER_LAYER + 1;
         adjustRootTaskLayer(t, mTmpAlwaysOnTopChildren, layer, false /* normalRootTasks */);
 
         t.setLayer(mHomeAppAnimationLayer, layerForHomeAnimationLayer);
         t.setLayer(mAppAnimationLayer, mTmpLayerForAnimationLayer);
-        t.setLayer(mSplitScreenDividerAnchor, mTmpLayerForSplitScreenDividerAnchor);
+        t.setLayer(mSplitScreenDividerAnchor, SPLIT_DIVIDER_LAYER);
         t.setLayer(mBoostedAppAnimationLayer, layerForBoostedAnimationLayer);
     }
 
     private int adjustNormalRootTaskLayer(WindowContainer child, int layer) {
-        if (child.asTask() != null && child.inSplitScreenWindowingMode()) {
-            // The split screen divider anchor is located above the split screen window.
-            mTmpLayerForSplitScreenDividerAnchor = layer++;
-        }
         if ((child.asTask() != null && child.asTask().isAnimatingByRecents())
                 || child.isAppTransitioning()) {
             // The animation layer is located above the highest animating root task and no
@@ -2104,7 +2101,7 @@
             if (destroyContentOnRemoval
                     || !task.isActivityTypeStandardOrUndefined()
                     || task.mCreatedByOrganizer) {
-                task.finishAllActivitiesImmediately();
+                task.remove(false /* withTransition */, "removeTaskDisplayArea");
             } else {
                 // Reparent task to corresponding launch root or display area.
                 final WindowContainer launchRoot =
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 1eec6aa..242693b 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -33,6 +33,7 @@
 import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
 import static android.os.UserHandle.USER_NULL;
 import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
 import static android.view.WindowManager.TRANSIT_NONE;
@@ -61,6 +62,8 @@
 import static com.android.server.wm.TaskFragmentProto.MIN_HEIGHT;
 import static com.android.server.wm.TaskFragmentProto.MIN_WIDTH;
 import static com.android.server.wm.TaskFragmentProto.WINDOW_CONTAINER;
+import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowContainerChildProto.TASK_FRAGMENT;
 
 import android.annotation.IntDef;
@@ -158,10 +161,11 @@
      */
     int mMinHeight;
 
-    /** Avoid reentrant of {@link #removeImmediately()}. */
-    private boolean mRemoving;
+    /** This task fragment will be removed when the cleanup of its children are done. */
+    private boolean mIsRemovalRequested;
 
-    // The TaskFragment that adjacent to this one.
+    /** The TaskFragment that is adjacent to this one. */
+    @Nullable
     private TaskFragment mAdjacentTaskFragment;
 
     /**
@@ -206,6 +210,9 @@
     @VisibleForTesting
     boolean mCreatedByOrganizer;
 
+    /** Whether this TaskFragment is embedded in a task. */
+    private final boolean mIsEmbedded;
+
     /** Organizer that organizing this TaskFragment. */
     @Nullable
     private ITaskFragmentOrganizer mTaskFragmentOrganizer;
@@ -215,6 +222,13 @@
     private IBinder mFragmentToken;
 
     /**
+     * Whether to delay the last activity of TaskFragment being immediately removed while finishing.
+     * This should only be set on a embedded TaskFragment, where the organizer can have the
+     * opportunity to perform other actions or animations.
+     */
+    private boolean mDelayLastActivityRemoval;
+
+    /**
      * The PID of the organizer that created this TaskFragment. It should be the same as the PID
      * of {@link android.window.TaskFragmentCreationParams#getOwnerToken()}.
      * {@link ActivityRecord#INVALID_PID} if this is not an organizer-created TaskFragment.
@@ -268,23 +282,53 @@
         }
     }
 
+    /** Creates an embedded task fragment. */
     TaskFragment(ActivityTaskManagerService atmService, IBinder fragmentToken,
             boolean createdByOrganizer) {
+        this(atmService, fragmentToken, createdByOrganizer, true /* isEmbedded */);
+    }
+
+    TaskFragment(ActivityTaskManagerService atmService, IBinder fragmentToken,
+            boolean createdByOrganizer, boolean isEmbedded) {
         super(atmService.mWindowManager);
 
         mAtmService = atmService;
         mTaskSupervisor = mAtmService.mTaskSupervisor;
         mRootWindowContainer = mAtmService.mRootWindowContainer;
         mCreatedByOrganizer = createdByOrganizer;
+        mIsEmbedded = isEmbedded;
         mTaskFragmentOrganizerController =
                 mAtmService.mWindowOrganizerController.mTaskFragmentOrganizerController;
         mFragmentToken = fragmentToken;
         mRemoteToken = new RemoteToken(this);
     }
 
-    void setAdjacentTaskFragment(TaskFragment taskFragment) {
-        mAdjacentTaskFragment = taskFragment;
-        taskFragment.mAdjacentTaskFragment = this;
+    @NonNull
+    static TaskFragment fromTaskFragmentToken(@Nullable IBinder token,
+            @NonNull ActivityTaskManagerService service) {
+        if (token == null) return null;
+        return service.mWindowOrganizerController.getTaskFragment(token);
+    }
+
+    void setAdjacentTaskFragment(@Nullable TaskFragment taskFragment) {
+        if (mAdjacentTaskFragment == taskFragment) {
+            return;
+        }
+        resetAdjacentTaskFragment();
+        if (taskFragment != null) {
+            mAdjacentTaskFragment = taskFragment;
+            taskFragment.setAdjacentTaskFragment(this);
+        }
+    }
+
+    private void resetAdjacentTaskFragment() {
+        // Reset the adjacent TaskFragment if its adjacent TaskFragment is also this TaskFragment.
+        if (mAdjacentTaskFragment != null && mAdjacentTaskFragment.mAdjacentTaskFragment == this) {
+            mAdjacentTaskFragment.mAdjacentTaskFragment = null;
+            mAdjacentTaskFragment.mDelayLastActivityRemoval = false;
+        }
+        mAdjacentTaskFragment = null;
+        mDelayLastActivityRemoval = false;
     }
 
     void setTaskFragmentOrganizer(TaskFragmentOrganizerToken organizer, int pid) {
@@ -374,6 +418,32 @@
         return this;
     }
 
+    /** Returns {@code true} if this is a container for embedded activities or tasks. */
+    boolean isEmbedded() {
+        if (mIsEmbedded) {
+            return true;
+        }
+        final WindowContainer<?> parent = getParent();
+        if (parent != null) {
+            final TaskFragment taskFragment = parent.asTaskFragment();
+            return taskFragment != null && taskFragment.isEmbedded();
+        }
+        return false;
+    }
+
+    /**
+     * Returns the TaskFragment that is being organized, which could be this or the ascendant
+     * TaskFragment.
+     */
+    @Nullable
+    TaskFragment getOrganizedTaskFragment() {
+        if (mTaskFragmentOrganizer != null) {
+            return this;
+        }
+
+        TaskFragment parentTaskFragment = getParent().asTaskFragment();
+        return parentTaskFragment != null ? parentTaskFragment.getOrganizedTaskFragment() : null;
+    }
 
     /**
      * Simply check and give warning logs if this is not operated on leaf {@link TaskFragment}.
@@ -570,17 +640,68 @@
         return false;
     }
 
+    ActivityRecord getTopNonFinishingActivity() {
+        return getTopNonFinishingActivity(true /* includeOverlays */);
+    }
+
+    ActivityRecord getTopNonFinishingActivity(boolean includeOverlays) {
+        return getTopNonFinishingActivity(includeOverlays, true /* includingEmbeddedTask */);
+    }
+
+    /**
+     * Returns the top-most non-finishing activity, even if the activity is NOT ok to show to
+     * the current user.
+     * @param includeOverlays whether the task overlay activity should be included.
+     * @param includingEmbeddedTask whether the activity in a task that being embedded from this
+     *                              one should be included.
+     * @see #topRunningActivity(boolean, boolean)
+     * @see ActivityRecord#okToShowLocked()
+     */
+    ActivityRecord getTopNonFinishingActivity(boolean includeOverlays,
+            boolean includingEmbeddedTask) {
+        // Split into 4 to avoid object creation due to variable capture.
+        if (includeOverlays) {
+            if (includingEmbeddedTask) {
+                return getActivity((r) -> !r.finishing);
+            }
+            return getActivity((r) -> !r.finishing && r.getTask() == this.getTask());
+        }
+
+        if (includingEmbeddedTask) {
+            return getActivity((r) -> !r.finishing && !r.isTaskOverlay());
+        }
+        return getActivity(
+                (r) -> !r.finishing && !r.isTaskOverlay() && r.getTask() == this.getTask());
+    }
+
     ActivityRecord topRunningActivity() {
         return topRunningActivity(false /* focusableOnly */);
     }
 
     ActivityRecord topRunningActivity(boolean focusableOnly) {
-        // Split into 2 to avoid object creation due to variable capture.
+        return topRunningActivity(focusableOnly, true /* includingEmbeddedTask */);
+    }
+
+    /**
+     * Returns the top-most running activity, which the activity is non-finishing and ok to show
+     * to the current user.
+     *
+     * @see ActivityRecord#canBeTopRunning()
+     */
+    ActivityRecord topRunningActivity(boolean focusableOnly, boolean includingEmbeddedTask) {
+        // Split into 4 to avoid object creation due to variable capture.
         if (focusableOnly) {
-            return getActivity((r) -> r.canBeTopRunning() && r.isFocusable());
-        } else {
+            if (includingEmbeddedTask) {
+                return getActivity((r) -> r.canBeTopRunning() && r.isFocusable());
+            }
+            return getActivity(
+                    (r) -> r.canBeTopRunning() && r.isFocusable() && r.getTask() == this.getTask());
+        }
+
+        if (includingEmbeddedTask) {
             return getActivity(ActivityRecord::canBeTopRunning);
         }
+        return getActivity((r) -> r.canBeTopRunning() && r.getTask() == this.getTask());
     }
 
     boolean isTopActivityFocusable() {
@@ -1165,6 +1286,10 @@
         return getVisibility(starting) != TASK_FRAGMENT_VISIBILITY_INVISIBLE;
     }
 
+    boolean isFocusableAndVisible() {
+        return isTopActivityFocusable() && shouldBeVisible(null /* starting */);
+    }
+
     final boolean startPausing(boolean uiSleeping, ActivityRecord resuming, String reason) {
         return startPausing(mTaskSupervisor.mUserLeaving, uiSleeping, resuming, reason);
     }
@@ -1303,6 +1428,8 @@
 
             } else {
                 prev.schedulePauseTimeout();
+                // Unset readiness since we now need to wait until this pause is complete.
+                mAtmService.getTransitionController().setReady(this, false /* ready */);
                 return true;
             }
 
@@ -1491,7 +1618,12 @@
     @Override
     RemoteAnimationTarget createRemoteAnimationTarget(
             RemoteAnimationController.RemoteAnimationRecord record) {
-        final ActivityRecord activity = getTopMostActivity();
+        final ActivityRecord activity = record.getMode() == RemoteAnimationTarget.MODE_OPENING
+                // There may be a trampoline activity without window on top of the existing task
+                // which is moving to front. Exclude the finishing activity so the window of next
+                // activity can be chosen to create the animation target.
+                ? getTopNonFinishingActivity()
+                : getTopMostActivity();
         return activity != null ? activity.createRemoteAnimationTarget(record) : null;
     }
 
@@ -1856,10 +1988,48 @@
 
     @Override
     public void onConfigurationChanged(Configuration newParentConfig) {
+        // Task will animate differently.
+        if (mTaskFragmentOrganizer != null) {
+            mTmpPrevBounds.set(getBounds());
+        }
+
         super.onConfigurationChanged(newParentConfig);
+
+        if (shouldStartChangeTransition(mTmpPrevBounds)) {
+            initializeChangeTransition(mTmpPrevBounds);
+        }
+
+        if (mTaskFragmentOrganizer != null) {
+            // Update the surface position here instead of in the organizer so that we can make sure
+            // it can be synced with the surface freezer.
+            updateSurfacePosition(getSyncTransaction());
+        }
+
         sendTaskFragmentInfoChanged();
     }
 
+    /** Whether we should prepare a transition for this {@link TaskFragment} bounds change. */
+    private boolean shouldStartChangeTransition(Rect startBounds) {
+        if (mWmService.mDisableTransitionAnimation
+                || mDisplayContent == null
+                || mTaskFragmentOrganizer == null
+                || getSurfaceControl() == null
+                || !isVisible()) {
+            return false;
+        }
+
+        return !startBounds.equals(getBounds());
+    }
+
+    /**
+     * Initializes a change transition. See {@link SurfaceFreezer} for more information.
+     */
+    void initializeChangeTransition(Rect startBounds) {
+        mDisplayContent.prepareAppTransition(TRANSIT_CHANGE);
+        mDisplayContent.mChangingContainers.add(this);
+        mSurfaceFreezer.freeze(getSyncTransaction(), startBounds);
+    }
+
     @Override
     void setSurfaceControl(SurfaceControl sc) {
         super.setSurfaceControl(sc);
@@ -1887,6 +2057,10 @@
         }
     }
 
+    int getTaskFragmentOrganizerPid() {
+        return mTaskFragmentOrganizerPid;
+    }
+
     /**
      * Returns a {@link TaskFragmentInfo} with information from this TaskFragment. Should not be
      * called from {@link Task}.
@@ -1926,6 +2100,11 @@
         return mTaskFragmentOrganizer;
     }
 
+    @Override
+    boolean isOrganized() {
+        return mTaskFragmentOrganizer != null;
+    }
+
     /** Clear {@link #mLastPausedActivity} for all {@link TaskFragment} children */
     void clearLastPausedActivity() {
         forAllTaskFragments(taskFragment -> taskFragment.mLastPausedActivity = null);
@@ -1946,14 +2125,83 @@
     }
 
     @Override
-    void removeImmediately() {
-        if (mRemoving) {
+    void removeChild(WindowContainer child) {
+        removeChild(child, true /* removeSelfIfPossible */);
+    }
+
+    void removeChild(WindowContainer child, boolean removeSelfIfPossible) {
+        super.removeChild(child);
+        if (removeSelfIfPossible && (!mCreatedByOrganizer || mIsRemovalRequested) && !hasChild()) {
+            removeImmediately("removeLastChild " + child);
+        }
+    }
+
+    /**
+     * Requests to remove this task fragment. If it doesn't have children, it is removed
+     * immediately. Otherwise it will be removed until all activities are destroyed.
+     *
+     * @param withTransition Whether to use transition animation when removing activities. Set to
+     *                       {@code false} if this is invisible to user, e.g. display removal.
+     */
+    void remove(boolean withTransition, String reason) {
+        if (!hasChild()) {
+            removeImmediately(reason);
             return;
         }
-        mRemoving = true;
+        mIsRemovalRequested = true;
+        forAllActivities(r -> {
+            if (withTransition) {
+                r.finishIfPossible(reason, false /* oomAdj */);
+            } else {
+                r.destroyIfPossible(reason);
+            }
+        });
+    }
+
+    void setDelayLastActivityRemoval(boolean delay) {
+        if (!mIsEmbedded) {
+            Slog.w(TAG, "Set delaying last activity removal on a non-embedded TF.");
+        }
+        mDelayLastActivityRemoval = delay;
+    }
+
+    boolean isDelayLastActivityRemoval() {
+        return mDelayLastActivityRemoval;
+    }
+
+    boolean shouldDeferRemoval() {
+        if (!hasChild()) {
+            return false;
+        }
+        return isAnimating(TRANSITION | CHILDREN, WindowState.EXIT_ANIMATING_TYPES)
+                || mAtmService.getTransitionController().inTransition(this);
+    }
+
+    @Override
+    boolean handleCompleteDeferredRemoval() {
+        if (shouldDeferRemoval()) {
+            return true;
+        }
+        return super.handleCompleteDeferredRemoval();
+    }
+
+    /** The overridden method must call {@link #removeImmediately()} instead of super. */
+    void removeImmediately(String reason) {
+        Slog.d(TAG, "Remove task fragment: " + reason);
+        removeImmediately();
+    }
+
+    @Override
+    void removeImmediately() {
+        mIsRemovalRequested = false;
+        resetAdjacentTaskFragment();
         super.removeImmediately();
         sendTaskFragmentVanished();
-        mRemoving = false;
+    }
+
+    @Override
+    boolean canBeAnimationTarget() {
+        return true;
     }
 
     boolean dump(String prefix, FileDescriptor fd, PrintWriter pw, boolean dumpAll,
@@ -1981,10 +2229,10 @@
         for (int i = mChildren.size() - 1; i >= 0; --i) {
             WindowContainer child = mChildren.get(i);
             if (child.asTaskFragment() != null) {
-                printed |= child.asTaskFragment().dump(prefix + "      ", fd, pw, dumpAll,
+                printed |= child.asTaskFragment().dump(prefix + "  ", fd, pw, dumpAll,
                         dumpClient, dumpPackage, needSep, headerPrinter);
             } else if (child.asActivityRecord() != null) {
-                ActivityRecord.dumpActivity(fd, pw, i, child.asActivityRecord(), prefix + "      ",
+                ActivityRecord.dumpActivity(fd, pw, i, child.asActivityRecord(), prefix + "  ",
                         "Hist ", true, !dumpAll, dumpClient, dumpPackage, false, headerPrinter,
                         getTask());
             }
@@ -1995,7 +2243,13 @@
 
     void dumpInner(String prefix, PrintWriter pw, boolean dumpAll, String dumpPackage) {
         pw.print(prefix); pw.print("* "); pw.println(this);
-        pw.println(prefix + "  mBounds=" + getRequestedOverrideBounds());
+        final Rect bounds = getRequestedOverrideBounds();
+        if (!bounds.isEmpty()) {
+            pw.println(prefix + "  mBounds=" + bounds);
+        }
+        if (mIsRemovalRequested) {
+            pw.println(prefix + "  mIsRemovalRequested=true");
+        }
         if (dumpAll) {
             printThisActivity(pw, mLastPausedActivity, dumpPackage, false,
                     prefix + "  mLastPausedActivity: ", null);
@@ -2003,6 +2257,22 @@
     }
 
     @Override
+    void dump(PrintWriter pw, String prefix, boolean dumpAll) {
+        super.dump(pw, prefix, dumpAll);
+        pw.println(prefix + "bounds=" + getBounds().toShortString());
+        final String doublePrefix = prefix + "  ";
+        for (int i = mChildren.size() - 1; i >= 0; i--) {
+            final WindowContainer<?> child = mChildren.get(i);
+            pw.println(prefix + "* " + child);
+            // Only dump non-activity because full activity info is already printed by
+            // RootWindowContainer#dumpActivities.
+            if (child.asActivityRecord() == null) {
+                child.dump(pw, doublePrefix, dumpAll);
+            }
+        }
+    }
+
+    @Override
     void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
         proto.write(HASH_CODE, System.identityHashCode(this));
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index a322384..690f67c 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -199,6 +199,8 @@
 
         void onTaskFragmentError(ITaskFragmentOrganizer organizer, IBinder errorCallbackToken,
                 Throwable exception) {
+            ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
+                    "Sending TaskFragment error exception=%s", exception.toString());
             final Bundle exceptionBundle = putExceptionInBundle(exception);
             try {
                 organizer.onTaskFragmentError(errorCallbackToken, exceptionBundle);
@@ -230,11 +232,16 @@
         validateAndGetState(organizer);
         final int pid = Binder.getCallingPid();
         final long uid = Binder.getCallingUid();
-        synchronized (mGlobalLock) {
-            ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
-                    "Unregister task fragment organizer=%s uid=%d pid=%d",
-                    organizer.asBinder(), uid, pid);
-            removeOrganizer(organizer);
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
+                        "Unregister task fragment organizer=%s uid=%d pid=%d",
+                        organizer.asBinder(), uid, pid);
+                removeOrganizer(organizer);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
         }
     }
 
@@ -308,6 +315,7 @@
     void onTaskFragmentError(ITaskFragmentOrganizer organizer, IBinder errorCallbackToken,
             Throwable exception) {
         validateAndGetState(organizer);
+        Slog.w(TAG, "onTaskFragmentError ", exception);
         PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent(organizer,
                 errorCallbackToken, exception, PendingTaskFragmentEvent.EVENT_ERROR);
         mPendingTaskFragmentEvents.add(pendingEvent);
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 9ca89cb..fa7b276 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -16,9 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
 import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
 import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
@@ -67,14 +64,6 @@
 class TaskOrganizerController extends ITaskOrganizerController.Stub {
     private static final String TAG = "TaskOrganizerController";
 
-    // The set of modes that are currently supports
-    // TODO: Remove once the task organizer can support all modes
-    @VisibleForTesting
-    static final int[] UNSUPPORTED_WINDOWING_MODES = {
-            WINDOWING_MODE_UNDEFINED,
-            WINDOWING_MODE_FREEFORM
-    };
-
     private class DeathRecipient implements IBinder.DeathRecipient {
         ITaskOrganizer mTaskOrganizer;
 
@@ -113,109 +102,6 @@
             return mTaskOrganizer.asBinder();
         }
 
-        void addStartingWindow(Task task, ActivityRecord activity, int launchTheme,
-                TaskSnapshot taskSnapshot) {
-            final StartingWindowInfo info = task.getStartingWindowInfo(activity);
-            if (launchTheme != 0) {
-                info.splashScreenThemeResId = launchTheme;
-            }
-            info.mTaskSnapshot = taskSnapshot;
-            // make this happen prior than prepare surface
-            try {
-                mTaskOrganizer.addStartingWindow(info, activity.token);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Exception sending onTaskStart callback", e);
-            }
-        }
-
-        // Capture the animation surface control for activity's main window
-        private class StartingWindowAnimationAdaptor implements AnimationAdapter {
-            private SurfaceControl mAnimationLeash;
-            @Override
-            public boolean getShowWallpaper() {
-                return false;
-            }
-
-            @Override
-            public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t,
-                    int type, SurfaceAnimator.OnAnimationFinishedCallback finishCallback) {
-                mAnimationLeash = animationLeash;
-            }
-
-            @Override
-            public void onAnimationCancelled(SurfaceControl animationLeash) {
-                if (mAnimationLeash == animationLeash) {
-                    mAnimationLeash = null;
-                }
-            }
-
-            @Override
-            public long getDurationHint() {
-                return 0;
-            }
-
-            @Override
-            public long getStatusBarTransitionsStartTime() {
-                return 0;
-            }
-
-            @Override
-            public void dump(PrintWriter pw, String prefix) {
-                pw.print(prefix + "StartingWindowAnimationAdaptor mCapturedLeash=");
-                pw.print(mAnimationLeash);
-                pw.println();
-            }
-
-            @Override
-            public void dumpDebug(ProtoOutputStream proto) {
-            }
-        }
-
-        void removeStartingWindow(Task task, boolean prepareAnimation) {
-            SurfaceControl windowAnimationLeash = null;
-            Rect mainFrame = null;
-            final boolean playShiftUpAnimation = !task.inMultiWindowMode();
-            if (prepareAnimation && playShiftUpAnimation) {
-                final ActivityRecord topActivity = task.topActivityContainsStartingWindow();
-                if (topActivity != null) {
-                    final WindowState mainWindow =
-                            topActivity.findMainWindow(false/* includeStartingApp */);
-                    if (mainWindow != null) {
-                        final StartingWindowAnimationAdaptor adaptor =
-                                new StartingWindowAnimationAdaptor();
-                        final SurfaceControl.Transaction t = mainWindow.getPendingTransaction();
-                        mainWindow.startAnimation(t, adaptor, false,
-                                ANIMATION_TYPE_STARTING_REVEAL);
-                        windowAnimationLeash = adaptor.mAnimationLeash;
-                        mainFrame = mainWindow.getRelativeFrame();
-                        t.setPosition(windowAnimationLeash, mainFrame.left, mainFrame.top);
-                    }
-                }
-            }
-            try {
-                mTaskOrganizer.removeStartingWindow(task.mTaskId, windowAnimationLeash,
-                        mainFrame, prepareAnimation);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Exception sending onStartTaskFinished callback", e);
-            }
-        }
-
-        void copySplashScreenView(Task task) {
-            try {
-                mTaskOrganizer.copySplashScreenView(task.mTaskId);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Exception sending copyStartingWindowView callback", e);
-            }
-        }
-
-        void onAppSplashScreenViewRemoved(Task task) {
-            try {
-                mTaskOrganizer.onAppSplashScreenViewRemoved(task.mTaskId);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Exception sending onAppSplashScreenViewRemoved callback", e);
-            }
-        }
-
         SurfaceControl prepareLeash(Task task, String reason) {
             return new SurfaceControl(task.getSurfaceControl(), reason);
         }
@@ -302,23 +188,6 @@
             mUid = uid;
         }
 
-        void addStartingWindow(Task t, ActivityRecord activity, int launchTheme,
-                TaskSnapshot taskSnapshot) {
-            mOrganizer.addStartingWindow(t, activity, launchTheme, taskSnapshot);
-        }
-
-        void removeStartingWindow(Task t, boolean prepareAnimation) {
-            mOrganizer.removeStartingWindow(t, prepareAnimation);
-        }
-
-        void copySplashScreenView(Task t) {
-            mOrganizer.copySplashScreenView(t);
-        }
-
-        public void onAppSplashScreenViewRemoved(Task t) {
-            mOrganizer.onAppSplashScreenViewRemoved(t);
-        }
-
         /**
          * Register this task with this state, but doesn't trigger the task appeared callback to
          * the organizer.
@@ -493,11 +362,6 @@
 
                 final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
                 mService.mRootWindowContainer.forAllTasks((task) -> {
-                    if (ArrayUtils.contains(UNSUPPORTED_WINDOWING_MODES,
-                            task.getWindowingMode())) {
-                        return;
-                    }
-
                     boolean returnTask = !task.mCreatedByOrganizer;
                     task.updateTaskOrganizerState(true /* forceUpdate */,
                             returnTask /* skipTaskAppeared */);
@@ -553,46 +417,130 @@
     /**
      * @return the task organizer key for a given windowing mode.
      */
-    ITaskOrganizer getTaskOrganizer(int windowingMode) {
-        return isSupportedWindowingMode(windowingMode)
-                ? mTaskOrganizers.peekLast()
-                : null;
+    ITaskOrganizer getTaskOrganizer() {
+        return mTaskOrganizers.peekLast();
     }
 
-    boolean isSupportedWindowingMode(int winMode) {
-        return !ArrayUtils.contains(UNSUPPORTED_WINDOWING_MODES, winMode);
+    // Capture the animation surface control for activity's main window
+    private static class StartingWindowAnimationAdaptor implements AnimationAdapter {
+        private SurfaceControl mAnimationLeash;
+        @Override
+        public boolean getShowWallpaper() {
+            return false;
+        }
+
+        @Override
+        public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t,
+                int type, SurfaceAnimator.OnAnimationFinishedCallback finishCallback) {
+            mAnimationLeash = animationLeash;
+        }
+
+        @Override
+        public void onAnimationCancelled(SurfaceControl animationLeash) {
+            if (mAnimationLeash == animationLeash) {
+                mAnimationLeash = null;
+            }
+        }
+
+        @Override
+        public long getDurationHint() {
+            return 0;
+        }
+
+        @Override
+        public long getStatusBarTransitionsStartTime() {
+            return 0;
+        }
+
+        @Override
+        public void dump(PrintWriter pw, String prefix) {
+            pw.print(prefix + "StartingWindowAnimationAdaptor mCapturedLeash=");
+            pw.print(mAnimationLeash);
+            pw.println();
+        }
+
+        @Override
+        public void dumpDebug(ProtoOutputStream proto) {
+        }
     }
 
     boolean addStartingWindow(Task task, ActivityRecord activity, int launchTheme,
             TaskSnapshot taskSnapshot) {
         final Task rootTask = task.getRootTask();
-        if (rootTask == null || rootTask.mTaskOrganizer == null || activity.mStartingData == null) {
+        if (rootTask == null || activity.mStartingData == null) {
             return false;
         }
-        final TaskOrganizerState state =
-                mTaskOrganizerStates.get(rootTask.mTaskOrganizer.asBinder());
-        state.addStartingWindow(task, activity, launchTheme, taskSnapshot);
+        final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast();
+        if (lastOrganizer == null) {
+            return false;
+        }
+        final StartingWindowInfo info = task.getStartingWindowInfo(activity);
+        if (launchTheme != 0) {
+            info.splashScreenThemeResId = launchTheme;
+        }
+        info.mTaskSnapshot = taskSnapshot;
+        // make this happen prior than prepare surface
+        try {
+            lastOrganizer.addStartingWindow(info, activity.token);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Exception sending onTaskStart callback", e);
+            return false;
+        }
         return true;
     }
 
     void removeStartingWindow(Task task, boolean prepareAnimation) {
         final Task rootTask = task.getRootTask();
-        if (rootTask == null || rootTask.mTaskOrganizer == null) {
+        if (rootTask == null) {
             return;
         }
-        final TaskOrganizerState state =
-                mTaskOrganizerStates.get(rootTask.mTaskOrganizer.asBinder());
-        state.removeStartingWindow(task, prepareAnimation);
+        final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast();
+        if (lastOrganizer == null) {
+            return;
+        }
+        SurfaceControl windowAnimationLeash = null;
+        Rect mainFrame = null;
+        final boolean playShiftUpAnimation = !task.inMultiWindowMode();
+        if (prepareAnimation && playShiftUpAnimation) {
+            final ActivityRecord topActivity = task.topActivityContainsStartingWindow();
+            if (topActivity != null) {
+                final WindowState mainWindow =
+                        topActivity.findMainWindow(false/* includeStartingApp */);
+                if (mainWindow != null) {
+                    final StartingWindowAnimationAdaptor adaptor =
+                            new StartingWindowAnimationAdaptor();
+                    final SurfaceControl.Transaction t = mainWindow.getPendingTransaction();
+                    mainWindow.startAnimation(t, adaptor, false,
+                            ANIMATION_TYPE_STARTING_REVEAL);
+                    windowAnimationLeash = adaptor.mAnimationLeash;
+                    mainFrame = mainWindow.getRelativeFrame();
+                    t.setPosition(windowAnimationLeash, mainFrame.left, mainFrame.top);
+                }
+            }
+        }
+        try {
+            lastOrganizer.removeStartingWindow(task.mTaskId, windowAnimationLeash,
+                    mainFrame, prepareAnimation);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Exception sending onStartTaskFinished callback", e);
+        }
     }
 
     boolean copySplashScreenView(Task task) {
         final Task rootTask = task.getRootTask();
-        if (rootTask == null || rootTask.mTaskOrganizer == null) {
+        if (rootTask == null) {
             return false;
         }
-        final TaskOrganizerState state =
-                mTaskOrganizerStates.get(rootTask.mTaskOrganizer.asBinder());
-        state.copySplashScreenView(task);
+        final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast();
+        if (lastOrganizer == null) {
+            return false;
+        }
+        try {
+            lastOrganizer.copySplashScreenView(task.mTaskId);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Exception sending copyStartingWindowView callback", e);
+            return false;
+        }
         return true;
     }
 
@@ -604,12 +552,18 @@
      */
     public void onAppSplashScreenViewRemoved(Task task) {
         final Task rootTask = task.getRootTask();
-        if (rootTask == null || rootTask.mTaskOrganizer == null) {
+        if (rootTask == null) {
             return;
         }
-        final TaskOrganizerState state =
-                mTaskOrganizerStates.get(rootTask.mTaskOrganizer.asBinder());
-        state.onAppSplashScreenViewRemoved(task);
+        final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast();
+        if (lastOrganizer == null) {
+            return;
+        }
+        try {
+            lastOrganizer.onAppSplashScreenViewRemoved(task.mTaskId);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Exception sending onAppSplashScreenViewRemoved callback", e);
+        }
     }
 
     void onTaskAppeared(ITaskOrganizer organizer, Task task) {
@@ -704,7 +658,7 @@
 
                 ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Delete root task display=%d winMode=%d",
                         task.getDisplayId(), task.getWindowingMode());
-                task.removeImmediately("deleteRootTask");
+                task.remove(true /* withTransition */, "deleteRootTask");
                 return true;
             }
         } finally {
@@ -1017,9 +971,6 @@
             for (int k = 0; k < tasks.size(); k++) {
                 final Task task = tasks.get(k);
                 final int mode = task.getWindowingMode();
-                if (ArrayUtils.contains(UNSUPPORTED_WINDOWING_MODES, mode)) {
-                    continue;
-                }
                 pw.println(innerPrefix + "    ("
                         + WindowConfiguration.windowingModeToString(mode) + ") " + task);
             }
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 43165f8..07f0197 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -16,13 +16,19 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
@@ -36,9 +42,11 @@
 import static android.view.WindowManager.TransitionFlags;
 import static android.view.WindowManager.TransitionType;
 import static android.view.WindowManager.transitTypeToString;
+import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS;
 import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
 import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION;
 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
+import static android.window.TransitionInfo.FLAG_OCCLUDES_KEYGUARD;
 import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
 import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
 import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
@@ -134,6 +142,12 @@
     /** The final animation targets derived from participants after promotion. */
     private ArraySet<WindowContainer> mTargets = null;
 
+    /**
+     * Set of participating windowtokens (activity/wallpaper) which are visible at the end of
+     * the transition animation.
+     */
+    private final ArraySet<WindowToken> mVisibleAtTransitionEndTokens = new ArraySet<>();
+
     /** Custom activity-level animation options and callbacks. */
     private TransitionInfo.AnimationOptions mOverrideOptions;
     private IRemoteCallback mClientAnimationStartCallback = null;
@@ -145,7 +159,7 @@
     // TODO(b/188595497): remove when not needed.
     /** @see RecentsAnimationController#mNavigationBarAttachedToApp */
     private boolean mNavBarAttachedToApp = false;
-    private int mNavBarDisplayId = INVALID_DISPLAY;
+    private int mRecentsDisplayId = INVALID_DISPLAY;
 
     Transition(@TransitionType int type, @TransitionFlags int flags,
             TransitionController controller, BLASTSyncEngine syncEngine) {
@@ -165,6 +179,11 @@
         return mSyncId;
     }
 
+    @TransitionFlags
+    int getFlags() {
+        return mFlags;
+    }
+
     /**
      * Formally starts the transition. Participants can be collected before this is started,
      * but this won't consider itself ready until started -- even if all the participants have
@@ -340,7 +359,14 @@
         for (int i = 0; i < mParticipants.size(); ++i) {
             final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord();
             if (ar != null) {
-                if (!ar.isVisibleRequested()) {
+                boolean visibleAtTransitionEnd = mVisibleAtTransitionEndTokens.contains(ar);
+                // We need both the expected visibility AND current requested-visibility to be
+                // false. If it is expected-visible but not currently visible, it means that
+                // another animation is queued-up to animate this to invisibility, so we can't
+                // remove the surfaces yet. If it is currently visible, but not expected-visible,
+                // then doing commitVisibility here would actually be out-of-order and leave the
+                // activity in a bad state.
+                if (!visibleAtTransitionEnd && !ar.isVisibleRequested()) {
                     boolean commitVisibility = true;
                     if (ar.getDeferHidingClient() && ar.getTask() != null) {
                         if (ar.pictureInPictureArgs != null
@@ -363,17 +389,20 @@
                         activitiesWentInvisible = true;
                     }
                 }
-                if (mChanges.get(ar).mVisible != ar.isVisibleRequested()) {
+                if (mChanges.get(ar).mVisible != visibleAtTransitionEnd) {
                     // Legacy dispatch relies on this (for now).
-                    ar.mEnteringAnimation = ar.isVisibleRequested();
+                    ar.mEnteringAnimation = visibleAtTransitionEnd;
                 }
                 mController.dispatchLegacyAppTransitionFinished(ar);
             }
             final WallpaperWindowToken wt = mParticipants.valueAt(i).asWallpaperToken();
-            if (wt != null && !wt.isVisibleRequested()) {
-                ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
-                        "  Commit wallpaper becoming invisible: %s", wt);
-                wt.commitVisibility(false /* visible */);
+            if (wt != null) {
+                final boolean visibleAtTransitionEnd = mVisibleAtTransitionEndTokens.contains(wt);
+                if (!visibleAtTransitionEnd && !wt.isVisibleRequested()) {
+                    ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
+                            "  Commit wallpaper becoming invisible: %s", wt);
+                    wt.commitVisibility(false /* visible */);
+                }
             }
         }
         if (activitiesWentInvisible) {
@@ -386,6 +415,13 @@
         sendRemoteCallback(mClientAnimationFinishCallback);
 
         legacyRestoreNavigationBarFromApp();
+
+        if (mRecentsDisplayId != INVALID_DISPLAY) {
+            // Clean up input monitors (for recents)
+            final DisplayContent dc =
+                    mController.mAtm.mRootWindowContainer.getDisplayContent(mRecentsDisplayId);
+            dc.getInputMonitor().setActiveRecents(null /* activity */, null /* layer */);
+        }
     }
 
     void abort() {
@@ -483,6 +519,15 @@
             }
         }
 
+        // Record windowtokens (activity/wallpaper) that are expected to be visible after the
+        // transition animation. This will be used in finishTransition to prevent prematurely
+        // committing visibility.
+        for (int i = mParticipants.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = mParticipants.valueAt(i);
+            if (wc.asWindowToken() == null || !wc.isVisibleRequested()) continue;
+            mVisibleAtTransitionEndTokens.add(wc.asWindowToken());
+        }
+
         mStartTransaction = transaction;
         mFinishTransaction = mController.mAtm.mWindowManager.mTransactionFactory.get();
         buildFinishTransaction(mFinishTransaction, info.getRootLeash());
@@ -530,7 +575,43 @@
         }
         final DisplayContent dc =
                 mController.mAtm.mRootWindowContainer.getDisplayContent(displayId);
-        if (dc == null || !dc.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition()
+        if (dc == null) return;
+        mRecentsDisplayId = displayId;
+
+        // Recents has an input-consumer to grab input from the "live tile" app. Set that up here
+        final InputConsumerImpl recentsAnimationInputConsumer =
+                dc.getInputMonitor().getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
+        if (recentsAnimationInputConsumer != null) {
+            // find the top-most going-away activity and the recents activity. The top-most
+            // is used as layer reference while the recents is used for registering the consumer
+            // override.
+            ActivityRecord recentsActivity = null;
+            ActivityRecord topActivity = null;
+            for (int i = 0; i < info.getChanges().size(); ++i) {
+                final TransitionInfo.Change change = info.getChanges().get(i);
+                if (change.getTaskInfo() == null) continue;
+                final Task task = Task.fromWindowContainerToken(
+                        info.getChanges().get(i).getTaskInfo().token);
+                if (task == null) continue;
+                final int activityType = change.getTaskInfo().topActivityType;
+                final boolean isRecents = activityType == ACTIVITY_TYPE_HOME
+                        || activityType == ACTIVITY_TYPE_RECENTS;
+                if (isRecents && recentsActivity == null) {
+                    recentsActivity = task.getTopVisibleActivity();
+                } else if (!isRecents && topActivity == null) {
+                    topActivity = task.getTopNonFinishingActivity();
+                }
+            }
+            if (recentsActivity != null && topActivity != null) {
+                recentsAnimationInputConsumer.mWindowHandle.touchableRegion.set(
+                        topActivity.getBounds());
+                dc.getInputMonitor().setActiveRecents(recentsActivity, topActivity);
+            }
+        }
+
+        // The rest of this function handles nav-bar reparenting
+
+        if (!dc.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition()
                 // Skip the case where the nav bar is controlled by fade rotation.
                 || dc.getFadeRotationAnimationController() != null) {
             return;
@@ -557,7 +638,6 @@
             return;
         }
         mNavBarAttachedToApp = true;
-        mNavBarDisplayId = displayId;
         navWindow.mToken.cancelAnimation();
         final SurfaceControl.Transaction t = navWindow.mToken.getPendingTransaction();
         final SurfaceControl navSurfaceControl = navWindow.mToken.getSurfaceControl();
@@ -581,12 +661,17 @@
         if (!mNavBarAttachedToApp) return;
         mNavBarAttachedToApp = false;
 
+        if (mRecentsDisplayId == INVALID_DISPLAY) {
+            Slog.e(TAG, "Reparented navigation bar without a valid display");
+            mRecentsDisplayId = DEFAULT_DISPLAY;
+        }
+
         if (mController.mStatusBar != null) {
-            mController.mStatusBar.setNavigationBarLumaSamplingEnabled(mNavBarDisplayId, true);
+            mController.mStatusBar.setNavigationBarLumaSamplingEnabled(mRecentsDisplayId, true);
         }
 
         final DisplayContent dc =
-                mController.mAtm.mRootWindowContainer.getDisplayContent(mNavBarDisplayId);
+                mController.mAtm.mRootWindowContainer.getDisplayContent(mRecentsDisplayId);
         final WindowState navWindow = dc.getDisplayPolicy().getNavigationBar();
         if (navWindow == null) return;
         navWindow.setSurfaceTranslationY(0);
@@ -618,13 +703,14 @@
     }
 
     private void handleNonAppWindowsInTransition(int displayId,
-            @TransitionType int transit, int flags) {
+            @TransitionType int transit, @TransitionFlags int flags) {
         final DisplayContent dc =
                 mController.mAtm.mRootWindowContainer.getDisplayContent(displayId);
         if (dc == null) {
             return;
         }
-        if (transit == TRANSIT_KEYGUARD_GOING_AWAY
+        if ((transit == TRANSIT_KEYGUARD_GOING_AWAY
+                || (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0)
                 && !WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation) {
             if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0
                     && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0
@@ -656,7 +742,7 @@
         ArrayMap<WindowContainer, Integer> reasons = new ArrayMap<>();
         for (int i = mParticipants.size() - 1; i >= 0; --i) {
             ActivityRecord r = mParticipants.valueAt(i).asActivityRecord();
-            if (r == null) continue;
+            if (r == null || !r.mVisibleRequested) continue;
             // At this point, r is "ready", but if it's not "ALL ready" then it is probably only
             // ready due to starting-window.
             reasons.put(r, (r.mStartingData instanceof SplashScreenStartingData
@@ -705,6 +791,22 @@
         return wc.asWallpaperToken() != null;
     }
 
+    private static boolean occludesKeyguard(WindowContainer wc) {
+        final ActivityRecord ar = wc.asActivityRecord();
+        if (ar != null) {
+            return ar.canShowWhenLocked();
+        }
+        final Task t = wc.asTask();
+        if (t != null) {
+            // Get the top activity which was visible (since this is going away, it will remain
+            // client visible until the transition is finished).
+            // skip hidden (or about to hide) apps
+            final ActivityRecord top = t.getActivity(WindowToken::isClientVisible);
+            return top != null && top.canShowWhenLocked();
+        }
+        return false;
+    }
+
     /**
      * Under some conditions (eg. all visible targets within a parent container are transitioning
      * the same way) the transition can be "promoted" to the parent container. This means an
@@ -1049,6 +1151,10 @@
                 final ActivityManager.RunningTaskInfo tinfo = new ActivityManager.RunningTaskInfo();
                 task.fillTaskInfo(tinfo);
                 change.setTaskInfo(tinfo);
+                change.setRotationAnimation(getTaskRotationAnimation(task));
+                final ActivityRecord topMostActivity = task.getTopMostActivity();
+                change.setAllowEnterPip(topMostActivity != null
+                        && topMostActivity.checkEnterPictureInPictureAppOpsState());
             }
             out.addChange(change);
         }
@@ -1056,6 +1162,23 @@
         return out;
     }
 
+    private static int getTaskRotationAnimation(@NonNull Task task) {
+        final ActivityRecord top = task.getTopVisibleActivity();
+        if (top == null) return ROTATION_ANIMATION_UNSPECIFIED;
+        final WindowState mainWin = top.findMainWindow(false);
+        if (mainWin == null) return ROTATION_ANIMATION_UNSPECIFIED;
+        int anim = mainWin.getRotationAnimationHint();
+        if (anim >= 0) return anim;
+        anim = mainWin.getAttrs().rotationAnimation;
+        if (anim != ROTATION_ANIMATION_SEAMLESS) return anim;
+        if (mainWin != task.mDisplayContent.getDisplayPolicy().getTopFullscreenOpaqueWindow()
+                || !top.matchParentBounds()) {
+            // At the moment, we only support seamless rotation if there is only one window showing.
+            return ROTATION_ANIMATION_UNSPECIFIED;
+        }
+        return mainWin.getAttrs().rotationAnimation;
+    }
+
     boolean getLegacyIsReady() {
         return mState == STATE_STARTED && mSyncId >= 0 && mSyncEngine.isReady(mSyncId);
     }
@@ -1149,10 +1272,16 @@
             final DisplayContent dc = wc.asDisplayContent();
             if (dc != null) {
                 flags |= FLAG_IS_DISPLAY;
+                if (dc.hasAlertWindowSurfaces()) {
+                    flags |= FLAG_DISPLAY_HAS_ALERT_WINDOWS;
+                }
             }
             if (isWallpaper(wc)) {
                 flags |= FLAG_IS_WALLPAPER;
             }
+            if (occludesKeyguard(wc)) {
+                flags |= FLAG_OCCLUDES_KEYGUARD;
+            }
             return flags;
         }
 
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 44dee4d..419b4c3 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -18,11 +18,7 @@
 
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
-import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_NONE;
 import static android.view.WindowManager.TRANSIT_OPEN;
 
@@ -121,11 +117,16 @@
 
     void registerTransitionPlayer(@Nullable ITransitionPlayer player) {
         try {
+            // Note: asBinder() can be null if player is same process (likely in a test).
             if (mTransitionPlayer != null) {
-                mTransitionPlayer.asBinder().unlinkToDeath(mTransitionPlayerDeath, 0);
+                if (mTransitionPlayer.asBinder() != null) {
+                    mTransitionPlayer.asBinder().unlinkToDeath(mTransitionPlayerDeath, 0);
+                }
                 mTransitionPlayer = null;
             }
-            player.asBinder().linkToDeath(mTransitionPlayerDeath, 0);
+            if (player.asBinder() != null) {
+                player.asBinder().linkToDeath(mTransitionPlayerDeath, 0);
+            }
             mTransitionPlayer = player;
         } catch (RemoteException e) {
             throw new RuntimeException("Unable to set transition player");
@@ -230,6 +231,10 @@
         if (isCollecting()) {
             // Make the collecting transition wait until this request is ready.
             mCollectingTransition.setReady(readyGroupRef, false);
+            if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) {
+                // Add keyguard flag to dismiss keyguard
+                mCollectingTransition.addFlag(flags);
+            }
         } else {
             newTransition = requestStartTransition(createTransition(type, flags),
                     trigger != null ? trigger.asTask() : null, remoteTransition);
@@ -354,11 +359,7 @@
     }
 
     void dispatchLegacyAppTransitionStarting(TransitionInfo info) {
-        final boolean keyguardGoingAway = info.getType() == TRANSIT_KEYGUARD_GOING_AWAY
-                || (info.getFlags() & (TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE
-                        | TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION
-                        | TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER
-                        | TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION)) != 0;
+        final boolean keyguardGoingAway = info.isKeyguardGoingAway();
         for (int i = 0; i < mLegacyListeners.size(); ++i) {
             mLegacyListeners.get(i).onAppTransitionStartingLocked(keyguardGoingAway,
                     0 /* durationHint */, SystemClock.uptimeMillis(),
diff --git a/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java b/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java
index 416b9df..25f7269 100644
--- a/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/WallpaperAnimationAdapter.java
@@ -93,7 +93,7 @@
     RemoteAnimationTarget createRemoteAnimationTarget() {
         mTarget = new RemoteAnimationTarget(-1, -1, getLeash(), false, null, null,
                 mWallpaperToken.getPrefixOrderIndex(), new Point(), null, null,
-                mWallpaperToken.getWindowConfiguration(), true, null, null, null);
+                mWallpaperToken.getWindowConfiguration(), true, null, null, null, false);
         return mTarget;
     }
 
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 95e5fc2..7893612 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -16,6 +16,8 @@
 
 package com.android.server.wm;
 
+import static android.app.WallpaperManager.COMMAND_FREEZE;
+import static android.app.WallpaperManager.COMMAND_UNFREEZE;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
@@ -79,6 +81,8 @@
     private int mLastWallpaperDisplayOffsetX = Integer.MIN_VALUE;
     private int mLastWallpaperDisplayOffsetY = Integer.MIN_VALUE;
     private final float mMaxWallpaperScale;
+    // Whether COMMAND_FREEZE was dispatched.
+    private boolean mLastFrozen = false;
 
     // This is set when we are waiting for a wallpaper to tell us it is done
     // changing its scroll position.
@@ -194,6 +198,7 @@
                 if (DEBUG_WALLPAPER) Slog.v(TAG,
                         "Win " + w + ": token animating, looking behind.");
             }
+            mFindResults.setIsWallpaperTargetForLetterbox(w.hasWallpaperForLetterboxBackground());
             // Found a target! End search.
             return true;
         }
@@ -424,20 +429,25 @@
     Bundle sendWindowWallpaperCommand(
             WindowState window, String action, int x, int y, int z, Bundle extras, boolean sync) {
         if (window == mWallpaperTarget || window == mPrevWallpaperTarget) {
-            boolean doWait = sync;
-            for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
-                final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
-                token.sendWindowWallpaperCommand(action, x, y, z, extras, sync);
-            }
-
-            if (doWait) {
-                // TODO: Need to wait for result.
-            }
+            sendWindowWallpaperCommand(action, x, y, z, extras, sync);
         }
 
         return null;
     }
 
+    private void sendWindowWallpaperCommand(
+                String action, int x, int y, int z, Bundle extras, boolean sync) {
+        boolean doWait = sync;
+        for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
+            final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
+            token.sendWindowWallpaperCommand(action, x, y, z, extras, sync);
+        }
+
+        if (doWait) {
+            // TODO: Need to wait for result.
+        }
+    }
+
     private void updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync) {
         WindowState target = mWallpaperTarget;
         if (target != null) {
@@ -549,6 +559,9 @@
         final WindowState prevWallpaperTarget = mWallpaperTarget;
         mWallpaperTarget = wallpaperTarget;
 
+        if (prevWallpaperTarget == null && wallpaperTarget != null) {
+            updateWallpaperOffsetLocked(mWallpaperTarget, false);
+        }
         if (wallpaperTarget == null || prevWallpaperTarget == null) {
             return;
         }
@@ -641,6 +654,13 @@
 
         updateWallpaperTokens(visible);
 
+        if (visible && mLastFrozen != mFindResults.isWallpaperTargetForLetterbox) {
+            mLastFrozen = mFindResults.isWallpaperTargetForLetterbox;
+            sendWindowWallpaperCommand(
+                    mFindResults.isWallpaperTargetForLetterbox ? COMMAND_FREEZE : COMMAND_UNFREEZE,
+                    /* x= */ 0, /* y= */ 0, /* z= */ 0, /* extras= */ null, /* sync= */ false);
+        }
+
         if (DEBUG_WALLPAPER_LIGHT)  Slog.d(TAG, "New wallpaper: target=" + mWallpaperTarget
                 + " prev=" + mPrevWallpaperTarget);
     }
@@ -838,6 +858,7 @@
         boolean useTopWallpaperAsTarget = false;
         WindowState wallpaperTarget = null;
         boolean resetTopWallpaper = false;
+        boolean isWallpaperTargetForLetterbox = false;
 
         void setTopWallpaper(WindowState win) {
             topWallpaper = win;
@@ -851,11 +872,16 @@
             useTopWallpaperAsTarget = topWallpaperAsTarget;
         }
 
+        void setIsWallpaperTargetForLetterbox(boolean isWallpaperTargetForLetterbox) {
+            this.isWallpaperTargetForLetterbox = isWallpaperTargetForLetterbox;
+        }
+
         void reset() {
             topWallpaper = null;
             wallpaperTarget = null;
             useTopWallpaperAsTarget = false;
             resetTopWallpaper = false;
+            isWallpaperTargetForLetterbox = false;
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/Watermark.java b/services/core/java/com/android/server/wm/Watermark.java
index 66ab094..9780d33 100644
--- a/services/core/java/com/android/server/wm/Watermark.java
+++ b/services/core/java/com/android/server/wm/Watermark.java
@@ -31,6 +31,7 @@
 import android.view.Surface;
 import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl;
+import android.view.WindowManagerPolicyConstants;
 
 /**
  * Displays a watermark on top of the window manager's windows.
@@ -117,7 +118,7 @@
                     .setFormat(PixelFormat.TRANSLUCENT)
                     .setCallsite(TITLE)
                     .build();
-            t.setLayer(ctrl, WindowManagerService.TYPE_LAYER_MULTIPLIER * 100)
+            t.setLayer(ctrl, WindowManagerPolicyConstants.WATERMARK_LAYER)
                     .setPosition(ctrl, 0, 0)
                     .show(ctrl);
             // Ensure we aren't considered as obscuring for Input purposes.
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index ffee0b7..b6c8e13 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2689,6 +2689,11 @@
             final RemoteAnimationController.RemoteAnimationRecord adapters =
                     controller.createRemoteAnimationRecord(this, mTmpPoint, localBounds,
                             screenBounds, (isChanging ? mSurfaceFreezer.mFreezeBounds : null));
+            if (!isChanging) {
+                adapters.setMode(enter
+                        ? RemoteAnimationTarget.MODE_OPENING
+                        : RemoteAnimationTarget.MODE_CLOSING);
+            }
             resultAdapters = new Pair<>(adapters.mAdapter, adapters.mThumbnailAdapter);
         } else if (isChanging) {
             final float durationScale = mWmService.getTransitionAnimationScaleLocked();
@@ -2842,6 +2847,14 @@
         return false;
     }
 
+    /**
+     * {@code true} to indicate that this container can be a candidate of
+     * {@link AppTransitionController#getAnimationTargets(ArraySet, ArraySet, boolean) animation
+     * target}. */
+    boolean canBeAnimationTarget() {
+        return false;
+    }
+
     boolean okToDisplay() {
         final DisplayContent dc = getDisplayContent();
         return dc != null && dc.okToDisplay();
@@ -3400,6 +3413,29 @@
     }
 
     /**
+     * Forces the receiver container to always use the configuration of the supplier container as
+     * its requested override configuration. It allows to propagate configuration without changing
+     * the relationship between child and parent.
+     */
+    static void overrideConfigurationPropagation(WindowContainer<?> receiver,
+            WindowContainer<?> supplier) {
+        final ConfigurationContainerListener listener = new ConfigurationContainerListener() {
+            @Override
+            public void onMergedOverrideConfigurationChanged(Configuration mergedOverrideConfig) {
+                receiver.onRequestedOverrideConfigurationChanged(supplier.getConfiguration());
+            }
+        };
+        supplier.registerConfigurationChangeListener(listener);
+        receiver.registerWindowContainerListener(new WindowContainerListener() {
+            @Override
+            public void onRemoved() {
+                receiver.unregisterWindowContainerListener(this);
+                supplier.unregisterConfigurationChangeListener(listener);
+            }
+        });
+    }
+
+    /**
      * Returns the {@link WindowManager.LayoutParams.WindowType}.
      */
     @WindowManager.LayoutParams.WindowType int getWindowType() {
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 5bc4d49..132f139 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -676,24 +676,43 @@
     public abstract String getWindowName(@NonNull IBinder binder);
 
     /**
-     * Return the window name of IME Insets control target.
+     * The callback after the request of show/hide input method is sent.
      *
+     * @param show Whether to show or hide input method.
+     * @param focusedToken The token of focused window.
+     * @param requestToken The token of window who requests the change.
      * @param displayId The ID of the display which input method is currently focused.
-     * @return The corresponding {@link WindowState#getName()}
+     * @return The information of the input method target.
      */
-    public abstract @Nullable String getImeControlTargetNameForLogging(int displayId);
+    public abstract ImeTargetInfo onToggleImeRequested(boolean show,
+            @NonNull IBinder focusedToken, @NonNull IBinder requestToken, int displayId);
 
-    /**
-     * Return the current window name of the input method is on top of.
-     *
-     * Note that the concept of this window is only reparent the target window behind the input
-     * method window, it may different with the window which reported by
-     * {@code InputMethodManagerService#reportStartInput} which has input connection.
-     *
-     * @param displayId The ID of the display which input method is currently focused.
-     * @return The corresponding {@link WindowState#getName()}
-     */
-    public abstract @Nullable String getImeTargetNameForLogging(int displayId);
+    /** The information of input method target when IME is requested to show or hide. */
+    public static class ImeTargetInfo {
+        public final String focusedWindowName;
+        public final String requestWindowName;
+
+        /** The window name of IME Insets control target. */
+        public final String imeControlTargetName;
+
+        /**
+         * The current window name of the input method is on top of.
+         * <p>
+         * Note that the concept of this window is only used to reparent the target window behind
+         * the input method window, it may be different from the window reported by
+         * {@link com.android.server.inputmethod.InputMethodManagerService#reportStartInput} which
+         * has input connection.
+         */
+        public final String imeLayerTargetName;
+
+        public ImeTargetInfo(String focusedWindowName, String requestWindowName,
+                String imeControlTargetName, String imeLayerTargetName) {
+            this.focusedWindowName = focusedWindowName;
+            this.requestWindowName = requestWindowName;
+            this.imeControlTargetName = imeControlTargetName;
+            this.imeLayerTargetName = imeLayerTargetName;
+        }
+    }
 
     /**
      * Moves the {@link WindowToken} {@code binder} to the display specified by {@code displayId}.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7f6dce4..1265744 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -85,8 +85,10 @@
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_BLAST_SYNC;
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
+import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;
 import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
 import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN;
+import static android.window.WindowProviderService.isWindowProviderService;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BOOT;
@@ -339,27 +341,6 @@
 
     static final boolean PROFILE_ORIENTATION = false;
 
-    /** How much to multiply the policy's type layer, to reserve room
-     * for multiple windows of the same type and Z-ordering adjustment
-     * with TYPE_LAYER_OFFSET. */
-    static final int TYPE_LAYER_MULTIPLIER = 10000;
-
-    /** Offset from TYPE_LAYER_MULTIPLIER for moving a group of windows above
-     * or below others in the same layer. */
-    static final int TYPE_LAYER_OFFSET = 1000;
-
-    /** How much to increment the layer for each window, to reserve room
-     * for effect surfaces between them.
-     */
-    static final int WINDOW_LAYER_MULTIPLIER = 5;
-
-    /**
-     * Animation thumbnail is as far as possible below the window above
-     * the thumbnail (or in other words as far as possible above the window
-     * below it).
-     */
-    static final int LAYER_OFFSET_THUMBNAIL = WINDOW_LAYER_MULTIPLIER - 1;
-
     /** The maximum length we will accept for a loaded animation duration:
      * this is 10 seconds.
      */
@@ -1236,7 +1217,9 @@
         mAssistantOnTopOfDream = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_assistantOnTopOfDream);
 
-        mLetterboxConfiguration = new LetterboxConfiguration(context);
+        mLetterboxConfiguration = new LetterboxConfiguration(
+                // Using SysUI context to have access to Material colors extracted from Wallpaper.
+                ActivityThread.currentActivityThread().getSystemUiContext());
 
         mInputManager = inputManager; // Must be before createDisplayContentLocked.
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
@@ -1730,16 +1713,22 @@
                     && mWindowContextListenerController.hasListener(windowContextToken)) {
                 final int windowContextType = mWindowContextListenerController
                         .getWindowType(windowContextToken);
+                final Bundle options = mWindowContextListenerController
+                        .getOptions(windowContextToken);
                 if (type != windowContextType) {
                     ProtoLog.w(WM_ERROR, "Window types in WindowContext and"
                             + " LayoutParams.type should match! Type from LayoutParams is %d,"
                             + " but type from WindowContext is %d", type, windowContextType);
-                    return WindowManagerGlobal.ADD_INVALID_TYPE;
+                    // We allow WindowProviderService to add window other than windowContextType,
+                    // but the WindowProviderService won't be associated with the window's
+                    // WindowToken.
+                    if (!isWindowProviderService(options)) {
+                        return WindowManagerGlobal.ADD_INVALID_TYPE;
+                    }
+                } else {
+                    mWindowContextListenerController.registerWindowContainerListener(
+                            windowContextToken, token, callingUid, type, options);
                 }
-                final Bundle options = mWindowContextListenerController
-                        .getOptions(windowContextToken);
-                mWindowContextListenerController.registerWindowContainerListener(
-                        windowContextToken, token, callingUid, type, options);
             }
 
             // From now on, no exceptions or errors allowed!
@@ -1769,9 +1758,8 @@
             final boolean hideSystemAlertWindows = !mHidingNonSystemOverlayWindows.isEmpty();
             win.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows);
 
-            final ActivityRecord tokenActivity = token.asActivityRecord();
-            if (type == TYPE_APPLICATION_STARTING && tokenActivity != null) {
-                tokenActivity.mStartingWindow = win;
+            if (type == TYPE_APPLICATION_STARTING && activity != null) {
+                activity.attachStartingWindow(win);
                 ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "addWindow: %s startingWindow=%s",
                         activity, win);
             }
@@ -3050,7 +3038,7 @@
         mSettingsObserver.updateSystemUiSettings(true /* handleChange */);
         synchronized (mGlobalLock) {
             // force a re-application of focused window sysui visibility on each display.
-            mRoot.forAllDisplayPolicies(DisplayPolicy::resetSystemUiVisibilityLw);
+            mRoot.forAllDisplayPolicies(DisplayPolicy::resetSystemBarAttributes);
         }
     }
 
@@ -5292,6 +5280,7 @@
                 case LAYOUT_AND_ASSIGN_WINDOW_LAYERS_IF_NEEDED: {
                     synchronized (mGlobalLock) {
                         final DisplayContent displayContent = (DisplayContent) msg.obj;
+                        displayContent.mLayoutAndAssignWindowLayersScheduled = false;
                         displayContent.layoutAndAssignWindowLayersIfNeeded();
                     }
                     break;
@@ -7086,6 +7075,7 @@
                             "requestScrollCapture: caught exception dispatching to window."
                                     + "token=%s", targetWindow.mClient.asBinder());
                     responseBuilder.setWindowTitle(targetWindow.getName());
+                    responseBuilder.setPackageName(targetWindow.getOwningPackage());
                     responseBuilder.setDescription(String.format("caught exception: %s", e));
                     listener.onScrollCaptureResponse(responseBuilder.build());
                 }
@@ -7899,30 +7889,37 @@
         }
 
         @Override
-        public String getImeControlTargetNameForLogging(int displayId) {
+        public ImeTargetInfo onToggleImeRequested(boolean show, IBinder focusedToken,
+                IBinder requestToken, int displayId) {
+            final String focusedWindowName;
+            final String requestWindowName;
+            final String imeControlTargetName;
+            final String imeLayerTargetName;
             synchronized (mGlobalLock) {
+                final WindowState focusedWin = mWindowMap.get(focusedToken);
+                focusedWindowName = focusedWin != null ? focusedWin.getName() : "null";
+                final WindowState requestWin = mWindowMap.get(requestToken);
+                requestWindowName = requestWin != null ? requestWin.getName() : "null";
                 final DisplayContent dc = mRoot.getDisplayContent(displayId);
-                if (dc == null) {
-                    return null;
+                if (dc != null) {
+                    final InsetsControlTarget controlTarget = dc.getImeTarget(IME_TARGET_CONTROL);
+                    if (controlTarget != null) {
+                        final WindowState w = InsetsControlTarget.asWindowOrNull(controlTarget);
+                        imeControlTargetName = w != null ? w.getName() : controlTarget.toString();
+                    } else {
+                        imeControlTargetName = "null";
+                    }
+                    final InsetsControlTarget target = dc.getImeTarget(IME_TARGET_LAYERING);
+                    imeLayerTargetName = target != null ? target.getWindow().getName() : "null";
+                    if (show) {
+                        dc.onShowImeRequested();
+                    }
+                } else {
+                    imeControlTargetName = imeLayerTargetName = "no-display";
                 }
-                final InsetsControlTarget target = dc.getImeTarget(IME_TARGET_CONTROL);
-                if (target == null) {
-                    return null;
-                }
-                final WindowState win = target.getWindow();
-                return win != null ? win.getName() : target.toString();
             }
-        }
-
-        @Override
-        public String getImeTargetNameForLogging(int displayId) {
-            synchronized (mGlobalLock) {
-                final DisplayContent dc = mRoot.getDisplayContent(displayId);
-                if (dc == null || dc.getImeTarget(IME_TARGET_LAYERING) == null) {
-                    return null;
-                }
-                return dc.getImeTarget(IME_TARGET_LAYERING).getWindow().getName();
-            }
+            return new ImeTargetInfo(focusedWindowName, requestWindowName, imeControlTargetName,
+                    imeLayerTargetName);
         }
 
         @Override
@@ -8120,9 +8117,15 @@
             // This could prevent if there is no container animation, we still have to apply the
             // pending transaction and exit waiting.
             mAnimator.mNotifyWhenNoAnimation = true;
+            boolean animateStarting = false;
             while (timeoutRemaining > 0) {
+                // Waiting until all starting windows has finished animating.
+                animateStarting = mRoot.forAllActivities(a -> {
+                    return a.hasStartingWindow();
+                });
                 boolean isAnimating = mAnimator.isAnimationScheduled()
-                        || mRoot.isAnimating(TRANSITION | CHILDREN, ANIMATION_TYPE_ALL);
+                        || mRoot.isAnimating(TRANSITION | CHILDREN, ANIMATION_TYPE_ALL)
+                        || animateStarting;
                 if (!isAnimating) {
                     // isAnimating is a legacy transition query and will be removed, so also add
                     // a check for whether this is in a shell-transition when not using legacy.
@@ -8142,13 +8145,14 @@
             WindowContainer animatingContainer;
             animatingContainer = mRoot.getAnimatingContainer(TRANSITION | CHILDREN,
                     ANIMATION_TYPE_ALL);
-            if (mAnimator.isAnimationScheduled() || animatingContainer != null) {
+            if (mAnimator.isAnimationScheduled() || animatingContainer != null || animateStarting) {
                 Slog.w(TAG, "Timed out waiting for animations to complete,"
                         + " animatingContainer=" + animatingContainer
                         + " animationType=" + SurfaceAnimator.animationTypeToString(
                         animatingContainer != null
                                 ? animatingContainer.mSurfaceAnimator.getAnimationType()
-                                : SurfaceAnimator.ANIMATION_TYPE_NONE));
+                                : SurfaceAnimator.ANIMATION_TYPE_NONE)
+                        + " animateStarting=" + animateStarting);
             }
         }
     }
@@ -8241,7 +8245,7 @@
         }
 
         updateInputChannel(clientChannel.getToken(), callingUid, callingPid, displayId, surface,
-                name, applicationHandle, flags, privateFlags, type, null /* region */);
+                name, applicationHandle, flags, privateFlags, type, null /* region */, window);
 
         clientChannel.copyTo(outInputChannel);
     }
@@ -8249,9 +8253,10 @@
     private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid,
                                     int displayId, SurfaceControl surface, String name,
                                     InputApplicationHandle applicationHandle, int flags,
-                                    int privateFlags, int type, Region region) {
+                                    int privateFlags, int type, Region region, IWindow window) {
         InputWindowHandle h = new InputWindowHandle(applicationHandle, displayId);
         h.token = channelToken;
+        h.setWindowToken(window);
         h.name = name;
 
         final int sanitizedFlags = flags & (LayoutParams.FLAG_NOT_TOUCHABLE
@@ -8307,7 +8312,7 @@
         }
 
         updateInputChannel(channelToken, win.mOwnerUid, win.mOwnerPid, displayId, surface, name,
-                applicationHandle, flags, privateFlags, win.mWindowType, region);
+                applicationHandle, flags, privateFlags, win.mWindowType, region, win.mClient);
     }
 
     /** Return whether layer tracing is enabled */
@@ -8636,8 +8641,9 @@
             if (imeTargetWindowTask == null) {
                 return false;
             }
-            final TaskSnapshot snapshot = mAtmService.getTaskSnapshot(imeTargetWindowTask.mTaskId,
-                    false /* isLowResolution */);
+            final TaskSnapshot snapshot = getTaskSnapshot(imeTargetWindowTask.mTaskId,
+                    imeTargetWindowTask.mUserId, false /* isLowResolution */,
+                    false /* restoreFromDisk */);
             return snapshot != null && snapshot.hasImeSurface();
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index d5965494..1d1cb70 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -24,6 +24,7 @@
 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
 
+import android.content.res.Resources.NotFoundException;
 import android.graphics.Color;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -606,7 +607,7 @@
             return -1;
         } catch (IllegalArgumentException  e) {
             getErrPrintWriter().println(
-                    "Error: 'reset' or aspect ratio should be provided as an argument " + e);
+                    "Error: aspect ratio should be provided as an argument " + e);
             return -1;
         }
         synchronized (mInternal.mGlobalLock) {
@@ -625,7 +626,7 @@
             return -1;
         } catch (IllegalArgumentException  e) {
             getErrPrintWriter().println(
-                    "Error: 'reset' or corners radius should be provided as an argument " + e);
+                    "Error: corners radius should be provided as an argument " + e);
             return -1;
         }
         synchronized (mInternal.mGlobalLock) {
@@ -653,13 +654,13 @@
                     break;
                 default:
                     getErrPrintWriter().println(
-                            "Error: 'reset', 'solid_color', 'app_color_background' or "
+                            "Error: '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 "
+                    "Error: 'solid_color', 'app_color_background' or "
                         + "'wallpaper' should be provided as an argument" + e);
             return -1;
         }
@@ -669,6 +670,24 @@
         return 0;
     }
 
+    private int runSetLetterboxBackgroundColorResource(PrintWriter pw) throws RemoteException {
+        final int colorId;
+        try {
+            String arg = getNextArgRequired();
+            colorId = mInternal.mContext.getResources()
+                    .getIdentifier(arg, "color", "com.android.internal");
+        } catch (NotFoundException e) {
+            getErrPrintWriter().println(
+                    "Error: color in '@android:color/resource_name' format should be provided as "
+                            + "an argument " + e);
+            return -1;
+        }
+        synchronized (mInternal.mGlobalLock) {
+            mLetterboxConfiguration.setLetterboxBackgroundColorResourceId(colorId);
+        }
+        return 0;
+    }
+
     private int runSetLetterboxBackgroundColor(PrintWriter pw) throws RemoteException {
         final Color color;
         try {
@@ -676,7 +695,7 @@
             color = Color.valueOf(Color.parseColor(arg));
         } catch (IllegalArgumentException  e) {
             getErrPrintWriter().println(
-                    "Error: 'reset' or color in #RRGGBB format should be provided as "
+                    "Error: color in #RRGGBB format should be provided as "
                             + "an argument " + e);
             return -1;
         }
@@ -697,7 +716,7 @@
             return -1;
         } catch (IllegalArgumentException  e) {
             getErrPrintWriter().println(
-                    "Error: 'reset' or blur radius should be provided as an argument " + e);
+                    "Error: blur radius should be provided as an argument " + e);
             return -1;
         }
         synchronized (mInternal.mGlobalLock) {
@@ -717,7 +736,7 @@
             return -1;
         } catch (IllegalArgumentException  e) {
             getErrPrintWriter().println(
-                    "Error: 'reset' or alpha should be provided as an argument " + e);
+                    "Error: alpha should be provided as an argument " + e);
             return -1;
         }
         synchronized (mInternal.mGlobalLock) {
@@ -736,7 +755,7 @@
             return -1;
         } catch (IllegalArgumentException  e) {
             getErrPrintWriter().println(
-                    "Error: 'reset' or multiplier should be provided as an argument " + e);
+                    "Error: multiplier should be provided as an argument " + e);
             return -1;
         }
         synchronized (mInternal.mGlobalLock) {
@@ -764,6 +783,9 @@
                 case "--backgroundColor":
                     runSetLetterboxBackgroundColor(pw);
                     break;
+                case "--backgroundColorResource":
+                    runSetLetterboxBackgroundColorResource(pw);
+                    break;
                 case "--wallpaperBlurRadius":
                     runSetLetterboxBackgroundWallpaperBlurRadius(pw);
                     break;
@@ -1031,6 +1053,11 @@
         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("      --backgroundColorResource resource_name");
+        pw.println("        Color resource name of letterbox background which is used when");
+        pw.println("        background type is 'solid-color'. Use (set)get-letterbox-style to");
+        pw.println("        check and control background type. Parameter is a color resource");
+        pw.println("        name, for example, @android:color/system_accent2_50.");
         pw.println("      --wallpaperBlurRadius radius");
         pw.println("        Blur radius for 'wallpaper' letterbox background. If radius <= 0");
         pw.println("        both it and R.dimen.config_letterboxBackgroundWallpaperBlurRadius");
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index abf8afa..b568774 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -56,6 +56,7 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.RemoteException;
+import android.util.AndroidRuntimeException;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
@@ -322,6 +323,7 @@
         int effects = 0;
         ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId);
         mService.deferWindowLayout();
+        mService.mTaskSupervisor.setDeferRootVisibilityUpdate(true /* deferUpdate */);
         try {
             if (transition != null) {
                 // First check if we have a display rotation transition and if so, update it.
@@ -411,6 +413,7 @@
                 task.setMainWindowSizeChangeTransaction(sft);
             }
             if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
+                mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
                 // Already calls ensureActivityConfig
                 mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
                 mService.mRootWindowContainer.resumeFocusedTasksTopActivities();
@@ -432,6 +435,7 @@
                 mService.addWindowLayoutReasons(LAYOUT_REASON_CONFIG_CHANGED);
             }
         } finally {
+            mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
             mService.continueWindowLayout();
         }
     }
@@ -472,7 +476,15 @@
                 throw new UnsupportedOperationException("Not supported to set multi-window"
                         + " windowing mode during locked task mode.");
             }
+
+            final int prevMode = container.getWindowingMode();
             container.setWindowingMode(windowingMode);
+            if (prevMode != container.getWindowingMode()) {
+                // The activity in the container may become focusable or non-focusable due to
+                // windowing modes changes (such as entering or leaving pinned windowing mode),
+                // so also apply the lifecycle effects to this transaction.
+                effects |= TRANSACT_EFFECTS_LIFECYCLE;
+            }
         }
         return effects;
     }
@@ -687,10 +699,9 @@
                         .startActivityInTaskFragment(tf, activityIntent, activityOptions,
                                 hop.getCallingActivity());
                 if (!isStartResultSuccessful(result)) {
-                    final Throwable exception =
-                            new ActivityNotFoundException("start activity in taskFragment failed");
                     sendTaskFragmentOperationFailure(tf.getTaskFragmentOrganizer(),
-                            errorCallbackToken, exception);
+                            errorCallbackToken,
+                            convertStartFailureToThrowable(result, activityIntent));
                 }
                 break;
             case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT:
@@ -720,14 +731,31 @@
                 fragmentToken = hop.getContainer();
                 final IBinder adjacentFragmentToken = hop.getAdjacentRoot();
                 final TaskFragment tf1 = mLaunchTaskFragments.get(fragmentToken);
-                final TaskFragment tf2 = mLaunchTaskFragments.get(adjacentFragmentToken);
-                if (tf1 == null || tf2 == null) {
+                final TaskFragment tf2 = adjacentFragmentToken != null
+                        ? mLaunchTaskFragments.get(adjacentFragmentToken)
+                        : null;
+                if (tf1 == null || (adjacentFragmentToken != null && tf2 == null)) {
                     final Throwable exception = new IllegalArgumentException(
                             "Not allowed to set adjacent on invalid fragment tokens");
                     sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
                     break;
                 }
                 tf1.setAdjacentTaskFragment(tf2);
+
+                final Bundle bundle = hop.getLaunchOptions();
+                final WindowContainerTransaction.TaskFragmentAdjacentOptions adjacentOptions =
+                        bundle != null ? new WindowContainerTransaction.TaskFragmentAdjacentOptions(
+                                bundle) : null;
+                if (adjacentOptions == null) {
+                    break;
+                }
+
+                tf1.setDelayLastActivityRemoval(
+                        adjacentOptions.isDelayPrimaryLastActivityRemoval());
+                if (tf2 != null) {
+                    tf2.setDelayLastActivityRemoval(
+                            adjacentOptions.isDelaySecondaryLastActivityRemoval());
+                }
                 break;
         }
         return effects;
@@ -1169,7 +1197,12 @@
             return;
         }
         mLaunchTaskFragments.removeAt(index);
-        taskFragment.removeImmediately();
+        taskFragment.remove(true /* withTransition */, "deleteTaskFragment");
+    }
+
+    @Nullable
+    TaskFragment getTaskFragment(IBinder tfToken) {
+        return mLaunchTaskFragments.get(tfToken);
     }
 
     static class CallerInfo {
@@ -1190,4 +1223,21 @@
         mService.mTaskFragmentOrganizerController
                 .onTaskFragmentError(organizer, errorCallbackToken, exception);
     }
+
+    private Throwable convertStartFailureToThrowable(int result, Intent intent) {
+        switch (result) {
+            case ActivityManager.START_INTENT_NOT_RESOLVED:
+            case ActivityManager.START_CLASS_NOT_FOUND:
+                return new ActivityNotFoundException("No Activity found to handle " + intent);
+            case ActivityManager.START_PERMISSION_DENIED:
+                return new SecurityException("Permission denied and not allowed to start activity "
+                        + intent);
+            case ActivityManager.START_CANCELED:
+                return new AndroidRuntimeException("Activity could not be started for " + intent
+                        + " with error code : " + result);
+            default:
+                return new AndroidRuntimeException("Start activity failed with error code : "
+                        + result + " when starting " + intent);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowOrientationListener.java b/services/core/java/com/android/server/wm/WindowOrientationListener.java
index 0ded8fb..a967ea8 100644
--- a/services/core/java/com/android/server/wm/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/wm/WindowOrientationListener.java
@@ -68,7 +68,7 @@
     private static final String KEY_ROTATION_MEMORIZATION_TIMEOUT =
             "rotation_memorization_timeout_millis";
     private static final long DEFAULT_ROTATION_RESOLVER_TIMEOUT_MILLIS = 700L;
-    private static final long DEFAULT_ROTATION_MEMORIZATION_TIMEOUT_MILLIS = 10_000L; // 10 seconds
+    private static final long DEFAULT_ROTATION_MEMORIZATION_TIMEOUT_MILLIS = 3_000L; // 3 seconds
 
     private Handler mHandler;
     private SensorManager mSensorManager;
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 65b065a..cd29f0e 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -730,11 +730,14 @@
             canUpdate = true;
         }
 
-        // Compare the z-order of ActivityStacks if both activities landed on same display.
-        if (display == topDisplay
-                && mPreQTopResumedActivity.getRootTask().compareTo(
-                        activity.getRootTask()) <= 0) {
-            canUpdate = true;
+        // Update the topmost activity if the activity has higher z-order than the current
+        // top-resumed activity.
+        if (!canUpdate) {
+            final ActivityRecord ar = topDisplay.getActivity(r -> r == activity,
+                    true /* traverseTopToBottom */, mPreQTopResumedActivity);
+            if (ar != null && ar != mPreQTopResumedActivity) {
+                canUpdate = true;
+            }
         }
 
         if (canUpdate) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c9b1506..1ca0c7e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -107,6 +107,8 @@
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_DOCKED;
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_FREEFORM;
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
+import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_MULTIPLIER;
+import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
@@ -153,8 +155,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.H.WINDOW_STATE_BLAST_SYNC_TIMEOUT;
 import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
-import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
-import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
@@ -193,6 +193,7 @@
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityTaskManager;
 import android.app.AppOpsManager;
 import android.app.admin.DevicePolicyCache;
 import android.app.compat.CompatChanges;
@@ -1441,6 +1442,14 @@
         }
     }
 
+    @Override
+    public Rect getBounds() {
+        // The window bounds are used for layout in screen coordinates. If the token has bounds for
+        // size compatibility mode, its configuration bounds are app based coordinates which should
+        // not be used for layout.
+        return mToken.hasSizeCompatBounds() ? mToken.getBounds() : super.getBounds();
+    }
+
     /** Retrieves the current frame of the window that the application sees. */
     Rect getFrame() {
         return mWindowFrames.mFrame;
@@ -4728,7 +4737,7 @@
         final int drawState = mWinAnimator.mDrawState;
         if ((drawState == HAS_DRAWN || drawState == READY_TO_SHOW) && mActivityRecord != null) {
             if (mAttrs.type != TYPE_APPLICATION_STARTING) {
-                mActivityRecord.onFirstWindowDrawn(this, mWinAnimator);
+                mActivityRecord.onFirstWindowDrawn(this);
             } else {
                 mActivityRecord.onStartingWindowDrawn();
             }
@@ -4816,6 +4825,7 @@
         windowInfo.focused = isFocused();
         Task task = getTask();
         windowInfo.inPictureInPicture = (task != null) && task.inPinnedWindowingMode();
+        windowInfo.taskId = task == null ? ActivityTaskManager.INVALID_TASK_ID : task.mTaskId;
         windowInfo.hasFlagWatchOutsideTouch =
                 (mAttrs.flags & WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH) != 0;
 
@@ -5964,7 +5974,7 @@
         // since a generic WindowContainer only needs to wait for its
         // children to finish and is immediately ready from its own
         // perspective but at the WindowState level we need to wait for ourselves
-        // to draw even if the children draw first our don't need to sync, so we start
+        // to draw even if the children draw first or don't need to sync, so we start
         // in WAITING state rather than READY.
         mSyncState = SYNC_STATE_WAITING_FOR_DRAW;
         requestRedrawForSync();
@@ -5975,6 +5985,16 @@
         return true;
     }
 
+    @Override
+    boolean isSyncFinished() {
+        if (mSyncState == SYNC_STATE_WAITING_FOR_DRAW && mViewVisibility == View.GONE) {
+            // Don't wait for GONE windows. However, we don't alter the state in case the window
+            // becomes un-gone while the syncset is still active.
+            return true;
+        }
+        return super.isSyncFinished();
+    }
+
     boolean finishDrawing(SurfaceControl.Transaction postDrawTransaction) {
         if (mOrientationChangeRedrawRequestTime > 0) {
             final long duration =
@@ -6043,8 +6063,11 @@
     }
 
     boolean hasWallpaper() {
-        return (mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0
-                || (mActivityRecord != null && mActivityRecord.hasWallpaperBackgroudForLetterbox());
+        return (mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0 || hasWallpaperForLetterboxBackground();
+    }
+
+    boolean hasWallpaperForLetterboxBackground() {
+        return mActivityRecord != null && mActivityRecord.hasWallpaperBackgroudForLetterbox();
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 8094196..f25706a 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -41,7 +41,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
 import static com.android.server.wm.WindowManagerService.logWithStack;
 import static com.android.server.wm.WindowStateAnimatorProto.DRAW_STATE;
 import static com.android.server.wm.WindowStateAnimatorProto.SURFACE;
@@ -71,7 +70,6 @@
  **/
 class WindowStateAnimator {
     static final String TAG = TAG_WITH_CLASS_NAME ? "WindowStateAnimator" : TAG_WM;
-    static final int WINDOW_FREEZE_LAYER = TYPE_LAYER_MULTIPLIER * 200;
     static final int PRESERVED_SURFACE_LAYER = 1;
 
     /**
diff --git a/services/core/xsd/Android.bp b/services/core/xsd/Android.bp
index d43cf3f..6a50d38 100644
--- a/services/core/xsd/Android.bp
+++ b/services/core/xsd/Android.bp
@@ -14,7 +14,6 @@
     package_name: "com.android.server.pm.permission.configfile",
 }
 
-
 xsd_config {
     name: "platform-compat-config",
     srcs: ["platform-compat/config/platform-compat-config.xsd"],
@@ -42,6 +41,7 @@
     srcs: ["display-layout-config/display-layout-config.xsd"],
     api_dir: "display-layout-config/schema",
     package_name: "com.android.server.display.config.layout",
+    boolean_getter: true,
 }
 
 xsd_config {
diff --git a/services/core/xsd/device-state-config/device-state-config.xsd b/services/core/xsd/device-state-config/device-state-config.xsd
index 94a398f..86f4176 100644
--- a/services/core/xsd/device-state-config/device-state-config.xsd
+++ b/services/core/xsd/device-state-config/device-state-config.xsd
@@ -40,10 +40,19 @@
             <xs:element name="name" type="xs:string" minOccurs="0">
                 <xs:annotation name="nullable" />
             </xs:element>
+            <xs:element name="flags" type="flags" />
             <xs:element name="conditions" type="conditions" />
         </xs:sequence>
     </xs:complexType>
 
+    <xs:complexType name="flags">
+        <xs:sequence>
+            <xs:element name="flag" type="xs:string" minOccurs="0" maxOccurs="unbounded">
+                <xs:annotation name="nullable" />
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+
     <xs:complexType name="conditions">
         <xs:sequence>
             <xs:element name="lid-switch" type="lidSwitchCondition" minOccurs="0">
diff --git a/services/core/xsd/device-state-config/schema/current.txt b/services/core/xsd/device-state-config/schema/current.txt
index 08fccf8..a98d4e5 100644
--- a/services/core/xsd/device-state-config/schema/current.txt
+++ b/services/core/xsd/device-state-config/schema/current.txt
@@ -11,9 +11,11 @@
   public class DeviceState {
     ctor public DeviceState();
     method public com.android.server.policy.devicestate.config.Conditions getConditions();
+    method public com.android.server.policy.devicestate.config.Flags getFlags();
     method public java.math.BigInteger getIdentifier();
     method @Nullable public String getName();
     method public void setConditions(com.android.server.policy.devicestate.config.Conditions);
+    method public void setFlags(com.android.server.policy.devicestate.config.Flags);
     method public void setIdentifier(java.math.BigInteger);
     method public void setName(@Nullable String);
   }
@@ -23,6 +25,11 @@
     method public java.util.List<com.android.server.policy.devicestate.config.DeviceState> getDeviceState();
   }
 
+  public class Flags {
+    ctor public Flags();
+    method @Nullable public java.util.List<java.lang.String> getFlag();
+  }
+
   public class LidSwitchCondition {
     ctor public LidSwitchCondition();
     method public boolean getOpen();
diff --git a/services/core/xsd/display-layout-config/display-layout-config.xsd b/services/core/xsd/display-layout-config/display-layout-config.xsd
index c542c0d..e14139a 100644
--- a/services/core/xsd/display-layout-config/display-layout-config.xsd
+++ b/services/core/xsd/display-layout-config/display-layout-config.xsd
@@ -52,6 +52,6 @@
             <xs:element name="address" type="xs:nonNegativeInteger"/>
         </xs:sequence>
         <xs:attribute name="enabled" type="xs:boolean" use="optional" />
-        <xs:attribute name="isDefault" type="xs:boolean" use="optional" />
+        <xs:attribute name="defaultDisplay" type="xs:boolean" use="optional" />
     </xs:complexType>
 </xs:schema>
diff --git a/services/core/xsd/display-layout-config/schema/current.txt b/services/core/xsd/display-layout-config/schema/current.txt
index 8171885..f391575 100644
--- a/services/core/xsd/display-layout-config/schema/current.txt
+++ b/services/core/xsd/display-layout-config/schema/current.txt
@@ -4,11 +4,11 @@
   public class Display {
     ctor public Display();
     method public java.math.BigInteger getAddress();
-    method public boolean getEnabled();
-    method public boolean getIsDefault();
+    method public boolean isDefaultDisplay();
+    method public boolean isEnabled();
     method public void setAddress(java.math.BigInteger);
+    method public void setDefaultDisplay(boolean);
     method public void setEnabled(boolean);
-    method public void setIsDefault(boolean);
   }
 
   public class Layout {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 193d92a..70219d2 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -327,7 +327,6 @@
 import com.android.server.SystemServerInitThreadPool;
 import com.android.server.SystemService;
 import com.android.server.devicepolicy.ActiveAdmin.TrustAgentInfo;
-import com.android.server.devicepolicy.Owners.OwnerDto;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.net.NetworkPolicyManagerInternal;
 import com.android.server.pm.RestrictionsSet;
@@ -1259,17 +1258,37 @@
     }
 
     // Used by DevicePolicyManagerServiceShellCommand
-    List<OwnerDto> listAllOwners() {
+    List<OwnerShellData> listAllOwners() {
         Preconditions.checkCallAuthorization(
                 hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
         return mInjector.binderWithCleanCallingIdentity(() -> {
-            List<OwnerDto> owners = mOwners.listAllOwners();
+            SparseArray<DevicePolicyData> userData;
+
+            // Gets the owners of "full users" first (device owner and profile owners)
+            List<OwnerShellData> owners = mOwners.listAllOwners();
             synchronized (getLockObject()) {
                 for (int i = 0; i < owners.size(); i++) {
-                    OwnerDto owner = owners.get(i);
+                    OwnerShellData owner = owners.get(i);
                     owner.isAffiliated = isUserAffiliatedWithDeviceLocked(owner.userId);
                 }
+                userData = mUserData;
             }
+
+            // Then the owners of profile users (managed profiles)
+            for (int i = 0; i < userData.size(); i++) {
+                DevicePolicyData policyData = mUserData.valueAt(i);
+                int userId = userData.keyAt(i);
+                int parentUserId = mUserManagerInternal.getProfileParentId(userId);
+                boolean isProfile = parentUserId != userId;
+                if (!isProfile) continue;
+                for (int j = 0; j < policyData.mAdminList.size(); j++) {
+                    ActiveAdmin admin = policyData.mAdminList.get(j);
+                    OwnerShellData owner = OwnerShellData.forManagedProfileOwner(userId,
+                            parentUserId, admin.info.getComponent());
+                    owners.add(owner);
+                }
+            }
+
             return owners;
         });
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
index a2db6aac..85fe65c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
@@ -22,8 +22,6 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 
-import com.android.server.devicepolicy.Owners.OwnerDto;
-
 import java.io.PrintWriter;
 import java.util.Collection;
 import java.util.List;
@@ -205,12 +203,12 @@
     }
 
     private int runListOwners(PrintWriter pw) {
-        List<OwnerDto> owners = mService.listAllOwners();
+        List<OwnerShellData> owners = mService.listAllOwners();
         int size = printAndGetSize(pw, owners, "owner");
         if (size == 0) return 0;
 
         for (int i = 0; i < size; i++) {
-            OwnerDto owner = owners.get(i);
+            OwnerShellData owner = owners.get(i);
             pw.printf("User %2d: admin=%s", owner.userId, owner.admin.flattenToShortString());
             if (owner.isDeviceOwner) {
                 pw.print(",DeviceOwner");
@@ -218,6 +216,9 @@
             if (owner.isProfileOwner) {
                 pw.print(",ProfileOwner");
             }
+            if (owner.isManagedProfileOwner) {
+                pw.printf(",ManagedProfileOwner(parentUserId=%d)", owner.parentUserId);
+            }
             if (owner.isAffiliated) {
                 pw.print(",Affiliated");
             }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OwnerShellData.java b/services/devicepolicy/java/com/android/server/devicepolicy/OwnerShellData.java
new file mode 100644
index 0000000..b98c3dc
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OwnerShellData.java
@@ -0,0 +1,98 @@
+/*
+ * 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.devicepolicy;
+
+import static android.os.UserHandle.USER_NULL;
+
+import android.annotation.UserIdInt;
+import android.content.ComponentName;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * Data-transfer object used by {@link DevicePolicyManagerServiceShellCommand}.
+ */
+final class OwnerShellData {
+
+    public final @UserIdInt int userId;
+    public final @UserIdInt int parentUserId;
+    public final ComponentName admin;
+    public final boolean isDeviceOwner;
+    public final boolean isProfileOwner;
+    public final boolean isManagedProfileOwner;
+    public boolean isAffiliated;
+
+    // NOTE: class is too simple to require a Builder (not to mention isAffiliated is mutable)
+    private OwnerShellData(@UserIdInt int userId, @UserIdInt int parentUserId, ComponentName admin,
+            boolean isDeviceOwner, boolean isProfileOwner, boolean isManagedProfileOwner) {
+        Preconditions.checkArgument(userId != USER_NULL, "userId cannot be USER_NULL");
+        this.userId = userId;
+        this.parentUserId = parentUserId;
+        this.admin = Objects.requireNonNull(admin, "admin must not be null");
+        this.isDeviceOwner = isDeviceOwner;
+        this.isProfileOwner = isProfileOwner;
+        this.isManagedProfileOwner = isManagedProfileOwner;
+        if (isManagedProfileOwner) {
+            Preconditions.checkArgument(parentUserId != USER_NULL,
+                    "parentUserId cannot be USER_NULL for managed profile owner");
+            Preconditions.checkArgument(parentUserId != userId,
+                    "cannot be parent of itself (%d)", userId);
+        }
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder(getClass().getSimpleName())
+                .append("[userId=").append(userId)
+                .append(",admin=").append(admin.flattenToShortString());
+        if (isDeviceOwner) {
+            sb.append(",deviceOwner");
+        }
+        if (isProfileOwner) {
+            sb.append(",isProfileOwner");
+        }
+        if (isManagedProfileOwner) {
+            sb.append(",isManagedProfileOwner");
+        }
+        if (parentUserId != USER_NULL) {
+            sb.append(",parentUserId=").append(parentUserId);
+        }
+        if (isAffiliated) {
+            sb.append(",isAffiliated");
+        }
+        return sb.append(']').toString();
+    }
+
+    static OwnerShellData forDeviceOwner(@UserIdInt int userId, ComponentName admin) {
+        return new OwnerShellData(userId, /* parentUserId= */ USER_NULL, admin,
+                /* isDeviceOwner= */ true, /* isProfileOwner= */ false,
+                /* isManagedProfileOwner= */ false);
+    }
+
+    static OwnerShellData forUserProfileOwner(@UserIdInt int userId, ComponentName admin) {
+        return new OwnerShellData(userId, /* parentUserId= */ USER_NULL, admin,
+                /* isDeviceOwner= */ false, /* isProfileOwner= */ true,
+                /* isManagedProfileOwner= */ false);
+    }
+
+    static OwnerShellData forManagedProfileOwner(@UserIdInt int userId, @UserIdInt int parentUserId,
+            ComponentName admin) {
+        return new OwnerShellData(userId, parentUserId, admin, /* isDeviceOwner= */ false,
+                /* isProfileOwner= */ false, /* isManagedProfileOwner= */ true);
+    }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index fd09e3f..3584728 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -19,7 +19,6 @@
 import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
 
 import android.annotation.Nullable;
-import android.annotation.UserIdInt;
 import android.app.ActivityManagerInternal;
 import android.app.AppOpsManagerInternal;
 import android.app.admin.DevicePolicyManager.DeviceOwnerType;
@@ -476,17 +475,16 @@
         }
     }
 
-    List<OwnerDto> listAllOwners() {
-        List<OwnerDto> owners = new ArrayList<>();
+    List<OwnerShellData> listAllOwners() {
+        List<OwnerShellData> owners = new ArrayList<>();
         synchronized (mLock) {
             if (mDeviceOwner != null) {
-                owners.add(new OwnerDto(mDeviceOwnerUserId, mDeviceOwner.admin,
-                        /* isDeviceOwner= */ true));
+                owners.add(OwnerShellData.forDeviceOwner(mDeviceOwnerUserId, mDeviceOwner.admin));
             }
             for (int i = 0; i < mProfileOwners.size(); i++) {
                 int userId = mProfileOwners.keyAt(i);
                 OwnerInfo info = mProfileOwners.valueAt(i);
-                owners.add(new OwnerDto(userId, info.admin, /* isDeviceOwner= */ false));
+                owners.add(OwnerShellData.forUserProfileOwner(userId, info.admin));
             }
         }
         return owners;
@@ -1236,24 +1234,6 @@
         }
     }
 
-    /**
-     * Data-transfer object used by {@link DevicePolicyManagerServiceShellCommand}.
-     */
-    static final class OwnerDto {
-        public final @UserIdInt int userId;
-        public final ComponentName admin;
-        public final boolean isDeviceOwner;
-        public final boolean isProfileOwner;
-        public boolean isAffiliated;
-
-        private OwnerDto(@UserIdInt int userId, ComponentName admin, boolean isDeviceOwner) {
-            this.userId = userId;
-            this.admin = Objects.requireNonNull(admin, "admin must not be null");
-            this.isDeviceOwner = isDeviceOwner;
-            this.isProfileOwner = !isDeviceOwner;
-        }
-    }
-
     public void dump(IndentingPrintWriter pw) {
         boolean needBlank = false;
         if (mDeviceOwner != null) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 81995a7..4897ca0 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1586,6 +1586,9 @@
             // all listeners have the chance to react with special handling.
             Settings.Global.putInt(context.getContentResolver(),
                     Settings.Global.AIRPLANE_MODE_ON, 1);
+        } else if (context.getResources().getBoolean(R.bool.config_autoResetAirplaneMode)) {
+            Settings.Global.putInt(context.getContentResolver(),
+                    Settings.Global.AIRPLANE_MODE_ON, 0);
         }
 
         StatusBarManagerService statusBar = null;
diff --git a/services/net/Android.bp b/services/net/Android.bp
index a822257..53ce6b2 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -52,7 +52,6 @@
         // classes generated by netd_aidl_interfaces-platform-java above.
         "netd_aidl_interface-V3-java",
         "networkstack-client",
-        "modules-utils-build_system",
     ],
     apex_available: [
         "com.android.wifi",
diff --git a/services/tests/PackageManager/packageinstaller/Android.bp b/services/tests/PackageManager/packageinstaller/Android.bp
new file mode 100644
index 0000000..35d754b
--- /dev/null
+++ b/services/tests/PackageManager/packageinstaller/Android.bp
@@ -0,0 +1,39 @@
+// 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 {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+    name: "PackageInstallerTests",
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+    static_libs: [
+        "androidx.test.rules",
+        "androidx.test.runner",
+        "junit",
+        "kotlin-test",
+        "truth-prebuilt",
+    ],
+    platform_apis: true,
+    test_suites: ["device-tests"],
+}
diff --git a/services/tests/PackageManager/packageinstaller/AndroidManifest.xml b/services/tests/PackageManager/packageinstaller/AndroidManifest.xml
new file mode 100644
index 0000000..d706258
--- /dev/null
+++ b/services/tests/PackageManager/packageinstaller/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.packageinstaller.test">
+
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.packageinstaller.test"
+        />
+
+</manifest>
+
diff --git a/services/tests/PackageManager/packageinstaller/AndroidTest.xml b/services/tests/PackageManager/packageinstaller/AndroidTest.xml
new file mode 100644
index 0000000..c39285ff
--- /dev/null
+++ b/services/tests/PackageManager/packageinstaller/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?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.
+  -->
+
+<configuration description="Test module config for PackageInstallerTests">
+    <option name="test-tag" value="PackageInstallerTests" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="PackageInstallerTests.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="com.android.packageinstaller.test" />
+    </test>
+</configuration>
diff --git a/services/tests/PackageManager/packageinstaller/src/com/android/packageinstaller/test/ExportedComponentTest.kt b/services/tests/PackageManager/packageinstaller/src/com/android/packageinstaller/test/ExportedComponentTest.kt
new file mode 100644
index 0000000..d7d2726
--- /dev/null
+++ b/services/tests/PackageManager/packageinstaller/src/com/android/packageinstaller/test/ExportedComponentTest.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.packageinstaller.test
+
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.net.Uri
+import androidx.test.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Test
+
+class ExportedComponentTest {
+
+    private val context: Context = InstrumentationRegistry.getContext()
+
+    @Test
+    fun verifyNoExportedReceivers() {
+        val intent = Intent(Intent.ACTION_INSTALL_PACKAGE).apply {
+            data = Uri.parse("content://mockForTest")
+        }
+        val packageInstallers = context.packageManager.queryIntentActivities(intent,
+            PackageManager.MATCH_DEFAULT_ONLY or PackageManager.MATCH_DISABLED_COMPONENTS)
+            .map { it.activityInfo.packageName }
+            .distinct()
+            .map { context.packageManager.getPackageInfo(it, PackageManager.GET_RECEIVERS) }
+
+        assertThat(packageInstallers).isNotEmpty()
+
+        packageInstallers.forEach {
+            val exported = it.receivers.filter { it.exported }
+            assertWithMessage("Receivers should not be exported").that(exported).isEmpty()
+        }
+    }
+}
diff --git a/services/tests/mockingservicestests/AndroidManifest.xml b/services/tests/mockingservicestests/AndroidManifest.xml
index 17a5dcc..3cab5ec 100644
--- a/services/tests/mockingservicestests/AndroidManifest.xml
+++ b/services/tests/mockingservicestests/AndroidManifest.xml
@@ -28,6 +28,7 @@
     <uses-permission android:name="android.permission.MANAGE_APPOPS"/>
     <uses-permission android:name="android.permission.MONITOR_DEVICE_CONFIG_ACCESS"/>
     <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG"/>
+    <uses-permission android:name="android.permission.WAKE_LOCK"/>
     <uses-permission
         android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"/>
 
diff --git a/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file6.xml b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file6.xml
new file mode 100644
index 0000000..b3c14228
--- /dev/null
+++ b/services/tests/mockingservicestests/assets/SensorPrivacyServiceMockingTest/persisted_file6.xml
@@ -0,0 +1,3 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
+<sensor-privacy enabled="false">
+</sensor-privacy>
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index acf50b45..e472b06 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -323,6 +323,7 @@
         when(mPowerManager.newWakeLock(anyInt(), anyString())).thenReturn(mWakeLock);
         doNothing().when(mWakeLock).acquire();
         doNothing().when(mAlarmManager).set(anyInt(), anyLong(), anyString(), any(), any());
+        doNothing().when(mAlarmManager).setExact(anyInt(), anyLong(), anyString(), any(), any());
         doNothing().when(mAlarmManager)
                 .setWindow(anyInt(), anyLong(), anyLong(), anyString(), any(), any());
         doReturn(mock(Sensor.class)).when(mSensorManager)
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
index 20a5842..a883293 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -173,8 +173,6 @@
                 CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB);
         assertThat(mCachedAppOptimizerUnderTest.mFullDeltaRssThrottleKb).isEqualTo(
                 CachedAppOptimizer.DEFAULT_COMPACT_FULL_DELTA_RSS_THROTTLE_KB);
-        assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isEqualTo(
-                CachedAppOptimizer.DEFAULT_USE_FREEZER);
         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMinOomAdj).isEqualTo(
                 CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MIN_OOM_ADJ);
         assertThat(mCachedAppOptimizerUnderTest.mCompactThrottleMaxOomAdj).isEqualTo(
@@ -188,6 +186,10 @@
         }
         assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle)
                 .containsExactlyElementsIn(expected);
+
+        Assume.assumeTrue(mCachedAppOptimizerUnderTest.isFreezerSupported());
+        assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isEqualTo(
+                CachedAppOptimizer.DEFAULT_USE_FREEZER);
     }
 
     @Test
@@ -244,9 +246,8 @@
                     CachedAppOptimizer.DEFAULT_COMPACT_THROTTLE_MAX_OOM_ADJ - 10), false);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 CachedAppOptimizer.KEY_COMPACT_PROC_STATE_THROTTLE, "1,2,3", false);
-        assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isEqualTo(
-                CachedAppOptimizer.DEFAULT_USE_FREEZER);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+        assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isFalse();
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
                 CachedAppOptimizer.KEY_USE_FREEZER, CachedAppOptimizer.DEFAULT_USE_FREEZER
                         ? "false" : "true", false);
 
@@ -291,7 +292,8 @@
                 CachedAppOptimizer.DEFAULT_COMPACT_FULL_RSS_THROTTLE_KB + 1);
         assertThat(mCachedAppOptimizerUnderTest.mProcStateThrottle).containsExactly(1, 2, 3);
 
-        if (mCachedAppOptimizerUnderTest.isFreezerSupported()) {
+        Assume.assumeTrue(CachedAppOptimizer.isFreezerSupported());
+        if (CachedAppOptimizer.isFreezerSupported()) {
             if (CachedAppOptimizer.DEFAULT_USE_FREEZER) {
                 assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isFalse();
             } else {
@@ -325,15 +327,15 @@
 
     @Test
     public void useFreeze_doesNotListenToDeviceConfigChanges() throws InterruptedException {
-        Assume.assumeTrue(mCachedAppOptimizerUnderTest.isFreezerSupported());
+        Assume.assumeTrue(CachedAppOptimizer.isFreezerSupported());
 
-        assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isEqualTo(
-                CachedAppOptimizer.DEFAULT_USE_FREEZER);
+        assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isFalse();
 
         // The freezer DeviceConfig property is read at boot only
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
                 CachedAppOptimizer.KEY_USE_FREEZER, "true", false);
         mCachedAppOptimizerUnderTest.init();
+        assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isTrue();
         mCountDown = new CountDownLatch(1);
 
         // No notifications should get to the cached app optimizer.
@@ -346,14 +348,13 @@
 
         // Set the flag the other way without rebooting. It shall not change.
         mCountDown = new CountDownLatch(1);
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
                 CachedAppOptimizer.KEY_USE_FREEZER, "false", false);
         assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
         assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isTrue();
 
-
         // Now, set the flag to false and restart the cached app optimizer
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
                 CachedAppOptimizer.KEY_USE_FREEZER, "false", false);
         mCachedAppOptimizerUnderTest.init();
 
@@ -380,18 +381,17 @@
 
     @Test
     public void useFreeze_listensToDeviceConfigChangesBadValues() throws InterruptedException {
-        assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isEqualTo(
-                CachedAppOptimizer.DEFAULT_USE_FREEZER);
+        Assume.assumeTrue(CachedAppOptimizer.isFreezerSupported());
+        assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isFalse();
 
         // When we push an invalid flag value...
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
                 CachedAppOptimizer.KEY_USE_FREEZER, "foobar", false);
 
         mCachedAppOptimizerUnderTest.init();
 
-        // Then we set the default.
-        assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isEqualTo(
-                CachedAppOptimizer.DEFAULT_USE_FREEZER);
+        // DeviceConfig treats invalid value as false
+        assertThat(mCachedAppOptimizerUnderTest.useFreezer()).isFalse();
     }
 
     @Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java b/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java
index 589a349..457c8db 100644
--- a/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java
@@ -48,7 +48,7 @@
 import org.mockito.quality.Strictness;
 
 /**
- * Run it as {@code atest FrameworksMockingCoreTests:FactoryResetterTest}
+ * Run it as {@code atest FrameworksMockingServicesTests:FactoryResetterTest}
  */
 @Presubmit
 public final class FactoryResetterTest {
diff --git a/services/tests/mockingservicestests/src/com/android/server/devicepolicy/OwnerShellDataTest.java b/services/tests/mockingservicestests/src/com/android/server/devicepolicy/OwnerShellDataTest.java
new file mode 100644
index 0000000..dd67d72
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/devicepolicy/OwnerShellDataTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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.devicepolicy;
+
+import static android.os.UserHandle.USER_NULL;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.testng.Assert.expectThrows;
+
+import android.content.ComponentName;
+
+import org.junit.Test;
+
+/**
+ * Run it as {@code atest FrameworksMockingServicesTests:OwnerShellDataTest}
+ */
+public final class OwnerShellDataTest {
+
+    private static final int USER_ID = 007;
+    private static final int PARENT_USER_ID = 'M' + 'I' + 6;
+    private static final ComponentName ADMIN = new ComponentName("Bond", "James");
+
+    @Test
+    public void testForDeviceOwner_noAdmin() {
+        expectThrows(NullPointerException.class,
+                () -> OwnerShellData.forDeviceOwner(USER_ID, /* admin= */ null));
+    }
+
+    @Test
+    public void testForDeviceOwner_invalidUser() {
+        expectThrows(IllegalArgumentException.class,
+                () -> OwnerShellData.forDeviceOwner(USER_NULL, ADMIN));
+    }
+
+    @Test
+    public void testForDeviceOwner() {
+        OwnerShellData dto = OwnerShellData.forDeviceOwner(USER_ID, ADMIN);
+
+        assertWithMessage("dto(%s).userId", dto).that(dto.userId).isEqualTo(USER_ID);
+        assertWithMessage("dto(%s).parentUserId", dto).that(dto.parentUserId)
+                .isEqualTo(USER_NULL);
+        assertWithMessage("dto(%s).admin", dto).that(dto.admin).isSameInstanceAs(ADMIN);
+        assertWithMessage("dto(%s).isDeviceOwner", dto).that(dto.isDeviceOwner).isTrue();
+        assertWithMessage("dto(%s).isProfileOwner", dto).that(dto.isProfileOwner).isFalse();
+        assertWithMessage("dto(%s).isManagedProfileOwner", dto).that(dto.isManagedProfileOwner)
+                .isFalse();
+        assertWithMessage("dto(%s).isAffiliated", dto).that(dto.isAffiliated).isFalse();
+    }
+
+    @Test
+    public void testForUserProfileOwner_noAdmin() {
+        expectThrows(NullPointerException.class,
+                () -> OwnerShellData.forUserProfileOwner(USER_ID, /* admin= */ null));
+    }
+
+    @Test
+    public void testForUserProfileOwner_invalidUser() {
+        expectThrows(IllegalArgumentException.class,
+                () -> OwnerShellData.forUserProfileOwner(USER_NULL, ADMIN));
+    }
+
+    @Test
+    public void testForUserProfileOwner() {
+        OwnerShellData dto = OwnerShellData.forUserProfileOwner(USER_ID, ADMIN);
+
+        assertWithMessage("dto(%s).userId", dto).that(dto.userId).isEqualTo(USER_ID);
+        assertWithMessage("dto(%s).parentUserId", dto).that(dto.parentUserId)
+                .isEqualTo(USER_NULL);
+        assertWithMessage("dto(%s).admin", dto).that(dto.admin).isSameInstanceAs(ADMIN);
+        assertWithMessage("dto(%s).isDeviceOwner", dto).that(dto.isDeviceOwner).isFalse();
+        assertWithMessage("dto(%s).isProfileOwner", dto).that(dto.isProfileOwner).isTrue();
+        assertWithMessage("dto(%s).isManagedProfileOwner", dto).that(dto.isManagedProfileOwner)
+                .isFalse();
+        assertWithMessage("dto(%s).isAffiliated", dto).that(dto.isAffiliated).isFalse();
+    }
+
+    @Test
+    public void testForManagedProfileOwner_noAdmin() {
+        expectThrows(NullPointerException.class,
+                () -> OwnerShellData.forManagedProfileOwner(USER_ID, PARENT_USER_ID, null));
+    }
+
+    @Test
+    public void testForManagedProfileOwner_invalidUser() {
+        expectThrows(IllegalArgumentException.class,
+                () -> OwnerShellData.forManagedProfileOwner(USER_NULL, PARENT_USER_ID, ADMIN));
+    }
+
+    @Test
+    public void testForManagedProfileOwner_invalidParent() {
+        expectThrows(IllegalArgumentException.class,
+                () -> OwnerShellData.forManagedProfileOwner(USER_ID, USER_NULL, ADMIN));
+    }
+
+    @Test
+    public void testForManagedProfileOwner_parentOfItself() {
+        expectThrows(IllegalArgumentException.class,
+                () -> OwnerShellData.forManagedProfileOwner(USER_ID, USER_ID, ADMIN));
+    }
+
+    @Test
+    public void testForManagedProfileOwner() {
+        OwnerShellData dto = OwnerShellData.forManagedProfileOwner(USER_ID, PARENT_USER_ID, ADMIN);
+
+        assertWithMessage("dto(%s).userId", dto).that(dto.userId).isEqualTo(USER_ID);
+        assertWithMessage("dto(%s).parentUserId", dto).that(dto.parentUserId)
+                .isEqualTo(PARENT_USER_ID);
+        assertWithMessage("dto(%s).admin", dto).that(dto.admin).isSameInstanceAs(ADMIN);
+        assertWithMessage("dto(%s).isDeviceOwner", dto).that(dto.isDeviceOwner).isFalse();
+        assertWithMessage("dto(%s).isProfileOwner", dto).that(dto.isProfileOwner).isFalse();
+        assertWithMessage("dto(%s).isManagedProfileOwner", dto).that(dto.isManagedProfileOwner)
+                .isTrue();
+        assertWithMessage("dto(%s).isAffiliated", dto).that(dto.isAffiliated).isFalse();
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 0efcc57..34856e2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -134,6 +134,14 @@
         when(mMockedResources.getFloat(com.android.internal.R.dimen
                 .config_screenBrightnessSettingMaximumFloat))
                 .thenReturn(BACKLIGHT_RANGE_ZERO_TO_ONE[1]);
+        when(mMockedResources.getStringArray(R.array.config_displayUniqueIdArray))
+                .thenReturn(new String[]{});
+        TypedArray mockArray = mock(TypedArray.class);
+        when(mockArray.length()).thenReturn(0);
+        when(mMockedResources.obtainTypedArray(R.array.config_maskBuiltInDisplayCutoutArray))
+                .thenReturn(mockArray);
+        when(mMockedResources.obtainTypedArray(R.array.config_waterfallCutoutArray))
+                .thenReturn(mockArray);
     }
 
     @After
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index b2471fa..56bcb83 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -885,6 +885,7 @@
         //   * E jobs test that expedited jobs don't skip the line when the app has no regular jobs
         //   * F jobs test correct expedited/regular ordering doesn't push jobs too high in list
         //   * G jobs test correct ordering for regular jobs
+        //   * H job tests correct behavior when enqueue times are the same
         JobStatus rA1 = createJobStatus("testPendingJobSorting", createJobInfo(1), 1);
         JobStatus rB2 = createJobStatus("testPendingJobSorting", createJobInfo(2), 2);
         JobStatus eC3 = createJobStatus("testPendingJobSorting",
@@ -896,6 +897,7 @@
                 createJobInfo(6).setExpedited(true), 2);
         JobStatus eA7 = createJobStatus("testPendingJobSorting",
                 createJobInfo(7).setExpedited(true), 1);
+        JobStatus rH8 = createJobStatus("testPendingJobSorting", createJobInfo(8), 14);
         JobStatus rF8 = createJobStatus("testPendingJobSorting", createJobInfo(8), 6);
         JobStatus eF9 = createJobStatus("testPendingJobSorting",
                 createJobInfo(9).setExpedited(true), 6);
@@ -915,6 +917,7 @@
         eB6.enqueueTime = 6;
         eA7.enqueueTime = 7;
         rF8.enqueueTime = 8;
+        rH8.enqueueTime = 8;
         eF9.enqueueTime = 9;
         rC10.enqueueTime = 10;
         eC11.enqueueTime = 11;
@@ -931,6 +934,7 @@
         mService.mPendingJobs.add(rD4);
         mService.mPendingJobs.add(eA7);
         mService.mPendingJobs.add(rG12);
+        mService.mPendingJobs.add(rH8);
         mService.mPendingJobs.add(rF8);
         mService.mPendingJobs.add(eB6);
         mService.mPendingJobs.add(eE14);
@@ -943,7 +947,7 @@
         mService.mPendingJobs.sort(mService.mPendingJobComparator);
 
         final JobStatus[] expectedOrder = new JobStatus[]{
-                eA7, rA1, eB6, rB2, eC3, rD4, eE5, eF9, rF8, eC11, rC10, rG12, rG13, eE14};
+                eA7, rA1, eB6, rB2, eC3, rD4, eE5, eF9, rH8, rF8, eC11, rC10, rG12, rG13, eE14};
         for (int i = 0; i < expectedOrder.length; ++i) {
             assertEquals("List wasn't correctly sorted @ index " + i,
                     expectedOrder[i].getJobId(), mService.mPendingJobs.get(i).getJobId());
@@ -995,7 +999,7 @@
         for (int i = 0; i < 2500; ++i) {
             JobStatus job = createJobStatus("testPendingJobSorting_Random",
                     createJobInfo(i).setExpedited(random.nextBoolean()), random.nextInt(250));
-            job.enqueueTime = Math.abs(random.nextInt(1_000_000));
+            job.enqueueTime = random.nextInt(1_000_000);
             mService.mPendingJobs.add(job);
 
             mService.mPendingJobComparator.refreshLocked();
@@ -1023,18 +1027,48 @@
 
     @Test
     public void testPendingJobSortingTransitivity() {
-        Random random = new Random(1); // Always use the same series of pseudo random values.
+        // Always use the same series of pseudo random values.
+        for (int seed : new int[]{1337, 7357, 606, 6357, 41106010, 3, 2, 1}) {
+            Random random = new Random(seed);
 
-        mService.mPendingJobs.clear();
+            mService.mPendingJobs.clear();
 
-        for (int i = 0; i < 250; ++i) {
-            JobStatus job = createJobStatus("testPendingJobSortingTransitivity",
-                    createJobInfo(i).setExpedited(random.nextBoolean()), random.nextInt(50));
-            job.enqueueTime = Math.abs(random.nextInt(1_000_000));
-            job.overrideState = random.nextInt(4);
-            mService.mPendingJobs.add(job);
+            for (int i = 0; i < 300; ++i) {
+                JobStatus job = createJobStatus("testPendingJobSortingTransitivity",
+                        createJobInfo(i).setExpedited(random.nextBoolean()), random.nextInt(50));
+                job.enqueueTime = random.nextInt(1_000_000);
+                job.overrideState = random.nextInt(4);
+                mService.mPendingJobs.add(job);
+            }
+
+            verifyPendingJobComparatorTransitivity();
         }
+    }
 
+    @Test
+    public void testPendingJobSortingTransitivity_Concentrated() {
+        // Always use the same series of pseudo random values.
+        for (int seed : new int[]{1337, 6000, 637739, 6357, 1, 7, 13}) {
+            Random random = new Random(seed);
+
+            mService.mPendingJobs.clear();
+
+            for (int i = 0; i < 300; ++i) {
+                JobStatus job = createJobStatus("testPendingJobSortingTransitivity_Concentrated",
+                        createJobInfo(i).setExpedited(random.nextFloat() < .03),
+                        random.nextInt(20));
+                job.enqueueTime = random.nextInt(250);
+                job.overrideState = random.nextFloat() < .01
+                        ? JobStatus.OVERRIDE_SORTING : JobStatus.OVERRIDE_NONE;
+                mService.mPendingJobs.add(job);
+                Log.d(TAG, sortedJobToString(job));
+            }
+
+            verifyPendingJobComparatorTransitivity();
+        }
+    }
+
+    private void verifyPendingJobComparatorTransitivity() {
         mService.mPendingJobComparator.refreshLocked();
 
         for (int i = 0; i < mService.mPendingJobs.size(); ++i) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java b/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java
new file mode 100644
index 0000000..f94377f
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/power/ScreenUndimDetectorTest.java
@@ -0,0 +1,305 @@
+/*
+ * 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 com.android.server.power;
+
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DIM;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_VR;
+import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE;
+
+import static com.android.server.power.ScreenUndimDetector.DEFAULT_MAX_DURATION_BETWEEN_UNDIMS_MILLIS;
+import static com.android.server.power.ScreenUndimDetector.KEY_KEEP_SCREEN_ON_ENABLED;
+import static com.android.server.power.ScreenUndimDetector.KEY_MAX_DURATION_BETWEEN_UNDIMS_MILLIS;
+import static com.android.server.power.ScreenUndimDetector.KEY_UNDIMS_REQUIRED;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.SystemClock;
+import android.provider.DeviceConfig;
+import android.testing.TestableContext;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.testables.TestableDeviceConfig;
+
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tests for {@link com.android.server.power.ScreenUndimDetector}
+ */
+@RunWith(JUnit4.class)
+public class ScreenUndimDetectorTest {
+    private static final List<Integer> ALL_POLICIES =
+            Arrays.asList(POLICY_OFF,
+                    POLICY_DOZE,
+                    POLICY_DIM,
+                    POLICY_BRIGHT,
+                    POLICY_VR);
+
+    @ClassRule
+    public static final TestableContext sContext = new TestableContext(
+            InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+    @Rule
+    public TestableDeviceConfig.TestableDeviceConfigRule
+            mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule();
+
+    private ScreenUndimDetector mScreenUndimDetector;
+
+    private final TestClock mClock = new TestClock();
+
+    private static class TestClock extends ScreenUndimDetector.InternalClock {
+        long mCurrentTime = 0;
+        @Override
+        public long getCurrentTime() {
+            return mCurrentTime;
+        }
+
+        public void advanceTime(long millisAdvanced) {
+            mCurrentTime += millisAdvanced;
+        }
+    }
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_UNDIMS_REQUIRED,
+                Integer.toString(1), false /*makeDefault*/);
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_MAX_DURATION_BETWEEN_UNDIMS_MILLIS,
+                Long.toString(DEFAULT_MAX_DURATION_BETWEEN_UNDIMS_MILLIS),
+                false /*makeDefault*/);
+
+        mScreenUndimDetector = new ScreenUndimDetector(mClock);
+        mScreenUndimDetector.systemReady(sContext);
+    }
+
+    @Test
+    public void recordScreenPolicy_disabledByFlag_noop() {
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_KEEP_SCREEN_ON_ENABLED, Boolean.FALSE.toString(), false /*makeDefault*/);
+
+        setup();
+        mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
+        mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+
+        assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
+    }
+
+    @Test
+    public void recordScreenPolicy_samePolicy_noop() {
+        for (int policy : ALL_POLICIES) {
+            setup();
+            mScreenUndimDetector.recordScreenPolicy(policy);
+            mScreenUndimDetector.recordScreenPolicy(policy);
+
+            assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
+        }
+    }
+
+    @Test
+    public void recordScreenPolicy_dimToBright_extends() {
+        mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
+        mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+
+        assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isTrue();
+    }
+
+    @Test
+    public void recordScreenPolicy_otherTransitions_doesNotExtend() {
+        for (int from : ALL_POLICIES) {
+            for (int to : ALL_POLICIES) {
+                if (from == POLICY_DIM && to == POLICY_BRIGHT) {
+                    continue;
+                }
+                setup();
+                mScreenUndimDetector.recordScreenPolicy(from);
+                mScreenUndimDetector.recordScreenPolicy(to);
+
+                assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
+                assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(0);
+            }
+        }
+    }
+
+    @Test
+    public void recordScreenPolicy_dimToBright_twoUndimsNeeded_extends() {
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_UNDIMS_REQUIRED,
+                Integer.toString(2), false /*makeDefault*/);
+        mScreenUndimDetector.readValuesFromDeviceConfig();
+
+        mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
+        mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+
+        assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
+
+        mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
+        mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+
+        assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isTrue();
+    }
+
+    @Test
+    public void recordScreenPolicy_dimBrightDimOff_resetsCounter_doesNotExtend() {
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_UNDIMS_REQUIRED,
+                Integer.toString(2), false /*makeDefault*/);
+        mScreenUndimDetector.readValuesFromDeviceConfig();
+
+        mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
+        mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+        mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
+        mScreenUndimDetector.recordScreenPolicy(POLICY_OFF);
+
+        assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
+        assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(0);
+    }
+
+    @Test
+    public void recordScreenPolicy_undimToOff_resetsCounter() {
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_UNDIMS_REQUIRED,
+                Integer.toString(2), false /*makeDefault*/);
+        mScreenUndimDetector.readValuesFromDeviceConfig();
+
+        mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+        mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
+        mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+        mScreenUndimDetector.recordScreenPolicy(POLICY_OFF);
+
+        assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
+        assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(0);
+    }
+
+    @Test
+    public void recordScreenPolicy_undimOffUndim_doesNotExtend() {
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_UNDIMS_REQUIRED,
+                Integer.toString(2), false /*makeDefault*/);
+        mScreenUndimDetector.readValuesFromDeviceConfig();
+
+        // undim
+        mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+        mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
+        mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+        // off
+        mScreenUndimDetector.recordScreenPolicy(POLICY_OFF);
+        // second undim
+        mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+        mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
+        mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+
+        assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
+        assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(1);
+    }
+
+    @Test
+    public void recordScreenPolicy_dimToBright_tooFarApart_doesNotExtend() {
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_UNDIMS_REQUIRED,
+                Integer.toString(2), false /*makeDefault*/);
+        mScreenUndimDetector.readValuesFromDeviceConfig();
+
+        mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
+        mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+
+        mClock.advanceTime(DEFAULT_MAX_DURATION_BETWEEN_UNDIMS_MILLIS + 5);
+        mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
+        mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+
+        assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
+        assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(1);
+    }
+
+    @Test
+    public void recordScreenPolicy_dimToNonBright_resets() {
+        for (int to : Arrays.asList(POLICY_OFF, POLICY_DOZE, POLICY_VR)) {
+            setup();
+            mScreenUndimDetector.mUndimCounter = 1;
+            mScreenUndimDetector.mUndimCounterStartedMillis = 123;
+            mScreenUndimDetector.mWakeLock.acquire();
+
+            mScreenUndimDetector.recordScreenPolicy(POLICY_DIM);
+            mScreenUndimDetector.recordScreenPolicy(to);
+
+            assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(0);
+            assertThat(mScreenUndimDetector.mUndimCounterStartedMillis).isEqualTo(0);
+            assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
+        }
+
+    }
+
+    @Test
+    public void recordScreenPolicy_brightToNonDim_resets() {
+        for (int to : Arrays.asList(POLICY_OFF, POLICY_DOZE, POLICY_VR)) {
+            setup();
+            mScreenUndimDetector.mUndimCounter = 1;
+            mScreenUndimDetector.mUndimCounterStartedMillis = 123;
+            mScreenUndimDetector.mWakeLock.acquire();
+
+            mScreenUndimDetector.recordScreenPolicy(POLICY_BRIGHT);
+            mScreenUndimDetector.recordScreenPolicy(to);
+
+            assertThat(mScreenUndimDetector.mUndimCounter).isEqualTo(0);
+            assertThat(mScreenUndimDetector.mUndimCounterStartedMillis).isEqualTo(0);
+            assertThat(mScreenUndimDetector.mWakeLock.isHeld()).isFalse();
+        }
+    }
+
+    @Test
+    public void recordScreenPolicy_otherTransitions_doesNotReset() {
+        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                KEY_UNDIMS_REQUIRED,
+                Integer.toString(3),
+                false /*makeDefault*/);
+        mScreenUndimDetector.readValuesFromDeviceConfig();
+
+        for (int from : ALL_POLICIES) {
+            for (int to : ALL_POLICIES) {
+                if (from == POLICY_DIM && to != POLICY_BRIGHT) {
+                    continue;
+                }
+                if (from == POLICY_BRIGHT && to != POLICY_DIM) {
+                    continue;
+                }
+                mScreenUndimDetector.mCurrentScreenPolicy = POLICY_OFF;
+                mScreenUndimDetector.mUndimCounter = 1;
+                mScreenUndimDetector.mUndimCounterStartedMillis =
+                        SystemClock.currentThreadTimeMillis();
+
+                mScreenUndimDetector.recordScreenPolicy(from);
+                mScreenUndimDetector.recordScreenPolicy(to);
+
+                assertThat(mScreenUndimDetector.mUndimCounter).isNotEqualTo(0);
+                assertThat(mScreenUndimDetector.mUndimCounterStartedMillis).isNotEqualTo(0);
+            }
+        }
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
index 844687f..ba79a76 100644
--- a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/SensorPrivacyServiceMockingTest.java
@@ -60,6 +60,8 @@
             String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 4);
     public static final String PERSISTENCE_FILE5 =
             String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 5);
+    public static final String PERSISTENCE_FILE6 =
+            String.format(PERSISTENCE_FILE_PATHS_TEMPLATE, 6);
 
     private Context mContext;
     @Mock
@@ -111,6 +113,7 @@
             initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE3);
             initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE4);
             initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE5);
+            initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE6);
 
             // Try all files with two known users
             doReturn(new int[]{0, 10}).when(mMockedUserManagerInternal).getUserIds();
@@ -124,6 +127,7 @@
             initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE3);
             initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE4);
             initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE5);
+            initServiceWithPersistenceFile(onDeviceFile, PERSISTENCE_FILE6);
 
         } finally {
             mockitoSession.finishMocking();
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index bcb2cf8..68b8469 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -71,6 +71,7 @@
     <uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE"/>
     <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS"/>
     <uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS"/>
+    <uses-permission android:name="android.permission.CONFIGURE_DISPLAY_BRIGHTNESS"/>
     <uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/>
     <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG"/>
     <uses-permission android:name="android.permission.HARDWARE_TEST"/>
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
index 8a745d5..4a06611f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
@@ -150,7 +150,6 @@
         when(mMockAms.getTraceManager()).thenReturn(mMockTraceManager);
         mContext = InstrumentationRegistry.getContext();
         mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
-        AccessibilityManagerService ams = new AccessibilityManagerService(mContext);
         mCaptor = new EventCaptor();
         mHandler = new TestHandler();
         mTouchExplorer = new TouchExplorer(mContext, mMockAms, null, mHandler);
diff --git a/services/tests/servicestests/src/com/android/server/appop/AppOpsStartedWatcherTest.java b/services/tests/servicestests/src/com/android/server/appop/AppOpsStartedWatcherTest.java
index c12eb32..e98a4dd 100644
--- a/services/tests/servicestests/src/com/android/server/appop/AppOpsStartedWatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/appop/AppOpsStartedWatcherTest.java
@@ -64,12 +64,16 @@
                 .times(1)).onOpStarted(eq(AppOpsManager.OP_FINE_LOCATION),
                 eq(Process.myUid()), eq(getContext().getPackageName()),
                 eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF),
-                eq(AppOpsManager.MODE_ALLOWED));
+                eq(AppOpsManager.MODE_ALLOWED), eq(OnOpStartedListener.START_TYPE_STARTED),
+                eq(AppOpsManager.ATTRIBUTION_FLAGS_NONE),
+                eq(AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE));
         inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
                 .times(1)).onOpStarted(eq(AppOpsManager.OP_CAMERA),
                 eq(Process.myUid()), eq(getContext().getPackageName()),
                 eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF),
-                eq(AppOpsManager.MODE_ALLOWED));
+                eq(AppOpsManager.MODE_ALLOWED), eq(OnOpStartedListener.START_TYPE_STARTED),
+                eq(AppOpsManager.ATTRIBUTION_FLAGS_NONE),
+                eq(AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE));
 
         // Stop watching
         appOpsManager.stopWatchingStarted(listener);
@@ -94,7 +98,9 @@
                 .times(2)).onOpStarted(eq(AppOpsManager.OP_CAMERA),
                 eq(Process.myUid()), eq(getContext().getPackageName()),
                 eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF),
-                eq(AppOpsManager.MODE_ALLOWED));
+                eq(AppOpsManager.MODE_ALLOWED), eq(OnOpStartedListener.START_TYPE_STARTED),
+                eq(AppOpsManager.ATTRIBUTION_FLAGS_NONE),
+                eq(AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE));
         verifyNoMoreInteractions(listener);
 
         // Finish up
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index 8592166a..a41f79e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -348,6 +348,17 @@
         verify((Interruptable) interruptableMonitor, never()).cancel();
     }
 
+    @Test
+    public void testClientDestroyed_afterFinish() {
+        final HalClientMonitor.LazyDaemon<Object> nonNullDaemon = () -> mock(Object.class);
+        final TestClientMonitor client =
+                new TestClientMonitor(mContext, mToken, nonNullDaemon);
+        mScheduler.scheduleClientMonitor(client);
+        client.mCallback.onClientFinished(client, true /* success */);
+        waitForIdle();
+        assertTrue(client.wasDestroyed());
+    }
+
     private BiometricSchedulerProto getDump(boolean clearSchedulerBuffer) throws Exception {
         return BiometricSchedulerProto.parseFrom(mScheduler.dumpProtoState(clearSchedulerBuffer));
     }
@@ -378,6 +389,11 @@
         protected void handleLifecycleAfterAuth(boolean authenticated) {
 
         }
+
+        @Override
+        public boolean wasUserDetected() {
+            return false;
+        }
     }
 
     private static class TestAuthenticationClient extends AuthenticationClient<Object> {
@@ -407,6 +423,11 @@
         protected void handleLifecycleAfterAuth(boolean authenticated) {
 
         }
+
+        @Override
+        public boolean wasUserDetected() {
+            return false;
+        }
     }
 
     private static class TestClientMonitor2 extends TestClientMonitor {
@@ -427,6 +448,7 @@
     private static class TestClientMonitor extends HalClientMonitor<Object> {
         private boolean mUnableToStart;
         private boolean mStarted;
+        private boolean mDestroyed;
 
         public TestClientMonitor(@NonNull Context context, @NonNull IBinder token,
                 @NonNull LazyDaemon<Object> lazyDaemon) {
@@ -465,6 +487,11 @@
 
         }
 
+        @Override
+        public void destroy() {
+            mDestroyed = true;
+        }
+
         public boolean wasUnableToStart() {
             return mUnableToStart;
         }
@@ -472,6 +499,11 @@
         public boolean hasStarted() {
             return mStarted;
         }
+
+        public boolean wasDestroyed() {
+            return mDestroyed;
+        }
+
     }
 
     private static void waitForIdle() {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java
index f1adcae..bfb0be7 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CoexCoordinatorTest.java
@@ -32,6 +32,7 @@
 import static org.mockito.Mockito.withSettings;
 
 import android.content.Context;
+import android.hardware.biometrics.BiometricConstants;
 import android.os.Handler;
 import android.os.Looper;
 import android.platform.test.annotations.Presubmit;
@@ -61,6 +62,8 @@
     private Context mContext;
     @Mock
     private CoexCoordinator.Callback mCallback;
+    @Mock
+    private CoexCoordinator.ErrorCallback mErrorCallback;
 
     @Before
     public void setUp() {
@@ -70,6 +73,7 @@
 
         mCoexCoordinator = CoexCoordinator.getInstance();
         mCoexCoordinator.setAdvancedLogicEnabled(true);
+        mCoexCoordinator.setFaceHapticDisabledWhenNonBypass(true);
     }
 
     @Test
@@ -151,12 +155,76 @@
 
         mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */, faceClient,
                 mCallback);
-        verify(mCallback).sendHapticFeedback();
+        // Haptics tested in #testKeyguard_bypass_haptics. Let's leave this commented out (instead
+        // of removed) to keep this context.
+        // verify(mCallback).sendHapticFeedback();
         verify(mCallback).sendAuthenticationResult(eq(true) /* addAuthTokenIfStrong */);
         verify(mCallback).handleLifecycleAfterAuth();
     }
 
     @Test
+    public void testKeyguard_faceAuthSuccess_nonBypass_udfpsRunning_noHaptics() {
+        testKeyguard_bypass_haptics(false /* bypassEnabled */,
+                true /* faceAccepted */,
+                false /* shouldReceiveHaptics */);
+    }
+
+    @Test
+    public void testKeyguard_faceAuthReject_nonBypass_udfpsRunning_noHaptics() {
+        testKeyguard_bypass_haptics(false /* bypassEnabled */,
+                false /* faceAccepted */,
+                false /* shouldReceiveHaptics */);
+    }
+
+    @Test
+    public void testKeyguard_faceAuthSuccess_bypass_udfpsRunning_haptics() {
+        testKeyguard_bypass_haptics(true /* bypassEnabled */,
+                true /* faceAccepted */,
+                true /* shouldReceiveHaptics */);
+    }
+
+    @Test
+    public void testKeyguard_faceAuthReject_bypass_udfpsRunning_haptics() {
+        testKeyguard_bypass_haptics(true /* bypassEnabled */,
+                false /* faceAccepted */,
+                true /* shouldReceiveHaptics */);
+    }
+
+    private void testKeyguard_bypass_haptics(boolean bypassEnabled, boolean faceAccepted,
+            boolean shouldReceiveHaptics) {
+        mCoexCoordinator.reset();
+
+        AuthenticationClient<?> faceClient = mock(AuthenticationClient.class);
+        when(faceClient.isKeyguard()).thenReturn(true);
+        when(faceClient.isKeyguardBypassEnabled()).thenReturn(bypassEnabled);
+
+        AuthenticationClient<?> udfpsClient = mock(AuthenticationClient.class,
+                withSettings().extraInterfaces(Udfps.class));
+        when(udfpsClient.isKeyguard()).thenReturn(true);
+        when(((Udfps) udfpsClient).isPointerDown()).thenReturn(false);
+
+        mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient);
+        mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, udfpsClient);
+
+        if (faceAccepted) {
+            mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */, faceClient,
+                    mCallback);
+        } else {
+            mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */, faceClient,
+                    LockoutTracker.LOCKOUT_NONE, mCallback);
+        }
+
+        if (shouldReceiveHaptics) {
+            verify(mCallback).sendHapticFeedback();
+        } else {
+            verify(mCallback, never()).sendHapticFeedback();
+        }
+
+        verify(mCallback).sendAuthenticationResult(eq(faceAccepted) /* addAuthTokenIfStrong */);
+        verify(mCallback).handleLifecycleAfterAuth();
+    }
+
+    @Test
     public void testKeyguard_faceAuth_udfpsTouching_faceSuccess_thenUdfpsRejectedWithinBounds() {
         testKeyguard_faceAuth_udfpsTouching_faceSuccess(false /* thenUdfpsAccepted */,
                 0 /* udfpsRejectedAfterMs */);
@@ -190,13 +258,16 @@
         mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient);
         mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, udfpsClient);
 
+        // For easier reading
+        final CoexCoordinator.Callback faceCallback = mCallback;
+
         mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */, faceClient,
-                mCallback);
-        verify(mCallback, never()).sendHapticFeedback();
-        verify(mCallback, never()).sendAuthenticationResult(anyBoolean());
+                faceCallback);
+        verify(faceCallback, never()).sendHapticFeedback();
+        verify(faceCallback, never()).sendAuthenticationResult(anyBoolean());
         // CoexCoordinator requests the system to hold onto this AuthenticationClient until
         // UDFPS result is known
-        verify(mCallback, never()).handleLifecycleAfterAuth();
+        verify(faceCallback, never()).handleLifecycleAfterAuth();
 
         // Reset the mock
         CoexCoordinator.Callback udfpsCallback = mock(CoexCoordinator.Callback.class);
@@ -209,6 +280,8 @@
             verify(udfpsCallback).sendAuthenticationResult(true /* addAuthTokenIfStrong */);
             verify(udfpsCallback).handleLifecycleAfterAuth();
 
+            verify(faceCallback).sendAuthenticationCanceled();
+
             assertTrue(mCoexCoordinator.mSuccessfulAuths.isEmpty());
         } else {
             mCoexCoordinator.onAuthenticationRejected(udfpsRejectedAfterMs, udfpsClient,
@@ -216,16 +289,16 @@
             if (udfpsRejectedAfterMs <= CoexCoordinator.SUCCESSFUL_AUTH_VALID_DURATION_MS) {
                 verify(udfpsCallback, never()).sendHapticFeedback();
 
-                verify(mCallback).sendHapticFeedback();
-                verify(mCallback).sendAuthenticationResult(eq(true) /* addAuthTokenIfStrong */);
-                verify(mCallback).handleLifecycleAfterAuth();
+                verify(faceCallback).sendHapticFeedback();
+                verify(faceCallback).sendAuthenticationResult(eq(true) /* addAuthTokenIfStrong */);
+                verify(faceCallback).handleLifecycleAfterAuth();
 
                 assertTrue(mCoexCoordinator.mSuccessfulAuths.isEmpty());
             } else {
                 assertTrue(mCoexCoordinator.mSuccessfulAuths.isEmpty());
 
-                verify(mCallback, never()).sendHapticFeedback();
-                verify(mCallback, never()).sendAuthenticationResult(anyBoolean());
+                verify(faceCallback, never()).sendHapticFeedback();
+                verify(faceCallback, never()).sendAuthenticationResult(anyBoolean());
 
                 verify(udfpsCallback).sendHapticFeedback();
                 verify(udfpsCallback)
@@ -294,12 +367,13 @@
     }
 
     @Test
-    public void testKeyguard_udfpsRejected_thenFaceRejected() {
+    public void testKeyguard_udfpsRejected_thenFaceRejected_noKeyguardBypass() {
         mCoexCoordinator.reset();
 
         AuthenticationClient<?> faceClient = mock(AuthenticationClient.class);
         when(faceClient.isKeyguard()).thenReturn(true);
         when(faceClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED);
+        when(faceClient.isKeyguardBypassEnabled()).thenReturn(false); // TODO: also test "true" case
 
         AuthenticationClient<?> udfpsClient = mock(AuthenticationClient.class,
                 withSettings().extraInterfaces(Udfps.class));
@@ -312,8 +386,9 @@
 
         mCoexCoordinator.onAuthenticationRejected(0 /* currentTimeMillis */, udfpsClient,
                 LockoutTracker.LOCKOUT_NONE, mCallback);
-        // Client becomes paused, but finger does not necessarily lift, since we suppress the haptic
-        when(udfpsClient.getState()).thenReturn(AuthenticationClient.STATE_STARTED_PAUSED);
+        // Auth was attempted
+        when(udfpsClient.getState())
+                .thenReturn(AuthenticationClient.STATE_STARTED_PAUSED_ATTEMPTED);
         verify(mCallback, never()).sendHapticFeedback();
         verify(mCallback).handleLifecycleAfterAuth();
 
@@ -418,4 +493,82 @@
         verify(callback).handleLifecycleAfterAuth();
         verify(successfulAuths).remove(eq(auth));
     }
+
+    @Test
+    public void testBiometricPrompt_FaceError() {
+        mCoexCoordinator.reset();
+
+        AuthenticationClient<?> client = mock(AuthenticationClient.class);
+        when(client.isBiometricPrompt()).thenReturn(true);
+        when(client.wasAuthAttempted()).thenReturn(true);
+
+        mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, client);
+
+        mCoexCoordinator.onAuthenticationError(client, BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
+                mErrorCallback);
+        verify(mErrorCallback).sendHapticFeedback();
+    }
+
+    @Test
+    public void testKeyguard_faceAuthOnly_errorWhenBypassEnabled() {
+        testKeyguard_faceAuthOnly(true /* bypassEnabled */);
+    }
+
+    @Test
+    public void testKeyguard_faceAuthOnly_errorWhenBypassDisabled() {
+        testKeyguard_faceAuthOnly(false /* bypassEnabled */);
+    }
+
+    private void testKeyguard_faceAuthOnly(boolean bypassEnabled) {
+        mCoexCoordinator.reset();
+
+        AuthenticationClient<?> client = mock(AuthenticationClient.class);
+        when(client.isKeyguard()).thenReturn(true);
+        when(client.isKeyguardBypassEnabled()).thenReturn(bypassEnabled);
+        when(client.wasAuthAttempted()).thenReturn(true);
+        when(client.wasUserDetected()).thenReturn(true);
+
+        mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, client);
+
+        mCoexCoordinator.onAuthenticationError(client, BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
+                mErrorCallback);
+        verify(mErrorCallback).sendHapticFeedback();
+    }
+
+    @Test
+    public void testKeyguard_coex_faceErrorWhenBypassEnabled() {
+        testKeyguard_coex_faceError(true /* bypassEnabled */);
+    }
+
+    @Test
+    public void testKeyguard_coex_faceErrorWhenBypassDisabled() {
+        testKeyguard_coex_faceError(false /* bypassEnabled */);
+    }
+
+    private void testKeyguard_coex_faceError(boolean bypassEnabled) {
+        mCoexCoordinator.reset();
+
+        AuthenticationClient<?> faceClient = mock(AuthenticationClient.class);
+        when(faceClient.isKeyguard()).thenReturn(true);
+        when(faceClient.isKeyguardBypassEnabled()).thenReturn(bypassEnabled);
+        when(faceClient.wasAuthAttempted()).thenReturn(true);
+        when(faceClient.wasUserDetected()).thenReturn(true);
+
+        AuthenticationClient<?> udfpsClient = mock(AuthenticationClient.class,
+                withSettings().extraInterfaces(Udfps.class));
+        when(udfpsClient.isKeyguard()).thenReturn(true);
+        when(((Udfps) udfpsClient).isPointerDown()).thenReturn(false);
+
+        mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient);
+        mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, udfpsClient);
+
+        mCoexCoordinator.onAuthenticationError(faceClient,
+                BiometricConstants.BIOMETRIC_ERROR_TIMEOUT, mErrorCallback);
+
+        if (bypassEnabled) {
+            verify(mErrorCallback).sendHapticFeedback();
+        } else {
+            verify(mErrorCallback, never()).sendHapticFeedback();
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index ff8fbda..b1b6e53 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -18,6 +18,7 @@
 
 import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
 
+import static org.mockito.Mockito.when;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotNull;
@@ -35,6 +36,11 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
+import static org.mockito.Mockito.mock;
+
+import com.android.server.wm.ActivityTaskManagerInternal;
+import com.android.server.wm.WindowProcessController;
+
 import junit.framework.Assert;
 
 import org.junit.Before;
@@ -55,10 +61,15 @@
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public final class DeviceStateManagerServiceTest {
-    private static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(0, "DEFAULT");
-    private static final DeviceState OTHER_DEVICE_STATE = new DeviceState(1, "OTHER");
+    private static final DeviceState DEFAULT_DEVICE_STATE =
+            new DeviceState(0, "DEFAULT", 0 /* flags */);
+    private static final DeviceState OTHER_DEVICE_STATE =
+            new DeviceState(1, "OTHER", 0 /* flags */);
     // A device state that is not reported as being supported for the default test provider.
-    private static final DeviceState UNSUPPORTED_DEVICE_STATE = new DeviceState(255, "UNSUPPORTED");
+    private static final DeviceState UNSUPPORTED_DEVICE_STATE =
+            new DeviceState(255, "UNSUPPORTED", 0 /* flags */);
+
+    private static final int FAKE_PROCESS_ID = 100;
 
     private TestDeviceStatePolicy mPolicy;
     private TestDeviceStateProvider mProvider;
@@ -69,6 +80,25 @@
         mProvider = new TestDeviceStateProvider();
         mPolicy = new TestDeviceStatePolicy(mProvider);
         mService = new DeviceStateManagerService(InstrumentationRegistry.getContext(), mPolicy);
+
+        // Necessary to allow us to check for top app process id in tests
+        mService.mActivityTaskManagerInternal = mock(ActivityTaskManagerInternal.class);
+        WindowProcessController windowProcessController = mock(WindowProcessController.class);
+        when(mService.mActivityTaskManagerInternal.getTopApp())
+                .thenReturn(windowProcessController);
+        when(windowProcessController.getPid()).thenReturn(FAKE_PROCESS_ID);
+
+        flushHandler(); // Flush the handler to ensure the initial values are committed.
+    }
+
+    private void flushHandler() {
+        flushHandler(1);
+    }
+
+    private void flushHandler(int count) {
+        for (int i = 0; i < count; i++) {
+            mService.getHandler().runWithScissors(() -> {}, 0);
+        }
     }
 
     @Test
@@ -80,6 +110,7 @@
                 DEFAULT_DEVICE_STATE.getIdentifier());
 
         mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
+        flushHandler();
         assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
         assertEquals(mService.getPendingState(), Optional.empty());
         assertEquals(mService.getBaseState(), Optional.of(OTHER_DEVICE_STATE));
@@ -92,6 +123,7 @@
         mPolicy.blockConfigure();
 
         mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
+        flushHandler();
         assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mService.getPendingState(), Optional.of(OTHER_DEVICE_STATE));
         assertEquals(mService.getBaseState(), Optional.of(OTHER_DEVICE_STATE));
@@ -99,6 +131,7 @@
                 OTHER_DEVICE_STATE.getIdentifier());
 
         mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier());
+        flushHandler();
         assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mService.getPendingState(), Optional.of(OTHER_DEVICE_STATE));
         assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
@@ -106,6 +139,7 @@
                 OTHER_DEVICE_STATE.getIdentifier());
 
         mPolicy.resumeConfigure();
+        flushHandler();
         assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mService.getPendingState(), Optional.empty());
         assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
@@ -149,6 +183,7 @@
         assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
 
         mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
+        flushHandler();
 
         // The current committed and requests states do not change because the current state remains
         // supported.
@@ -166,6 +201,7 @@
         mService.getBinderService().registerCallback(callback);
 
         // An initial callback will be triggered on registration, so we clear it here.
+        flushHandler();
         callback.clearLastNotifiedInfo();
 
         assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
@@ -174,6 +210,7 @@
 
         mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE,
                 OTHER_DEVICE_STATE });
+        flushHandler();
 
         // The current committed and requests states do not change because the current state remains
         // supported.
@@ -203,12 +240,14 @@
         mService.getBinderService().registerCallback(callback);
 
         mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
+        flushHandler();
         assertEquals(callback.getLastNotifiedInfo().baseState,
                 OTHER_DEVICE_STATE.getIdentifier());
         assertEquals(callback.getLastNotifiedInfo().currentState,
                 OTHER_DEVICE_STATE.getIdentifier());
 
         mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier());
+        flushHandler();
         assertEquals(callback.getLastNotifiedInfo().baseState,
                 DEFAULT_DEVICE_STATE.getIdentifier());
         assertEquals(callback.getLastNotifiedInfo().currentState,
@@ -216,6 +255,7 @@
 
         mPolicy.blockConfigure();
         mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
+        flushHandler();
         // The callback should not have been notified of the state change as the policy is still
         // pending callback.
         assertEquals(callback.getLastNotifiedInfo().baseState,
@@ -224,6 +264,7 @@
                 DEFAULT_DEVICE_STATE.getIdentifier());
 
         mPolicy.resumeConfigure();
+        flushHandler();
         // Now that the policy is finished processing the callback should be notified of the state
         // change.
         assertEquals(callback.getLastNotifiedInfo().baseState,
@@ -236,6 +277,7 @@
     public void registerCallback_emitsInitialValue() throws RemoteException {
         TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
         mService.getBinderService().registerCallback(callback);
+        flushHandler();
         assertNotNull(callback.getLastNotifiedInfo());
         assertEquals(callback.getLastNotifiedInfo().baseState,
                 DEFAULT_DEVICE_STATE.getIdentifier());
@@ -247,6 +289,7 @@
     public void requestState() throws RemoteException {
         TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
         mService.getBinderService().registerCallback(callback);
+        flushHandler();
 
         final IBinder token = new Binder();
         assertEquals(callback.getLastNotifiedStatus(token),
@@ -254,6 +297,10 @@
 
         mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(),
                 0 /* flags */);
+        // Flush the handler twice. The first flush ensures the request is added and the policy is
+        // notified, while the second flush ensures the callback is notified once the change is
+        // committed.
+        flushHandler(2 /* count */);
 
         assertEquals(callback.getLastNotifiedStatus(token),
                 TestDeviceStateManagerCallback.STATUS_ACTIVE);
@@ -271,6 +318,7 @@
                 OTHER_DEVICE_STATE.getIdentifier());
 
         mService.getBinderService().cancelRequest(token);
+        flushHandler();
 
         assertEquals(callback.getLastNotifiedStatus(token),
                 TestDeviceStateManagerCallback.STATUS_CANCELED);
@@ -291,6 +339,7 @@
     public void requestState_pendingStateAtRequest() throws RemoteException {
         TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
         mService.getBinderService().registerCallback(callback);
+        flushHandler();
 
         mPolicy.blockConfigure();
 
@@ -303,6 +352,10 @@
 
         mService.getBinderService().requestState(firstRequestToken,
                 OTHER_DEVICE_STATE.getIdentifier(), 0 /* flags */);
+        // Flush the handler twice. The first flush ensures the request is added and the policy is
+        // notified, while the second flush ensures the callback is notified once the change is
+        // committed.
+        flushHandler(2 /* count */);
 
         assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mService.getPendingState(), Optional.of(OTHER_DEVICE_STATE));
@@ -312,8 +365,8 @@
 
         mService.getBinderService().requestState(secondRequestToken,
                 DEFAULT_DEVICE_STATE.getIdentifier(), 0 /* flags */);
-
         mPolicy.resumeConfigureOnce();
+        flushHandler();
 
         // First request status is now suspended as there is another pending request.
         assertEquals(callback.getLastNotifiedStatus(firstRequestToken),
@@ -330,6 +383,7 @@
                 DEFAULT_DEVICE_STATE.getIdentifier());
 
         mPolicy.resumeConfigure();
+        flushHandler();
 
         assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
         assertEquals(mService.getPendingState(), Optional.empty());
@@ -339,6 +393,7 @@
 
         // Now cancel the second request to make the first request active.
         mService.getBinderService().cancelRequest(secondRequestToken);
+        flushHandler();
 
         assertEquals(callback.getLastNotifiedStatus(firstRequestToken),
                 TestDeviceStateManagerCallback.STATUS_ACTIVE);
@@ -356,6 +411,7 @@
     public void requestState_sameAsBaseState() throws RemoteException {
         TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
         mService.getBinderService().registerCallback(callback);
+        flushHandler();
 
         final IBinder token = new Binder();
         assertEquals(callback.getLastNotifiedStatus(token),
@@ -363,6 +419,7 @@
 
         mService.getBinderService().requestState(token, DEFAULT_DEVICE_STATE.getIdentifier(),
                 0 /* flags */);
+        flushHandler();
 
         assertEquals(callback.getLastNotifiedStatus(token),
                 TestDeviceStateManagerCallback.STATUS_ACTIVE);
@@ -372,6 +429,7 @@
     public void requestState_flagCancelWhenBaseChanges() throws RemoteException {
         TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
         mService.getBinderService().registerCallback(callback);
+        flushHandler();
 
         final IBinder token = new Binder();
         assertEquals(callback.getLastNotifiedStatus(token),
@@ -379,6 +437,10 @@
 
         mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(),
                 DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES);
+        // Flush the handler twice. The first flush ensures the request is added and the policy is
+        // notified, while the second flush ensures the callback is notified once the change is
+        // committed.
+        flushHandler(2 /* count */);
 
         assertEquals(callback.getLastNotifiedStatus(token),
                 TestDeviceStateManagerCallback.STATUS_ACTIVE);
@@ -391,6 +453,7 @@
                 OTHER_DEVICE_STATE.getIdentifier());
 
         mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
+        flushHandler();
 
         // Request is canceled because the base state changed.
         assertEquals(callback.getLastNotifiedStatus(token),
@@ -407,6 +470,7 @@
     public void requestState_becomesUnsupported() throws RemoteException {
         TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
         mService.getBinderService().registerCallback(callback);
+        flushHandler();
 
         final IBinder token = new Binder();
         assertEquals(callback.getLastNotifiedStatus(token),
@@ -414,6 +478,7 @@
 
         mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(),
                 0 /* flags */);
+        flushHandler();
 
         assertEquals(callback.getLastNotifiedStatus(token),
                 TestDeviceStateManagerCallback.STATUS_ACTIVE);
@@ -425,6 +490,7 @@
                 OTHER_DEVICE_STATE.getIdentifier());
 
         mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
+        flushHandler();
 
         // Request is canceled because the state is no longer supported.
         assertEquals(callback.getLastNotifiedStatus(token),
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateTest.java
index b5c8053..e286cb2 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateTest.java
@@ -41,24 +41,26 @@
     @Test
     public void testConstruct() {
         final DeviceState state = new DeviceState(MINIMUM_DEVICE_STATE /* identifier */,
-                "CLOSED" /* name */);
+                "CLOSED" /* name */, DeviceState.FLAG_CANCEL_STICKY_REQUESTS /* flags */);
         assertEquals(state.getIdentifier(), MINIMUM_DEVICE_STATE);
         assertEquals(state.getName(), "CLOSED");
+        assertEquals(state.getFlags(), DeviceState.FLAG_CANCEL_STICKY_REQUESTS);
     }
 
     @Test
     public void testConstruct_nullName() {
         final DeviceState state = new DeviceState(MAXIMUM_DEVICE_STATE /* identifier */,
-                null /* name */);
+                null /* name */, 0/* flags */);
         assertEquals(state.getIdentifier(), MAXIMUM_DEVICE_STATE);
         assertNull(state.getName());
+        assertEquals(state.getFlags(), 0);
     }
 
     @Test
     public void testConstruct_tooLargeIdentifier() {
         assertThrows(IllegalArgumentException.class, () -> {
             final DeviceState state = new DeviceState(MAXIMUM_DEVICE_STATE + 1 /* identifier */,
-                    null /* name */);
+                    null /* name */, 0 /* flags */);
         });
     }
 
@@ -66,7 +68,7 @@
     public void testConstruct_tooSmallIdentifier() {
         assertThrows(IllegalArgumentException.class, () -> {
             final DeviceState state = new DeviceState(MINIMUM_DEVICE_STATE - 1 /* identifier */,
-                    null /* name */);
+                    null /* name */, 0 /* flags */);
         });
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java b/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java
new file mode 100644
index 0000000..c9cf2f0
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java
@@ -0,0 +1,230 @@
+/*
+ * 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.devicestate;
+
+import static com.android.server.devicestate.OverrideRequestController.STATUS_ACTIVE;
+import static com.android.server.devicestate.OverrideRequestController.STATUS_CANCELED;
+import static com.android.server.devicestate.OverrideRequestController.STATUS_SUSPENDED;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
+
+import android.annotation.Nullable;
+import android.hardware.devicestate.DeviceStateRequest;
+import android.os.Binder;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.annotation.NonNull;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Unit tests for {@link OverrideRequestController}.
+ * <p/>
+ * Run with <code>atest OverrideRequestControllerTest</code>.
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public final class OverrideRequestControllerTest {
+    private TestStatusChangeListener mStatusListener;
+    private OverrideRequestController mController;
+
+    @Before
+    public void setup() {
+        mStatusListener = new TestStatusChangeListener();
+        mController = new OverrideRequestController(mStatusListener);
+    }
+
+    @Test
+    public void addRequest() {
+        OverrideRequest request = new OverrideRequest(new Binder(), 0 /* pid */,
+                0 /* requestedState */, 0 /* flags */);
+        assertNull(mStatusListener.getLastStatus(request));
+
+        mController.addRequest(request);
+        assertEquals(mStatusListener.getLastStatus(request).intValue(), STATUS_ACTIVE);
+    }
+
+    @Test
+    public void addRequest_suspendExistingRequest() {
+        OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+                0 /* requestedState */, 0 /* flags */);
+        assertNull(mStatusListener.getLastStatus(firstRequest));
+
+        mController.addRequest(firstRequest);
+        assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
+
+        OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+                0 /* requestedState */, 0 /* flags */);
+        assertNull(mStatusListener.getLastStatus(secondRequest));
+
+        mController.addRequest(secondRequest);
+        assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
+        assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED);
+    }
+
+    @Test
+    public void addRequest_cancelActiveRequest() {
+        OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+                0 /* requestedState */, 0 /* flags */);
+        OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+                0 /* requestedState */, 0 /* flags */);
+
+        mController.addRequest(firstRequest);
+        mController.addRequest(secondRequest);
+
+        assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
+        assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED);
+
+        mController.cancelRequest(secondRequest.getToken());
+
+        assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_CANCELED);
+        assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
+    }
+
+    @Test
+    public void addRequest_cancelSuspendedRequest() {
+        OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+                0 /* requestedState */, 0 /* flags */);
+        OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+                0 /* requestedState */, 0 /* flags */);
+
+        mController.addRequest(firstRequest);
+        mController.addRequest(secondRequest);
+
+        assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
+        assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED);
+
+        mController.cancelRequest(firstRequest.getToken());
+
+        assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
+        assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
+    }
+
+    @Test
+    public void handleBaseStateChanged() {
+        OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+                0 /* requestedState */, 0 /* flags */);
+        OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+                0 /* requestedState */,
+                DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES /* flags */);
+
+        mController.addRequest(firstRequest);
+        mController.addRequest(secondRequest);
+
+        assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
+        assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED);
+
+        mController.handleBaseStateChanged();
+
+        assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_CANCELED);
+        assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
+    }
+
+    @Test
+    public void handleProcessDied() {
+        OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+                0 /* requestedState */, 0 /* flags */);
+        OverrideRequest secondRequest = new OverrideRequest(new Binder(), 1 /* pid */,
+                0 /* requestedState */, 0 /* flags */);
+
+        mController.addRequest(firstRequest);
+        mController.addRequest(secondRequest);
+
+        assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
+        assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED);
+
+        mController.handleProcessDied(1);
+
+        assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_CANCELED);
+        assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
+
+        mController.handleProcessDied(0);
+
+        assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
+    }
+
+    @Test
+    public void handleProcessDied_stickyRequests() {
+        mController.setStickyRequestsAllowed(true);
+
+        OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+                0 /* requestedState */, 0 /* flags */);
+        OverrideRequest secondRequest = new OverrideRequest(new Binder(), 1 /* pid */,
+                0 /* requestedState */, 0 /* flags */);
+
+        mController.addRequest(firstRequest);
+        mController.addRequest(secondRequest);
+
+        assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
+        assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED);
+
+        mController.handleProcessDied(1);
+
+        assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
+        assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED);
+
+        mController.cancelStickyRequests();
+
+        assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_CANCELED);
+        assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
+    }
+
+    @Test
+    public void handleNewSupportedStates() {
+        OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+                1 /* requestedState */, 0 /* flags */);
+        OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+                2 /* requestedState */, 0 /* flags */);
+
+        mController.addRequest(firstRequest);
+        mController.addRequest(secondRequest);
+
+        assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
+        assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_SUSPENDED);
+
+        mController.handleNewSupportedStates(new int[]{ 0, 1 });
+
+        assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_CANCELED);
+        assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
+
+        mController.handleNewSupportedStates(new int[]{ 0 });
+
+        assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
+    }
+
+    private static final class TestStatusChangeListener implements
+            OverrideRequestController.StatusChangeListener {
+        private Map<OverrideRequest, Integer> mLastStatusMap = new HashMap<>();
+
+        @Override
+        public void onStatusChanged(@NonNull OverrideRequest request, int newStatus) {
+            mLastStatusMap.put(request, newStatus);
+        }
+
+        @Nullable
+        public Integer getLastStatus(OverrideRequest request) {
+            return mLastStatusMap.get(request);
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 5ba375b..7c55716 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -885,6 +885,61 @@
         assertFalse(callback.mDisplayAddedCalled);
     }
 
+
+
+    @Test
+    public void testSettingTwoBrightnessConfigurationsOnMultiDisplay() {
+        Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+        DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+
+        // get the first two internal displays
+        Display[] displays = displayManager.getDisplays();
+        Display internalDisplayOne = null;
+        Display internalDisplayTwo = null;
+        for (Display display : displays) {
+            if (display.getType() == Display.TYPE_INTERNAL) {
+                if (internalDisplayOne == null) {
+                    internalDisplayOne = display;
+                } else {
+                    internalDisplayTwo = display;
+                    break;
+                }
+            }
+        }
+
+        // return if there are fewer than 2 displays on this device
+        if (internalDisplayOne == null || internalDisplayTwo == null) {
+            return;
+        }
+
+        final String uniqueDisplayIdOne = internalDisplayOne.getUniqueId();
+        final String uniqueDisplayIdTwo = internalDisplayTwo.getUniqueId();
+
+        BrightnessConfiguration configOne =
+                new BrightnessConfiguration.Builder(
+                        new float[]{0.0f, 12345.0f}, new float[]{15.0f, 400.0f})
+                        .setDescription("model:1").build();
+        BrightnessConfiguration configTwo =
+                new BrightnessConfiguration.Builder(
+                        new float[]{0.0f, 6789.0f}, new float[]{12.0f, 300.0f})
+                        .setDescription("model:2").build();
+
+        displayManager.setBrightnessConfigurationForDisplay(configOne,
+                uniqueDisplayIdOne);
+        displayManager.setBrightnessConfigurationForDisplay(configTwo,
+                uniqueDisplayIdTwo);
+
+        BrightnessConfiguration configFromOne =
+                displayManager.getBrightnessConfigurationForDisplay(uniqueDisplayIdOne);
+        BrightnessConfiguration configFromTwo =
+                displayManager.getBrightnessConfigurationForDisplay(uniqueDisplayIdTwo);
+
+        assertNotNull(configFromOne);
+        assertEquals(configOne, configFromOne);
+        assertEquals(configTwo, configFromTwo);
+
+    }
+
     private void testDisplayInfoFrameRateOverrideModeCompat(boolean compatChangeEnabled)
             throws Exception {
         DisplayManagerService displayManager =
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index a205a1d..1ac28ab 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -1340,11 +1340,19 @@
                 createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
         director.start(createMockSensorManager());
 
-        ArgumentCaptor<ProximityActiveListener> captor =
+        ArgumentCaptor<ProximityActiveListener> ProximityCaptor =
                 ArgumentCaptor.forClass(ProximityActiveListener.class);
         verify(mSensorManagerInternalMock).addProximityActiveListener(any(Executor.class),
-                captor.capture());
-        ProximityActiveListener listener = captor.getValue();
+                ProximityCaptor.capture());
+        ProximityActiveListener proximityListener = ProximityCaptor.getValue();
+
+        ArgumentCaptor<DisplayListener> DisplayCaptor =
+                ArgumentCaptor.forClass(DisplayListener.class);
+        verify(mInjector).registerDisplayListener(DisplayCaptor.capture(), any(Handler.class),
+                eq(DisplayManager.EVENT_FLAG_DISPLAY_ADDED
+                        | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
+                        | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
+        DisplayListener displayListener = DisplayCaptor.getValue();
 
         // Verify that there is no proximity vote initially
         Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY);
@@ -1353,13 +1361,38 @@
         when(mDisplayManagerInternalMock.getRefreshRateForDisplayAndSensor(eq(DISPLAY_ID), eq(null),
                   eq(Sensor.STRING_TYPE_PROXIMITY))).thenReturn(new RefreshRateRange(60, 60));
 
+        when(mInjector.isDozeState(any(Display.class))).thenReturn(false);
+
         // Set the proximity to active and verify that we added a vote.
-        listener.onProximityActive(true);
+        proximityListener.onProximityActive(true);
+        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY);
+        assertVoteForRefreshRate(vote, 60.f);
+
+        // Set the display state to doze and verify that the vote is gone
+        when(mInjector.isDozeState(any(Display.class))).thenReturn(true);
+        displayListener.onDisplayAdded(DISPLAY_ID);
+        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY);
+        assertNull(vote);
+
+        // Set the display state to on and verify that we added the vote back.
+        when(mInjector.isDozeState(any(Display.class))).thenReturn(false);
+        displayListener.onDisplayChanged(DISPLAY_ID);
+        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY);
+        assertVoteForRefreshRate(vote, 60.f);
+
+        // Set the display state to doze and verify that the vote is gone
+        when(mInjector.isDozeState(any(Display.class))).thenReturn(true);
+        displayListener.onDisplayAdded(DISPLAY_ID);
+        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY);
+        assertNull(vote);
+
+        // Remove the display to cause the doze state to be removed
+        displayListener.onDisplayRemoved(DISPLAY_ID);
         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY);
         assertVoteForRefreshRate(vote, 60.f);
 
         // Turn prox off and verify vote is gone.
-        listener.onProximityActive(false);
+        proximityListener.onProximityActive(false);
         vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY);
         assertNull(vote);
     }
@@ -1710,6 +1743,11 @@
             return null;
         }
 
+        @Override
+        public boolean isDozeState(Display d) {
+            return false;
+        }
+
         void notifyPeakRefreshRateChanged() {
             if (mPeakRefreshRateObserver != null) {
                 mPeakRefreshRateObserver.dispatchChange(false /*selfChange*/,
diff --git a/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java b/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java
index 196454b..57a9cb2 100644
--- a/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java
@@ -17,13 +17,16 @@
 package com.android.server.display;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import android.content.Context;
 import android.hardware.display.BrightnessConfiguration;
 import android.util.Pair;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -144,15 +147,93 @@
     }
 
     @Test
-    public void testStoreAndReloadOfBrightnessConfigurations() {
+    public void testStoreAndReloadOfDisplayBrightnessConfigurations() {
+        final String uniqueDisplayId = "test:123";
+        int userSerial = 0;
+        String packageName = "pdsTestPackage";
         final float[] lux = { 0f, 10f };
         final float[] nits = {1f, 100f };
         final BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits)
                 .setDescription("a description")
                 .build();
         mDataStore.loadIfNeeded();
+        assertNull(mDataStore.getBrightnessConfigurationForDisplayLocked(uniqueDisplayId,
+                userSerial));
+
+        DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) {
+            @Override
+            public boolean hasStableUniqueId() {
+                return true;
+            }
+
+            @Override
+            public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
+                return null;
+            }
+        };
+
+        mDataStore.setBrightnessConfigurationForDisplayLocked(config, testDisplayDevice, userSerial,
+                packageName);
+
+        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        mInjector.setWriteStream(baos);
+        mDataStore.saveIfNeeded();
+        assertTrue(mInjector.wasWriteSuccessful());
+        TestInjector newInjector = new TestInjector();
+        PersistentDataStore newDataStore = new PersistentDataStore(newInjector);
+        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+        newInjector.setReadStream(bais);
+        newDataStore.loadIfNeeded();
+        assertNotNull(newDataStore.getBrightnessConfigurationForDisplayLocked(uniqueDisplayId,
+                userSerial));
+        assertEquals(mDataStore.getBrightnessConfigurationForDisplayLocked(uniqueDisplayId,
+                userSerial), newDataStore.getBrightnessConfigurationForDisplayLocked(
+                        uniqueDisplayId, userSerial));
+    }
+
+    @Test
+    public void testSetBrightnessConfigurationFailsWithUnstableId() {
+        final String uniqueDisplayId = "test:123";
+        int userSerial = 0;
+        String packageName = "pdsTestPackage";
+        final float[] lux = { 0f, 10f };
+        final float[] nits = {1f, 100f };
+        final BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits)
+                .setDescription("a description")
+                .build();
+        mDataStore.loadIfNeeded();
+        assertNull(mDataStore.getBrightnessConfigurationForDisplayLocked(uniqueDisplayId,
+                userSerial));
+
+        DisplayDevice testDisplayDevice = new DisplayDevice(null, null, uniqueDisplayId, null) {
+            @Override
+            public boolean hasStableUniqueId() {
+                return false;
+            }
+
+            @Override
+            public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
+                return null;
+            }
+        };
+
+        assertFalse(mDataStore.setBrightnessConfigurationForDisplayLocked(
+                config, testDisplayDevice, userSerial, packageName));
+    }
+
+    @Test
+    public void testStoreAndReloadOfBrightnessConfigurations() {
+        final float[] lux = { 0f, 10f };
+        final float[] nits = {1f, 100f };
+        final BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits)
+                .setDescription("a description")
+                .build();
+        Context context = InstrumentationRegistry.getInstrumentation().getContext();
+        String packageName = context.getPackageName();
+
+        mDataStore.loadIfNeeded();
         assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/));
-        mDataStore.setBrightnessConfigurationForUser(config, 0, "packagename");
+        mDataStore.setBrightnessConfigurationForUser(config, 0, packageName);
 
         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
         mInjector.setWriteStream(baos);
@@ -173,17 +254,18 @@
     public void testNullBrightnessConfiguration() {
         final float[] lux = { 0f, 10f };
         final float[] nits = {1f, 100f };
+        int userSerial = 0;
         final BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits)
                 .setDescription("a description")
                 .build();
         mDataStore.loadIfNeeded();
-        assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/));
+        assertNull(mDataStore.getBrightnessConfiguration(userSerial));
 
-        mDataStore.setBrightnessConfigurationForUser(config, 0, "packagename");
-        assertNotNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/));
+        mDataStore.setBrightnessConfigurationForUser(config, userSerial, "packagename");
+        assertNotNull(mDataStore.getBrightnessConfiguration(userSerial));
 
-        mDataStore.setBrightnessConfigurationForUser(null, 0, "packagename");
-        assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/));
+        mDataStore.setBrightnessConfigurationForUser(null, userSerial, "packagename");
+        assertNull(mDataStore.getBrightnessConfiguration(userSerial));
     }
 
     public class TestInjector extends PersistentDataStore.Injector {
diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
index 4d2d2f1..8e2c1f0 100644
--- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
@@ -150,8 +150,9 @@
         provider.setListener(listener);
 
         verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
-        final DeviceState[] expectedStates = new DeviceState[]{ new DeviceState(1, ""),
-                new DeviceState(2, "") };
+        final DeviceState[] expectedStates = new DeviceState[]{
+                new DeviceState(1, "", 0 /* flags */),
+                new DeviceState(2, "", 0 /* flags */) };
         assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue());
 
         verify(listener).onStateChanged(mIntegerCaptor.capture());
@@ -187,8 +188,9 @@
         provider.setListener(listener);
 
         verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
-        final DeviceState[] expectedStates = new DeviceState[]{ new DeviceState(1, ""),
-                new DeviceState(2, "CLOSED") };
+        final DeviceState[] expectedStates = new DeviceState[]{
+                new DeviceState(1, "", 0 /* flags */),
+                new DeviceState(2, "CLOSED", 0 /* flags */) };
         assertArrayEquals(expectedStates, mDeviceStateArrayCaptor.getValue());
 
         // onStateChanged() should not be called because the provider has not yet been notified of
@@ -264,8 +266,11 @@
 
         verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
         assertArrayEquals(
-                new DeviceState[]{ new DeviceState(1, "CLOSED"), new DeviceState(2, "HALF_OPENED"),
-                        new DeviceState(3, "OPENED") }, mDeviceStateArrayCaptor.getValue());
+                new DeviceState[]{
+                        new DeviceState(1, "CLOSED", 0 /* flags */),
+                        new DeviceState(2, "HALF_OPENED", 0 /* flags */),
+                        new DeviceState(3, "OPENED", 0 /* flags */) },
+                mDeviceStateArrayCaptor.getValue());
         // onStateChanged() should not be called because the provider has not yet been notified of
         // the initial sensor state.
         verify(listener, never()).onStateChanged(mIntegerCaptor.capture());
@@ -350,8 +355,10 @@
 
         verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
         assertArrayEquals(
-                new DeviceState[]{ new DeviceState(1, "CLOSED"), new DeviceState(2, "HALF_OPENED"),
-                        }, mDeviceStateArrayCaptor.getValue());
+                new DeviceState[]{
+                        new DeviceState(1, "CLOSED", 0 /* flags */),
+                        new DeviceState(2, "HALF_OPENED", 0 /* flags */)
+                }, mDeviceStateArrayCaptor.getValue());
         // onStateChanged() should be called because the provider could not find the sensor.
         verify(listener).onStateChanged(mIntegerCaptor.capture());
         assertEquals(1, mIntegerCaptor.getValue().intValue());
diff --git a/services/tests/servicestests/src/com/android/server/power/NotifierTest.java b/services/tests/servicestests/src/com/android/server/power/NotifierTest.java
index 5012ca9..6e3f754 100644
--- a/services/tests/servicestests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/NotifierTest.java
@@ -210,7 +210,7 @@
         @Override
         Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
                 SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
-                FaceDownDetector faceDownDetector) {
+                FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) {
             return mNotifierMock;
         }
 
@@ -298,6 +298,7 @@
                         BatteryStats.SERVICE_NAME)),
                 mInjector.createSuspendBlocker(mService, "testBlocker"),
                 null,
+                null,
                 null);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 5eabc1b..3d64d4b 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -215,7 +215,7 @@
             @Override
             Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
                     SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
-                    FaceDownDetector faceDownDetector) {
+                    FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) {
                 return mNotifierMock;
             }
 
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
index 61b5c2b..4cc4d55 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -83,7 +83,7 @@
 @Presubmit
 public class VibrationThreadTest {
 
-    private static final int TEST_TIMEOUT_MILLIS = 1_000;
+    private static final int TEST_TIMEOUT_MILLIS = 900;
     private static final int UID = Process.ROOT_UID;
     private static final int VIBRATOR_ID = 1;
     private static final String PACKAGE_NAME = "package";
diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
index 4b3771b..f21991d 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java
@@ -475,7 +475,7 @@
 
     @Test
     public void requestProjection_failsForBogusPackageName() throws Exception {
-        when(mPackageManager.getPackageUid(PACKAGE_NAME, 0))
+        when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
                 .thenReturn(TestInjector.CALLING_UID + 1);
 
         assertThrows(SecurityException.class, () -> mService.requestProjection(mBinder,
@@ -485,7 +485,7 @@
 
     @Test
     public void requestProjection_failsIfNameNotFound() throws Exception {
-        when(mPackageManager.getPackageUid(PACKAGE_NAME, 0))
+        when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
                 .thenThrow(new PackageManager.NameNotFoundException());
 
         assertThrows(SecurityException.class, () -> mService.requestProjection(mBinder,
@@ -495,7 +495,8 @@
 
     @Test
     public void requestProjection_failsIfNoProjectionTypes() throws Exception {
-        when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+        when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(TestInjector.CALLING_UID);
 
         assertThrows(IllegalArgumentException.class,
                 () -> mService.requestProjection(mBinder, PROJECTION_TYPE_NONE, PACKAGE_NAME));
@@ -507,7 +508,8 @@
 
     @Test
     public void requestProjection_failsIfMultipleProjectionTypes() throws Exception {
-        when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+        when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(TestInjector.CALLING_UID);
 
         // Don't use PROJECTION_TYPE_ALL because that's actually == -1 and will fail the > 0 check.
         int multipleProjectionTypes = PROJECTION_TYPE_AUTOMOTIVE | 0x0002 | 0x0004;
@@ -522,7 +524,8 @@
 
     @Test
     public void requestProjection_enforcesToggleAutomotiveProjectionPermission() throws Exception {
-        doThrow(new SecurityException()).when(mPackageManager).getPackageUid(PACKAGE_NAME, 0);
+        doThrow(new SecurityException())
+                .when(mPackageManager).getPackageUidAsUser(eq(PACKAGE_NAME), anyInt());
 
         assertThrows(SecurityException.class, () -> mService.requestProjection(mBinder,
                 PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME));
@@ -531,12 +534,14 @@
 
     @Test
     public void requestProjection_automotive_failsIfAlreadySetByOtherPackage() throws Exception {
-        when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+        when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(TestInjector.CALLING_UID);
         mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
         assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
 
         String otherPackage = "Raconteurs";
-        when(mPackageManager.getPackageUid(otherPackage, 0)).thenReturn(TestInjector.CALLING_UID);
+        when(mPackageManager.getPackageUidAsUser(eq(otherPackage), anyInt()))
+                .thenReturn(TestInjector.CALLING_UID);
         assertFalse(mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, otherPackage));
         assertThat(mService.getProjectingPackages(PROJECTION_TYPE_AUTOMOTIVE),
                 contains(PACKAGE_NAME));
@@ -544,7 +549,8 @@
 
     @Test
     public void requestProjection_failsIfCannotLinkToDeath() throws Exception {
-        when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+        when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(TestInjector.CALLING_UID);
         doThrow(new RemoteException()).when(mBinder).linkToDeath(any(), anyInt());
 
         assertFalse(mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME));
@@ -553,7 +559,8 @@
 
     @Test
     public void requestProjection() throws Exception {
-        when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+        when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(TestInjector.CALLING_UID);
         // Should work for all powers of two.
         for (int i = 0; i < Integer.SIZE; ++i) {
             int projectionType = 1 << i;
@@ -568,11 +575,12 @@
 
     @Test
     public void releaseProjection_failsForBogusPackageName() throws Exception {
-        when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+        when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(TestInjector.CALLING_UID);
         mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
         assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
 
-        when(mPackageManager.getPackageUid(PACKAGE_NAME, 0))
+        when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
                 .thenReturn(TestInjector.CALLING_UID + 1);
 
         assertThrows(SecurityException.class, () -> mService.releaseProjection(
@@ -582,10 +590,11 @@
 
     @Test
     public void releaseProjection_failsIfNameNotFound() throws Exception {
-        when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+        when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(TestInjector.CALLING_UID);
         mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
         assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
-        when(mPackageManager.getPackageUid(PACKAGE_NAME, 0))
+        when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
                 .thenThrow(new PackageManager.NameNotFoundException());
 
         assertThrows(SecurityException.class, () -> mService.releaseProjection(
@@ -595,7 +604,8 @@
 
     @Test
     public void releaseProjection_enforcesToggleAutomotiveProjectionPermission() throws Exception {
-        when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+        when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(TestInjector.CALLING_UID);
         mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
         assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
         doThrow(new SecurityException()).when(mContext).enforceCallingPermission(
@@ -613,7 +623,8 @@
 
     @Test
     public void releaseProjection() throws Exception {
-        when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+        when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(TestInjector.CALLING_UID);
         requestAllPossibleProjectionTypes();
         assertEquals(PROJECTION_TYPE_ALL, mService.getActiveProjectionTypes());
 
@@ -632,7 +643,8 @@
 
     @Test
     public void binderDeath_releasesProjection() throws Exception {
-        when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+        when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(TestInjector.CALLING_UID);
         requestAllPossibleProjectionTypes();
         assertEquals(PROJECTION_TYPE_ALL, mService.getActiveProjectionTypes());
         ArgumentCaptor<IBinder.DeathRecipient> deathRecipientCaptor = ArgumentCaptor.forClass(
@@ -647,7 +659,8 @@
     @Test
     public void getActiveProjectionTypes() throws Exception {
         assertEquals(PROJECTION_TYPE_NONE, mService.getActiveProjectionTypes());
-        when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+        when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(TestInjector.CALLING_UID);
         mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
         assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
         mService.releaseProjection(PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
@@ -657,7 +670,8 @@
     @Test
     public void getProjectingPackages() throws Exception {
         assertTrue(mService.getProjectingPackages(PROJECTION_TYPE_ALL).isEmpty());
-        when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+        when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(TestInjector.CALLING_UID);
         mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
         assertEquals(1, mService.getProjectingPackages(PROJECTION_TYPE_AUTOMOTIVE).size());
         assertEquals(1, mService.getProjectingPackages(PROJECTION_TYPE_ALL).size());
@@ -681,7 +695,8 @@
     @Test
     public void addOnProjectionStateChangedListener_callsListenerIfProjectionActive()
             throws Exception {
-        when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+        when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(TestInjector.CALLING_UID);
         mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
         assertEquals(PROJECTION_TYPE_AUTOMOTIVE, mService.getActiveProjectionTypes());
 
@@ -710,7 +725,8 @@
 
         mService.removeOnProjectionStateChangedListener(listener);
         // Now set automotive projection, should not call back.
-        when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+        when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(TestInjector.CALLING_UID);
         mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
         verify(listener, never()).onProjectionStateChanged(anyInt(), any());
     }
@@ -726,7 +742,8 @@
         verifyNoMoreInteractions(listener);
 
         // Now set automotive projection, should call back.
-        when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+        when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(TestInjector.CALLING_UID);
         mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
         verify(listener).onProjectionStateChanged(eq(PROJECTION_TYPE_AUTOMOTIVE),
                 eq(List.of(PACKAGE_NAME)));
@@ -752,8 +769,9 @@
         int fakeProjectionType = 0x0002;
         int otherFakeProjectionType = 0x0004;
         String otherPackageName = "Internet Arms";
-        when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
-        when(mPackageManager.getPackageUid(otherPackageName, 0))
+        when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(TestInjector.CALLING_UID);
+        when(mPackageManager.getPackageUidAsUser(eq(otherPackageName), anyInt()))
                 .thenReturn(TestInjector.CALLING_UID);
         IOnProjectionStateChangedListener listener = mock(IOnProjectionStateChangedListener.class);
         when(listener.asBinder()).thenReturn(mBinder); // Any binder will do.
@@ -806,7 +824,8 @@
 
         // Now kill the binder for the listener. This should remove it from the list of listeners.
         listenerDeathRecipient.getValue().binderDied();
-        when(mPackageManager.getPackageUid(PACKAGE_NAME, 0)).thenReturn(TestInjector.CALLING_UID);
+        when(mPackageManager.getPackageUidAsUser(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(TestInjector.CALLING_UID);
         mService.requestProjection(mBinder, PROJECTION_TYPE_AUTOMOTIVE, PACKAGE_NAME);
         verify(listener, never()).onProjectionStateChanged(anyInt(), any());
     }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 71c05b5..ea46eab 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -32,6 +32,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyInt;
@@ -72,6 +73,7 @@
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Slog;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.IAccessibilityManager;
@@ -1182,6 +1184,7 @@
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
 
         mService.buzzBeepBlinkLocked(r);
+        verifyDelayedVibrate(mService.getVibratorHelper().createFallbackVibration(false));
 
         // quiet update should stop making noise
         mService.buzzBeepBlinkLocked(s);
@@ -1564,6 +1567,32 @@
     }
 
     @Test
+    public void testRingtoneInsistentBeep_canUpdate() throws Exception {
+        NotificationChannel ringtoneChannel =
+                new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+        ringtoneChannel.setSound(Uri.fromParts("a", "b", "c"),
+                new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+        ringtoneChannel.enableVibration(true);
+        NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+        mService.addNotification(ringtoneNotification);
+        assertFalse(mService.shouldMuteNotificationLocked(ringtoneNotification));
+        mService.buzzBeepBlinkLocked(ringtoneNotification);
+        verifyBeepLooped();
+        verifyDelayedVibrateLooped();
+        Mockito.reset(mVibrator);
+        Mockito.reset(mRingtonePlayer);
+
+        assertFalse(mService.shouldMuteNotificationLocked(ringtoneNotification));
+        mService.buzzBeepBlinkLocked(ringtoneNotification);
+
+        // beep wasn't reset
+        verifyNeverBeep();
+        verifyNeverVibrate();
+        verify(mRingtonePlayer, never()).stopAsync();
+        verify(mVibrator, never()).cancel();
+    }
+
+    @Test
     public void testCannotInterruptRingtoneInsistentBuzz() {
         NotificationChannel ringtoneChannel =
                 new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index f57c416..f660af0 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -516,7 +516,7 @@
 
         when(mAssistants.isAdjustmentAllowed(anyString())).thenReturn(true);
 
-        mWorkerHandler = mService.new WorkerHandler(mTestableLooper.getLooper());
+        mWorkerHandler = spy(mService.new WorkerHandler(mTestableLooper.getLooper()));
         mService.init(mWorkerHandler, mRankingHandler, mPackageManager, mPackageManagerClient,
                 mockLightsManager, mListeners, mAssistants, mConditionProviders, mCompanionMgr,
                 mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager, mGroupHelper, mAm, mAtm,
@@ -2703,6 +2703,42 @@
     }
 
     @Test
+    public void testCrossUserSnooze() {
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 10);
+        mService.addNotification(r);
+        NotificationRecord r2 = generateNotificationRecord(mTestNotificationChannel, 0);
+        mService.addNotification(r2);
+
+        mListener = mock(ManagedServices.ManagedServiceInfo.class);
+        mListener.component = new ComponentName(PKG, PKG);
+        when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false);
+        when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
+
+        mService.snoozeNotificationInt(r.getKey(), 1000, null, mListener);
+
+        verify(mWorkerHandler, never()).post(
+                any(NotificationManagerService.SnoozeNotificationRunnable.class));
+    }
+
+    @Test
+    public void testSameUserSnooze() {
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 10);
+        mService.addNotification(r);
+        NotificationRecord r2 = generateNotificationRecord(mTestNotificationChannel, 0);
+        mService.addNotification(r2);
+
+        mListener = mock(ManagedServices.ManagedServiceInfo.class);
+        mListener.component = new ComponentName(PKG, PKG);
+        when(mListener.enabledAndUserMatches(anyInt())).thenReturn(true);
+        when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
+
+        mService.snoozeNotificationInt(r2.getKey(), 1000, null, mListener);
+
+        verify(mWorkerHandler).post(
+                any(NotificationManagerService.SnoozeNotificationRunnable.class));
+    }
+
+    @Test
     public void testSnoozeRunnable_reSnoozeASingleSnoozedNotification() throws Exception {
         final NotificationRecord notification = generateNotificationRecord(
                 mTestNotificationChannel, 1, null, true);
@@ -4793,6 +4829,52 @@
     }
 
     @Test
+    public void testSetNotificationsShownFromListener_protectsCrossUserInformation()
+            throws RemoteException {
+        Notification.Builder nb = new Notification.Builder(
+                mContext, mTestNotificationChannel.getId())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+                "tag" + System.currentTimeMillis(),  UserHandle.PER_USER_RANGE, 0,
+                nb.build(), UserHandle.getUserHandleForUid(mUid + UserHandle.PER_USER_RANGE),
+                null, 0);
+        final NotificationRecord r =
+                new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+        r.setTextChanged(true);
+        mService.addNotification(r);
+
+        // no security exception!
+        mBinderService.setNotificationsShownFromListener(null, new String[] {r.getKey()});
+
+        verify(mAppUsageStats, never()).reportInterruptiveNotification(
+                anyString(), anyString(), anyInt());
+    }
+
+    @Test
+    public void testCancelNotificationsFromListener_protectsCrossUserInformation()
+            throws RemoteException {
+        Notification.Builder nb = new Notification.Builder(
+                mContext, mTestNotificationChannel.getId())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+                "tag" + System.currentTimeMillis(),  UserHandle.PER_USER_RANGE, 0,
+                nb.build(), UserHandle.getUserHandleForUid(mUid + UserHandle.PER_USER_RANGE),
+                null, 0);
+        final NotificationRecord r =
+                new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+        r.setTextChanged(true);
+        mService.addNotification(r);
+
+        // no security exception!
+        mBinderService.cancelNotificationsFromListener(null, new String[] {r.getKey()});
+
+        waitForIdle();
+        assertEquals(1, mService.getNotificationRecordCount());
+    }
+
+    @Test
     public void testMaybeRecordInterruptionLocked_doesNotRecordTwice()
             throws RemoteException {
         final NotificationRecord r = generateNotificationRecord(
diff --git a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
index 222c692..6b36fe8 100644
--- a/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/SingleKeyGestureTests.java
@@ -69,7 +69,7 @@
 
     @Before
     public void setUp() {
-        mDetector = new SingleKeyGestureDetector(mContext);
+        mDetector = new SingleKeyGestureDetector();
         initSingleKeyGestureRules();
         mWaitTimeout = ViewConfiguration.getMultiPressTimeout() + 50;
         mLongPressTime = ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout() + 50;
@@ -78,7 +78,7 @@
     }
 
     private void initSingleKeyGestureRules() {
-        mDetector.addRule(new SingleKeyGestureDetector.SingleKeyRule(KEYCODE_POWER,
+        mDetector.addRule(new SingleKeyGestureDetector.SingleKeyRule(mContext, KEYCODE_POWER,
                 KEY_LONGPRESS | KEY_VERYLONGPRESS) {
             @Override
             int getMaxMultiPressCount() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index fa1f4ac..0b91802 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -27,6 +27,7 @@
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.argThat;
@@ -70,6 +71,7 @@
 @Presubmit
 @RunWith(WindowTestRunner.class)
 public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
+    private static final long TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5);
     private ActivityMetricsLogger mActivityMetricsLogger;
     private ActivityMetricsLogger.LaunchingState mLaunchingState;
     private ActivityMetricsLaunchObserver mLaunchObserver;
@@ -137,7 +139,7 @@
         // messages that are waiting for the lock.
         waitHandlerIdle(mAtm.mH);
         // AMLO callbacks happen on a separate thread than AML calls, so we need to use a timeout.
-        return verify(mock, timeout(TimeUnit.SECONDS.toMillis(5)));
+        return verify(mock, timeout(TIMEOUT_MS));
     }
 
     private void verifyOnActivityLaunchFinished(ActivityRecord activity) {
@@ -258,15 +260,40 @@
 
     @Test
     public void testOnActivityLaunchWhileSleeping() {
-        notifyActivityLaunching(mTopActivity.intent);
-        notifyActivityLaunched(START_SUCCESS, mTopActivity);
-        doReturn(true).when(mTopActivity.mDisplayContent).isSleeping();
-        mTopActivity.setState(ActivityRecord.State.RESUMED, "test");
-        mTopActivity.setVisibility(false);
+        notifyActivityLaunching(mTrampolineActivity.intent);
+        notifyActivityLaunched(START_SUCCESS, mTrampolineActivity);
+        doReturn(true).when(mTrampolineActivity.mDisplayContent).isSleeping();
+        mTrampolineActivity.setState(ActivityRecord.State.RESUMED, "test");
+        mTrampolineActivity.setVisibility(false);
         waitHandlerIdle(mAtm.mH);
         // Not cancel immediately because in one of real cases, the keyguard may be going away or
         // occluded later, then the activity can be drawn.
-        verify(mLaunchObserver, never()).onActivityLaunchCancelled(eqProto(mTopActivity));
+        verify(mLaunchObserver, never()).onActivityLaunchCancelled(eqProto(mTrampolineActivity));
+
+        clearInvocations(mLaunchObserver);
+        mLaunchTopByTrampoline = true;
+        mTopActivity.mVisibleRequested = false;
+        notifyActivityLaunching(mTopActivity.intent);
+        // It should schedule a message with UNKNOWN_VISIBILITY_CHECK_DELAY_MS to check whether
+        // the launch event is still valid.
+        notifyActivityLaunched(START_SUCCESS, mTopActivity);
+
+        // The posted message will acquire wm lock, so the test needs to release the lock to verify.
+        final Throwable error = awaitInWmLock(() -> {
+            try {
+                // Though the aborting target should be eqProto(mTopActivity), use any() to avoid
+                // any changes in proto that may cause failure by different arguments.
+                verify(mLaunchObserver, timeout(TIMEOUT_MS)).onActivityLaunchCancelled(any());
+            } catch (Throwable e) {
+                // Catch any errors including assertion because this runs in another thread.
+                return e;
+            }
+            return null;
+        });
+        // The launch event must be cancelled because the activity keeps invisible.
+        if (error != null) {
+            throw new AssertionError(error);
+        }
     }
 
     @Test
@@ -377,6 +404,7 @@
 
         // Another round without setting visibility of the trampoline activity.
         onActivityLaunchedTrampoline();
+        mTrampolineActivity.setState(ActivityRecord.State.PAUSING, "test");
         notifyWindowsDrawn(mTopActivity);
         // If the transition can start, the invisible activities should be discarded and the launch
         // event be reported successfully.
@@ -448,6 +476,16 @@
     }
 
     @Test
+    public void testConsecutiveLaunch() {
+        mTrampolineActivity.setState(ActivityRecord.State.INITIALIZING, "test");
+        onActivityLaunched(mTrampolineActivity);
+        mActivityMetricsLogger.notifyActivityLaunching(mTopActivity.intent,
+                mTrampolineActivity /* caller */, mTrampolineActivity.getUid());
+        notifyActivityLaunched(START_SUCCESS, mTopActivity);
+        transitToDrawnAndVerifyOnLaunchFinished(mTopActivity);
+    }
+
+    @Test
     public void testConsecutiveLaunchNewTask() {
         final IBinder launchCookie = mock(IBinder.class);
         final WindowContainerToken launchRootTask = mock(WindowContainerToken.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 488875b..9ad479a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -18,6 +18,7 @@
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
@@ -39,6 +40,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
 import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -145,6 +147,8 @@
 import org.mockito.invocation.InvocationOnMock;
 
 import java.util.ArrayList;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
 
 
 /**
@@ -215,7 +219,7 @@
     public void testNoCleanupMovingActivityInSameStack() {
         final ActivityRecord activity = createActivityWith2LevelTask();
         final Task rootTask = activity.getRootTask();
-        final Task newTask = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask).build();
+        final Task newTask = createTaskInRootTask(rootTask, 0 /* userId */);
         activity.reparent(newTask, 0, null /*reason*/);
         verify(rootTask, times(0)).cleanUpActivityReferences(any());
     }
@@ -1601,16 +1605,23 @@
     }
 
     @Test
-    public void testRemoveImmediately() throws RemoteException {
-        final ActivityRecord activity = createActivityWithTask();
-        final WindowProcessController wpc = activity.app;
-        activity.getTask().removeImmediately("test");
-
-        verify(mAtm.getLifecycleManager()).scheduleTransaction(any(), eq(activity.appToken),
-                isA(DestroyActivityItem.class));
-        assertNull(activity.app);
-        assertEquals(DESTROYED, activity.getState());
-        assertFalse(wpc.hasActivities());
+    public void testRemoveImmediately() {
+        final Consumer<Consumer<ActivityRecord>> test = setup -> {
+            final ActivityRecord activity = createActivityWithTask();
+            final WindowProcessController wpc = activity.app;
+            setup.accept(activity);
+            activity.getTask().removeImmediately("test");
+            try {
+                verify(mAtm.getLifecycleManager()).scheduleTransaction(any(), eq(activity.appToken),
+                        isA(DestroyActivityItem.class));
+            } catch (RemoteException ignored) {
+            }
+            assertNull(activity.app);
+            assertEquals(DESTROYED, activity.getState());
+            assertFalse(wpc.hasActivities());
+        };
+        test.accept(activity -> activity.setState(RESUMED, "test"));
+        test.accept(activity -> activity.finishing = true);
     }
 
     @Test
@@ -2499,16 +2510,19 @@
     @Test
     public void testTransferStartingWindow() {
         registerTestStartingWindowOrganizer();
-        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
-        final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true)
+                .setVisible(false).build();
+        final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true)
+                .setVisible(false).build();
         activity1.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
                 false, false);
         waitUntilHandlersIdle();
         activity2.addStartingWindow(mPackageName,
-                android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1.appToken.asBinder(),
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1,
                 true, true, false, true, false, false);
         waitUntilHandlersIdle();
+        assertFalse(mDisplayContent.mSkipAppTransitionAnimation);
         assertNoStartingWindow(activity1);
         assertHasStartingWindow(activity2);
     }
@@ -2523,7 +2537,7 @@
                     // Surprise, ...! Transfer window in the middle of the creation flow.
                     activity2.addStartingWindow(mPackageName,
                             android.R.style.Theme, null, "Test", 0, 0, 0, 0,
-                            activity1.appToken.asBinder(), true, true, false,
+                            activity1, true, true, false,
                             true, false, false);
                 });
         activity1.addStartingWindow(mPackageName,
@@ -2544,7 +2558,7 @@
                 false, false);
         waitUntilHandlersIdle();
         activity2.addStartingWindow(mPackageName,
-                android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1.appToken.asBinder(),
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1,
                 true, true, false, true, false, false);
         waitUntilHandlersIdle();
         assertNoStartingWindow(activity1);
@@ -2600,7 +2614,7 @@
         // Make mVisibleSetFromTransferredStartingWindow true.
         final ActivityRecord middle = new ActivityBuilder(mAtm).setTask(task).build();
         task.startActivityLocked(middle, null /* focusedTopActivity */,
-                false /* newTask */, false /* keepCurTransition */, null /* options */,
+                false /* newTask */, false /* isTaskSwitch */, null /* options */,
                 null /* sourceRecord */);
         middle.makeFinishingLocked();
 
@@ -2613,9 +2627,10 @@
         top.setVisible(false);
         // The finishing middle should be able to transfer starting window to top.
         task.startActivityLocked(top, null /* focusedTopActivity */,
-                false /* newTask */, false /* keepCurTransition */, null /* options */,
+                false /* newTask */, false /* isTaskSwitch */, null /* options */,
                 null /* sourceRecord */);
 
+        assertTrue(mDisplayContent.mSkipAppTransitionAnimation);
         assertNull(middle.mStartingWindow);
         assertHasStartingWindow(top);
         assertTrue(top.isVisible());
@@ -2650,7 +2665,7 @@
         // Make sure the fixed rotation transform linked to activity2 when adding starting window
         // on activity2.
         topActivity.addStartingWindow(mPackageName,
-                android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity.appToken.asBinder(),
+                android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity,
                 false, false, false, true, false, false);
         waitUntilHandlersIdle();
         assertTrue(topActivity.hasFixedRotationTransform());
@@ -2682,6 +2697,52 @@
     }
 
     @Test
+    public void testStartingWindowInTaskFragment() {
+        final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final WindowState startingWindow = createWindowState(
+                new WindowManager.LayoutParams(TYPE_APPLICATION_STARTING), activity1);
+        activity1.addWindow(startingWindow);
+        activity1.attachStartingWindow(startingWindow);
+        activity1.mStartingData = mock(StartingData.class);
+        final Task task = activity1.getTask();
+        final Rect taskBounds = task.getBounds();
+        final int width = taskBounds.width();
+        final int height = taskBounds.height();
+        final BiConsumer<TaskFragment, Rect> fragmentSetup = (fragment, bounds) -> {
+            final Configuration config = fragment.getRequestedOverrideConfiguration();
+            config.windowConfiguration.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+            config.windowConfiguration.setBounds(bounds);
+            fragment.onRequestedOverrideConfigurationChanged(config);
+        };
+
+        final TaskFragment taskFragment1 = new TaskFragment(
+                mAtm, null /* fragmentToken */, false /* createdByOrganizer */);
+        fragmentSetup.accept(taskFragment1, new Rect(0, 0, width / 2, height));
+        task.addChild(taskFragment1, POSITION_TOP);
+
+        final TaskFragment taskFragment2 = new TaskFragment(
+                mAtm, null /* fragmentToken */, false /* createdByOrganizer */);
+        fragmentSetup.accept(taskFragment2, new Rect(width / 2, 0, width, height));
+        task.addChild(taskFragment2, POSITION_TOP);
+        final ActivityRecord activity2 = new ActivityBuilder(mAtm).build();
+        activity2.mVisibleRequested = true;
+        taskFragment2.addChild(activity2);
+        activity1.reparent(taskFragment1, POSITION_TOP);
+
+        assertEquals(task, activity1.mStartingData.mAssociatedTask);
+        assertEquals(taskFragment1.getBounds(), activity1.getBounds());
+        // The activity was resized by task fragment, but starting window must still cover the task.
+        assertEquals(taskBounds, activity1.mStartingWindow.getBounds());
+
+        // The starting window is only removed when all embedded activities are drawn.
+        final WindowState activityWindow = mock(WindowState.class);
+        activity1.onFirstWindowDrawn(activityWindow);
+        assertNotNull(activity1.mStartingWindow);
+        activity2.onFirstWindowDrawn(activityWindow);
+        assertNull(activity1.mStartingWindow);
+    }
+
+    @Test
     public void testTransitionAnimationBounds() {
         removeGlobalMinSizeRestriction();
         final Task task = new TaskBuilder(mSupervisor)
@@ -2851,6 +2912,73 @@
         assertFalse(activity.mDisplayContent.mClosingApps.contains(activity));
     }
 
+    @Test
+    public void testImeInsetsFrozenFlag_resetWhenReparented() {
+        final ActivityRecord activity = createActivityWithTask();
+        final WindowState app = createWindow(null, TYPE_APPLICATION, activity, "app");
+        final WindowState imeWindow = createWindow(null, TYPE_APPLICATION, "imeWindow");
+        final Task newTask = new TaskBuilder(mSupervisor).build();
+        makeWindowVisible(app, imeWindow);
+        mDisplayContent.mInputMethodWindow = imeWindow;
+        mDisplayContent.setImeLayeringTarget(app);
+        mDisplayContent.setImeInputTarget(app);
+
+        // Simulate app is closing and expect the last IME is shown and IME insets is frozen.
+        app.mActivityRecord.commitVisibility(false, false);
+        assertTrue(app.mActivityRecord.mLastImeShown);
+        assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
+
+        // Expect IME insets frozen state will reset when the activity is reparent to the new task.
+        activity.setState(RESUMED, "test");
+        activity.reparent(newTask, 0 /* top */, "test");
+        assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
+    }
+
+    @UseTestDisplay(addWindows = W_INPUT_METHOD)
+    @Test
+    public void testImeInsetsFrozenFlag_resetWhenResized() {
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        makeWindowVisibleAndDrawn(app, mImeWindow);
+        mDisplayContent.setImeLayeringTarget(app);
+        mDisplayContent.setImeInputTarget(app);
+
+        // Simulate app is closing and expect the last IME is shown and IME insets is frozen.
+        app.mActivityRecord.commitVisibility(false, false);
+        assertTrue(app.mActivityRecord.mLastImeShown);
+        assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
+
+        // Expect IME insets frozen state will reset when the activity is reparent to the new task.
+        app.mActivityRecord.onResize();
+        assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
+    }
+
+    @UseTestDisplay(addWindows = W_INPUT_METHOD)
+    @Test
+    public void testImeInsetsFrozenFlag_resetWhenNoImeFocusableInActivity() {
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        makeWindowVisibleAndDrawn(app, mImeWindow);
+        mDisplayContent.setImeLayeringTarget(app);
+        mDisplayContent.setImeInputTarget(app);
+
+        // Simulate app is closing and expect the last IME is shown and IME insets is frozen.
+        app.mActivityRecord.commitVisibility(false, false);
+        app.mActivityRecord.onWindowsGone();
+
+        assertTrue(app.mActivityRecord.mLastImeShown);
+        assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
+
+        // Expect IME insets frozen state will reset when the activity has no IME focusable window.
+        app.mActivityRecord.forAllWindowsUnchecked(w -> {
+            w.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM;
+            return true;
+        }, true);
+
+        app.mActivityRecord.commitVisibility(true, false);
+        app.mActivityRecord.onWindowsVisible();
+
+        assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
+    }
+
     private void assertHasStartingWindow(ActivityRecord atoken) {
         assertNotNull(atoken.mStartingSurface);
         assertNotNull(atoken.mStartingData);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 2df9a8d..3e8a2e9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -32,6 +32,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
@@ -477,7 +478,7 @@
         final ActivityRecord splitSecondActivity =
                 new ActivityBuilder(mAtm).setCreateTask(true).build();
         final ActivityRecord splitPrimaryActivity = new TaskBuilder(mSupervisor)
-                .setParentTask(splitOrg.mPrimary)
+                .setParentTaskFragment(splitOrg.mPrimary)
                 .setCreateActivity(true)
                 .build()
                 .getTopMostActivity();
@@ -755,12 +756,12 @@
     }
 
     /**
-     * This test ensures that {@link ActivityStarter#setTargetStackAndMoveToFrontIfNeeded} will
-     * move the existing task to front if the current focused stack doesn't have running task.
+     * This test ensures that {@link ActivityStarter#setTargetRootTaskIfNeeded} will
+     * move the existing task to front if the current focused root task doesn't have running task.
      */
     @Test
-    public void testBringTaskToFrontWhenFocusedStackIsFinising() {
-        // Put 2 tasks in the same stack (simulate the behavior of home stack).
+    public void testBringTaskToFrontWhenFocusedTaskIsFinishing() {
+        // Put 2 tasks in the same root task (simulate the behavior of home root task).
         final Task rootTask = new TaskBuilder(mSupervisor).build();
         final ActivityRecord activity = new ActivityBuilder(mAtm)
                 .setParentTask(rootTask)
@@ -777,13 +778,16 @@
         assertEquals(finishingTopActivity, mRootWindowContainer.topRunningActivity());
         finishingTopActivity.finishing = true;
 
-        // Launch the bottom task of the target stack.
+        // Launch the bottom task of the target root task.
         prepareStarter(FLAG_ACTIVITY_NEW_TASK, false /* mockGetLaunchStack */)
-                .setReason("testBringTaskToFrontWhenTopStackIsFinising")
-                .setIntent(activity.intent)
+                .setReason("testBringTaskToFrontWhenFocusedTaskIsFinishing")
+                .setIntent(activity.intent.addFlags(
+                        FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
                 .execute();
+        verify(activity.getRootTask()).startActivityLocked(any(), any(), anyBoolean(),
+                eq(true) /* isTaskSwitch */, any(), any());
         // The hierarchies of the activity should move to front.
-        assertEquals(activity, mRootWindowContainer.topRunningActivity());
+        assertEquals(activity.getTask(), mRootWindowContainer.topRunningActivity().getTask());
     }
 
     /**
@@ -852,7 +856,7 @@
         // Create another activity on top of the secondary display.
         final Task topStack = secondaryTaskContainer.createRootTask(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final Task topTask = new TaskBuilder(mSupervisor).setParentTask(topStack).build();
+        final Task topTask = new TaskBuilder(mSupervisor).setParentTaskFragment(topStack).build();
         new ActivityBuilder(mAtm).setTask(topTask).build();
 
         doReturn(mActivityMetricsLogger).when(mSupervisor).getActivityMetricsLogger();
@@ -916,7 +920,7 @@
                 DEFAULT_COMPONENT_PACKAGE_NAME + ".SingleTaskActivity");
         final Task task = new TaskBuilder(mSupervisor)
                 .setComponent(componentName)
-                .setParentTask(stack)
+                .setParentTaskFragment(stack)
                 .build();
         return new ActivityBuilder(mAtm)
                 .setComponent(componentName)
@@ -1052,8 +1056,8 @@
         final ActivityStarter starter = prepareStarter(0 /* flags */);
         starter.mStartActivity = new ActivityBuilder(mAtm).build();
         final Task task = new TaskBuilder(mAtm.mTaskSupervisor)
-                .setParentTask(mAtm.mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
-                        WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */))
+                .setParentTaskFragment(createTask(mDisplayContent, WINDOWING_MODE_FULLSCREEN,
+                        ACTIVITY_TYPE_STANDARD))
                 .setUserId(10)
                 .build();
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
index 0d177c1..26a6882 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
@@ -49,6 +49,7 @@
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatchers;
 
 import java.util.concurrent.TimeUnit;
 
@@ -108,16 +109,18 @@
         final ActivityMetricsLogger.LaunchingState launchingState =
                 new ActivityMetricsLogger.LaunchingState();
         spyOn(launchingState);
-        doReturn(true).when(launchingState).contains(eq(secondActivity));
+        doReturn(true).when(launchingState).hasActiveTransitionInfo();
+        doReturn(true).when(launchingState).contains(
+                ArgumentMatchers.argThat(r -> r == firstActivity || r == secondActivity));
         // The test case already runs inside global lock, so above thread can only execute after
         // this waiting method that releases the lock.
         mSupervisor.waitActivityVisibleOrLaunched(taskToFrontWait, firstActivity, launchingState);
 
         // Assert that the thread is finished.
         assertTrue(condition.block(TIMEOUT_MS));
-        assertEquals(taskToFrontWait.result, START_TASK_TO_FRONT);
-        assertEquals(taskToFrontWait.who, secondActivity.mActivityComponent);
-        assertEquals(taskToFrontWait.launchState, WaitResult.LAUNCH_STATE_HOT);
+        assertEquals(START_TASK_TO_FRONT, taskToFrontWait.result);
+        assertEquals(secondActivity.mActivityComponent, taskToFrontWait.who);
+        assertEquals(WaitResult.LAUNCH_STATE_HOT, taskToFrontWait.launchState);
         // START_TASK_TO_FRONT means that another component will be visible, so the component
         // should not be assigned as the first activity.
         assertNull(launchedComponent[0]);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 3a6aac9..a165e1c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -48,7 +48,6 @@
 import android.view.RemoteAnimationTarget;
 import android.view.WindowManager;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
@@ -94,11 +93,10 @@
         assertEquals(WindowManager.TRANSIT_OLD_UNSET,
                 AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
                         mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
-                        null, null, false));
+                        mDisplayContent.mChangingContainers, null, null, false));
     }
 
     @Test
-    @FlakyTest(bugId = 131005232)
     public void testTranslucentOpen() {
         final ActivityRecord behind = createActivityRecord(mDisplayContent,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
@@ -112,12 +110,11 @@
 
         assertEquals(WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN,
                 AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
-                    mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
-                    null, null, false));
+                        mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+                        mDisplayContent.mChangingContainers, null, null, false));
     }
 
     @Test
-    @FlakyTest(bugId = 131005232)
     public void testTranslucentClose() {
         final ActivityRecord behind = createActivityRecord(mDisplayContent,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
@@ -129,11 +126,10 @@
         assertEquals(WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE,
                 AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
                         mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
-                        null, null, false));
+                        mDisplayContent.mChangingContainers, null, null, false));
     }
 
     @Test
-    @FlakyTest(bugId = 131005232)
     public void testChangeIsNotOverwritten() {
         final ActivityRecord behind = createActivityRecord(mDisplayContent,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
@@ -144,14 +140,14 @@
         mDisplayContent.prepareAppTransition(TRANSIT_CHANGE);
         mDisplayContent.mOpeningApps.add(behind);
         mDisplayContent.mOpeningApps.add(translucentOpening);
+        mDisplayContent.mChangingContainers.add(translucentOpening.getTask());
         assertEquals(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE,
                 AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
                         mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
-                        null, null, false));
+                        mDisplayContent.mChangingContainers, null, null, false));
     }
 
     @Test
-    @FlakyTest(bugId = 131005232)
     public void testTransitWithinTask() {
         final ActivityRecord opening = createActivityRecord(mDisplayContent,
                 WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
@@ -198,7 +194,7 @@
         assertEquals(WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN,
                 AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
                         mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
-                        appWindowClosing, null, false));
+                        mDisplayContent.mChangingContainers, appWindowClosing, null, false));
     }
 
     @Test
@@ -229,7 +225,7 @@
         assertEquals(WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN,
                 AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
                         mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
-                        appWindowClosing, null, false));
+                        mDisplayContent.mChangingContainers, appWindowClosing, null, false));
     }
 
     @Test
@@ -375,7 +371,7 @@
         final ArraySet<ActivityRecord> closing = new ArraySet<>();
         closing.add(activity3);
 
-        // Promote animation targets to TaskStack level. Invisible ActivityRecords don't affect
+        // Promote animation targets to root Task level. Invisible ActivityRecords don't affect
         // promotion decision.
         assertEquals(
                 new ArraySet<>(new WindowContainer[]{activity1.getRootTask()}),
@@ -520,6 +516,128 @@
                         opening, closing, false /* visible */));
     }
 
+    @Test
+    public void testGetAnimationTargets_openingClosingTaskFragment() {
+        // [DefaultTDA] - [Task] -+- [TaskFragment1] - [ActivityRecord1] (opening, invisible)
+        //                        +- [TaskFragment2] - [ActivityRecord2] (closing, visible)
+        final Task parentTask = createTask(mDisplayContent);
+        final TaskFragment taskFragment1 = createTaskFragmentWithParentTask(parentTask,
+                false /* createEmbeddedTask */);
+        final ActivityRecord activity1 = taskFragment1.getTopMostActivity();
+        activity1.setVisible(false);
+        activity1.mVisibleRequested = true;
+
+        final TaskFragment taskFragment2 = createTaskFragmentWithParentTask(parentTask,
+                false /* createEmbeddedTask */);
+        final ActivityRecord activity2 = taskFragment2.getTopMostActivity();
+        activity2.setVisible(true);
+        activity2.mVisibleRequested = false;
+
+        final ArraySet<ActivityRecord> opening = new ArraySet<>();
+        opening.add(activity1);
+        final ArraySet<ActivityRecord> closing = new ArraySet<>();
+        closing.add(activity2);
+
+        // Promote animation targets up to TaskFragment level, not beyond.
+        assertEquals(new ArraySet<>(new WindowContainer[]{taskFragment1}),
+                AppTransitionController.getAnimationTargets(
+                        opening, closing, true /* visible */));
+        assertEquals(new ArraySet<>(new WindowContainer[]{taskFragment2}),
+                AppTransitionController.getAnimationTargets(
+                        opening, closing, false /* visible */));
+    }
+
+    @Test
+    public void testGetAnimationTargets_openingClosingTaskFragmentWithEmbeddedTask() {
+        // [DefaultTDA] - [Task] -+- [TaskFragment1] - [ActivityRecord1] (opening, invisible)
+        //                        +- [TaskFragment2] - [ActivityRecord2] (closing, visible)
+        final Task parentTask = createTask(mDisplayContent);
+        final TaskFragment taskFragment1 = createTaskFragmentWithParentTask(parentTask,
+                true /* createEmbeddedTask */);
+        final ActivityRecord activity1 = taskFragment1.getTopMostActivity();
+        activity1.setVisible(false);
+        activity1.mVisibleRequested = true;
+
+        final TaskFragment taskFragment2 = createTaskFragmentWithParentTask(parentTask,
+                true /* createEmbeddedTask */);
+        final ActivityRecord activity2 = taskFragment2.getTopMostActivity();
+        activity2.setVisible(true);
+        activity2.mVisibleRequested = false;
+
+        final ArraySet<ActivityRecord> opening = new ArraySet<>();
+        opening.add(activity1);
+        final ArraySet<ActivityRecord> closing = new ArraySet<>();
+        closing.add(activity2);
+
+        // Promote animation targets up to TaskFragment level, not beyond.
+        assertEquals(new ArraySet<>(new WindowContainer[]{taskFragment1}),
+                AppTransitionController.getAnimationTargets(
+                        opening, closing, true /* visible */));
+        assertEquals(new ArraySet<>(new WindowContainer[]{taskFragment2}),
+                AppTransitionController.getAnimationTargets(
+                        opening, closing, false /* visible */));
+    }
+
+    @Test
+    public void testGetAnimationTargets_openingTheOnlyTaskFragmentInTask() {
+        // [DefaultTDA] -+- [Task1] - [TaskFragment1] - [ActivityRecord1] (opening, invisible)
+        //               +- [Task2] - [ActivityRecord2] (closing, visible)
+        final Task task1 = createTask(mDisplayContent);
+        final TaskFragment taskFragment1 = createTaskFragmentWithParentTask(task1,
+                false /* createEmbeddedTask */);
+        final ActivityRecord activity1 = taskFragment1.getTopMostActivity();
+        activity1.setVisible(false);
+        activity1.mVisibleRequested = true;
+
+        final ActivityRecord activity2 = createActivityRecord(mDisplayContent);
+        activity2.setVisible(true);
+        activity2.mVisibleRequested = false;
+
+        final ArraySet<ActivityRecord> opening = new ArraySet<>();
+        opening.add(activity1);
+        final ArraySet<ActivityRecord> closing = new ArraySet<>();
+        closing.add(activity2);
+
+        // Promote animation targets up to leaf Task level because there's only one TaskFragment in
+        // the Task.
+        assertEquals(new ArraySet<>(new WindowContainer[]{task1}),
+                AppTransitionController.getAnimationTargets(
+                        opening, closing, true /* visible */));
+        assertEquals(new ArraySet<>(new WindowContainer[]{activity2.getTask()}),
+                AppTransitionController.getAnimationTargets(
+                        opening, closing, false /* visible */));
+    }
+
+    @Test
+    public void testGetAnimationTargets_closingTheOnlyTaskFragmentInTask() {
+        // [DefaultTDA] -+- [Task1] - [TaskFragment1] - [ActivityRecord1] (closing, visible)
+        //               +- [Task2] - [ActivityRecord2] (opening, invisible)
+        final Task task1 = createTask(mDisplayContent);
+        final TaskFragment taskFragment1 = createTaskFragmentWithParentTask(task1,
+                false /* createEmbeddedTask */);
+        final ActivityRecord activity1 = taskFragment1.getTopMostActivity();
+        activity1.setVisible(true);
+        activity1.mVisibleRequested = false;
+
+        final ActivityRecord activity2 = createActivityRecord(mDisplayContent);
+        activity2.setVisible(false);
+        activity2.mVisibleRequested = true;
+
+        final ArraySet<ActivityRecord> opening = new ArraySet<>();
+        opening.add(activity2);
+        final ArraySet<ActivityRecord> closing = new ArraySet<>();
+        closing.add(activity1);
+
+        // Promote animation targets up to leaf Task level because there's only one TaskFragment in
+        // the Task.
+        assertEquals(new ArraySet<>(new WindowContainer[]{activity2.getTask()}),
+                AppTransitionController.getAnimationTargets(
+                        opening, closing, true /* visible */));
+        assertEquals(new ArraySet<>(new WindowContainer[]{task1}),
+                AppTransitionController.getAnimationTargets(
+                        opening, closing, false /* visible */));
+    }
+
     static class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
         @Override
         public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index c3279bf..a0a3ce7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -18,11 +18,16 @@
 
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
 import static android.view.WindowManager.TRANSIT_OLD_UNSET;
 import static android.view.WindowManager.TRANSIT_OPEN;
 
@@ -30,6 +35,7 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -37,9 +43,11 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 
+import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
+import android.util.ArraySet;
 import android.view.Display;
 import android.view.IRemoteAnimationFinishedCallback;
 import android.view.IRemoteAnimationRunner;
@@ -82,8 +90,8 @@
         assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY,
                 AppTransitionController.getTransitCompatType(mDc.mAppTransition,
                         mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
-                        null /* wallpaperTarget */, null /* oldWallpaper */,
-                        false /*skipAppTransitionAnimation*/));
+                        mDisplayContent.mChangingContainers, null /* wallpaperTarget */,
+                        null /* oldWallpaper */, false /*skipAppTransitionAnimation*/));
     }
 
     @Test
@@ -97,8 +105,8 @@
         assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY,
                 AppTransitionController.getTransitCompatType(mDc.mAppTransition,
                         mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
-                        null /* wallpaperTarget */, null /* oldWallpaper */,
-                        false /*skipAppTransitionAnimation*/));
+                        mDisplayContent.mChangingContainers, null /* wallpaperTarget */,
+                        null /* oldWallpaper */, false /*skipAppTransitionAnimation*/));
     }
 
     @Test
@@ -112,8 +120,8 @@
         assertEquals(TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE,
                 AppTransitionController.getTransitCompatType(mDc.mAppTransition,
                         mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
-                        null /* wallpaperTarget */, null /* oldWallpaper */,
-                        false /*skipAppTransitionAnimation*/));
+                        mDisplayContent.mChangingContainers, null /* wallpaperTarget */,
+                        null /* oldWallpaper */, false /*skipAppTransitionAnimation*/));
     }
 
     @Test
@@ -127,8 +135,8 @@
         assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY,
                 AppTransitionController.getTransitCompatType(mDc.mAppTransition,
                         mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
-                        null /* wallpaperTarget */, null /* oldWallpaper */,
-                        false /*skipAppTransitionAnimation*/));
+                        mDisplayContent.mChangingContainers, null /* wallpaperTarget */,
+                        null /* oldWallpaper */, false /*skipAppTransitionAnimation*/));
     }
 
     @Test
@@ -142,8 +150,129 @@
         assertEquals(TRANSIT_OLD_UNSET,
                 AppTransitionController.getTransitCompatType(mDc.mAppTransition,
                         mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
-                        null /* wallpaperTarget */, null /* oldWallpaper */,
-                        true /*skipAppTransitionAnimation*/));
+                        mDisplayContent.mChangingContainers, null /* wallpaperTarget */,
+                        null /* oldWallpaper */, true /*skipAppTransitionAnimation*/));
+    }
+
+    @Test
+    public void testTaskChangeWindowingMode() {
+        final ActivityRecord activity = createActivityRecord(mDc);
+
+        mDc.prepareAppTransition(TRANSIT_OPEN);
+        mDc.prepareAppTransition(TRANSIT_CHANGE);
+        mDc.mOpeningApps.add(activity); // Make sure TRANSIT_CHANGE has the priority
+        mDc.mChangingContainers.add(activity.getTask());
+
+        assertEquals(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE,
+                AppTransitionController.getTransitCompatType(mDc.mAppTransition,
+                        mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+                        mDisplayContent.mChangingContainers, null /* wallpaperTarget */,
+                        null /* oldWallpaper */, false /*skipAppTransitionAnimation*/));
+    }
+
+    @Test
+    public void testTaskFragmentChange() {
+        final ActivityRecord activity = createActivityRecord(mDc);
+        final TaskFragment taskFragment = new TaskFragment(mAtm, new Binder(),
+                true /* createdByOrganizer */, true /* isEmbedded */);
+        activity.getTask().addChild(taskFragment, POSITION_TOP);
+        activity.reparent(taskFragment, POSITION_TOP);
+
+        mDc.prepareAppTransition(TRANSIT_OPEN);
+        mDc.prepareAppTransition(TRANSIT_CHANGE);
+        mDc.mOpeningApps.add(activity); // Make sure TRANSIT_CHANGE has the priority
+        mDc.mChangingContainers.add(taskFragment);
+
+        assertEquals(TRANSIT_OLD_TASK_FRAGMENT_CHANGE,
+                AppTransitionController.getTransitCompatType(mDc.mAppTransition,
+                        mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+                        mDisplayContent.mChangingContainers, null /* wallpaperTarget */,
+                        null /* oldWallpaper */, false /*skipAppTransitionAnimation*/));
+    }
+
+    @Test
+    public void testTaskFragmentOpeningTransition() {
+        final ActivityRecord activity = createHierarchyForTaskFragmentTest(
+                false /* createEmbeddedTask */);
+        activity.setVisible(false);
+
+        mDisplayContent.prepareAppTransition(TRANSIT_OPEN);
+        mDisplayContent.mOpeningApps.add(activity);
+        assertEquals(TRANSIT_OLD_TASK_FRAGMENT_OPEN,
+                AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
+                        mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+                        mDisplayContent.mChangingContainers, null /* wallpaperTarget */,
+                        null /* oldWallpaper */, false /* skipAppTransitionAnimation */));
+    }
+
+    @Test
+    public void testEmbeddedTaskOpeningTransition() {
+        final ActivityRecord activity = createHierarchyForTaskFragmentTest(
+                true /* createEmbeddedTask */);
+        activity.setVisible(false);
+
+        mDisplayContent.prepareAppTransition(TRANSIT_OPEN);
+        mDisplayContent.mOpeningApps.add(activity);
+        assertEquals(TRANSIT_OLD_TASK_FRAGMENT_OPEN,
+                AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
+                        mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+                        mDisplayContent.mChangingContainers, null /* wallpaperTarget */,
+                        null /* oldWallpaper */, false /* skipAppTransitionAnimation */));
+    }
+
+    @Test
+    public void testTaskFragmentClosingTransition() {
+        final ActivityRecord activity = createHierarchyForTaskFragmentTest(
+                false /* createEmbeddedTask */);
+        activity.setVisible(true);
+
+        mDisplayContent.prepareAppTransition(TRANSIT_CLOSE);
+        mDisplayContent.mClosingApps.add(activity);
+        assertEquals(TRANSIT_OLD_TASK_FRAGMENT_CLOSE,
+                AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
+                        mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+                        mDisplayContent.mChangingContainers, null /* wallpaperTarget */,
+                        null /* oldWallpaper */, false /* skipAppTransitionAnimation */));
+    }
+
+    @Test
+    public void testEmbeddedTaskClosingTransition() {
+        final ActivityRecord activity = createHierarchyForTaskFragmentTest(
+                true /* createEmbeddedTask */);
+        activity.setVisible(true);
+
+        mDisplayContent.prepareAppTransition(TRANSIT_CLOSE);
+        mDisplayContent.mClosingApps.add(activity);
+        assertEquals(TRANSIT_OLD_TASK_FRAGMENT_CLOSE,
+                AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
+                        mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+                        mDisplayContent.mChangingContainers, null /* wallpaperTarget */,
+                        null /* oldWallpaper */, false /* skipAppTransitionAnimation */));
+    }
+
+    /**
+     * Creates a {@link Task} with two {@link TaskFragment TaskFragments}.
+     * The bottom TaskFragment is to prevent
+     * {@link AppTransitionController#getAnimationTargets(ArraySet, ArraySet, boolean) the animation
+     * target} to promote to Task or above.
+     *
+     * @param createEmbeddedTask {@code true} to create embedded Task for verified TaskFragment
+     * @return The Activity to be put in either opening or closing Activity
+     */
+    private ActivityRecord createHierarchyForTaskFragmentTest(boolean createEmbeddedTask) {
+        final Task parentTask = createTask(mDisplayContent);
+        final TaskFragment bottomTaskFragment = createTaskFragmentWithParentTask(parentTask,
+                false /* createEmbeddedTask */);
+        final ActivityRecord bottomActivity = bottomTaskFragment.getTopMostActivity();
+        bottomActivity.setOccludesParent(true);
+        bottomActivity.setVisible(true);
+
+        final TaskFragment verifiedTaskFragment = createTaskFragmentWithParentTask(parentTask,
+                createEmbeddedTask);
+        final ActivityRecord activity = verifiedTaskFragment.getTopMostActivity();
+        activity.setOccludesParent(true);
+
+        return activity;
     }
 
     @Test
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 1191035..14dc33e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -50,6 +50,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
 import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
@@ -66,6 +67,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.same;
@@ -106,9 +108,12 @@
 import android.app.servertransaction.FixedRotationAdjustmentsItem;
 import android.content.res.Configuration;
 import android.graphics.Insets;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.metrics.LogMaker;
+import android.os.Binder;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
@@ -134,6 +139,7 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
+import com.android.server.LocalServices;
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.wm.utils.WmDisplayCutout;
 
@@ -141,6 +147,8 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -1300,7 +1308,7 @@
     }
 
     @UseTestDisplay(addWindows = { W_ACTIVITY, W_WALLPAPER, W_STATUS_BAR, W_NAVIGATION_BAR,
-            W_NOTIFICATION_SHADE })
+            W_INPUT_METHOD, W_NOTIFICATION_SHADE })
     @Test
     public void testApplyTopFixedRotationTransform() {
         final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
@@ -1404,6 +1412,14 @@
         assertEquals("The process should receive rotated configuration for compatibility",
                 expectedProcConfig, app2.app.getConfiguration());
 
+        // If the rotated activity requests to show IME, the IME window should use the
+        // transformation from activity to lay out in the same orientation.
+        mDisplayContent.setImeLayeringTarget(mAppWindow);
+        LocalServices.getService(WindowManagerInternal.class).onToggleImeRequested(true /* show */,
+                app.token, app.token, mDisplayContent.mDisplayId);
+        assertTrue(mImeWindow.mToken.hasFixedRotationTransform());
+        assertTrue(mImeWindow.isAnimating(PARENTS, ANIMATION_TYPE_FIXED_TRANSFORM));
+
         // The fixed rotation transform can only be finished when all animation finished.
         doReturn(false).when(app2).isAnimating(anyInt(), anyInt());
         mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app2.token);
@@ -2191,6 +2207,141 @@
         assertNotEquals(imeChildWindow, mDisplayContent.findFocusedWindow());
     }
 
+    @UseTestDisplay(addWindows = W_INPUT_METHOD)
+    @Test
+    public void testImeMenuDialogFocusWhenImeLayeringTargetChanges() {
+        final WindowState imeMenuDialog =
+                createWindow(mImeWindow, TYPE_INPUT_METHOD_DIALOG, "imeMenuDialog");
+        makeWindowVisibleAndDrawn(imeMenuDialog, mImeWindow);
+        assertTrue(imeMenuDialog.canReceiveKeys());
+        mDisplayContent.setInputMethodWindowLocked(mImeWindow);
+
+        // Verify imeMenuDialog can be focused window if the next IME target requests IME visible.
+        final WindowState imeAppTarget =
+                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+        mDisplayContent.setImeLayeringTarget(imeAppTarget);
+        spyOn(imeAppTarget);
+        doReturn(true).when(imeAppTarget).getRequestedVisibility(ITYPE_IME);
+        assertEquals(imeMenuDialog, mDisplayContent.findFocusedWindow());
+
+        // Verify imeMenuDialog doesn't be focused window if the next IME target does not
+        // request IME visible.
+        final WindowState nextImeAppTarget =
+                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "nextImeAppTarget");
+        spyOn(nextImeAppTarget);
+        doReturn(true).when(nextImeAppTarget).isAnimating(PARENTS | TRANSITION,
+                ANIMATION_TYPE_APP_TRANSITION);
+        mDisplayContent.setImeLayeringTarget(nextImeAppTarget);
+        assertNotEquals(imeMenuDialog, mDisplayContent.findFocusedWindow());
+    }
+
+    @Test
+    public void testVirtualDisplayContent() {
+        MockitoSession mockSession = mockitoSession()
+                .initMocks(this)
+                .spyStatic(SurfaceControl.class)
+                .strictness(Strictness.LENIENT)
+                .startMocking();
+
+        // GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
+        // mirror.
+        final IBinder tokenToMirror = setUpDefaultTaskDisplayAreaWindowToken();
+
+        // GIVEN SurfaceControl can successfully mirror the provided surface.
+        Point surfaceSize = new Point(
+                mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().width(),
+                mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().height());
+        surfaceControlMirrors(surfaceSize);
+
+        // WHEN creating the DisplayContent for a new virtual display.
+        final DisplayContent virtualDisplay = new TestDisplayContent.Builder(mAtm,
+                mDisplayInfo).build();
+
+        // THEN mirroring is initiated for the default display's DisplayArea.
+        assertThat(virtualDisplay.mTokenToMirror).isEqualTo(tokenToMirror);
+
+        mockSession.finishMocking();
+    }
+
+    @Test
+    public void testVirtualDisplayContent_capturedAreaResized() {
+        MockitoSession mockSession = mockitoSession()
+                .initMocks(this)
+                .spyStatic(SurfaceControl.class)
+                .strictness(Strictness.LENIENT)
+                .startMocking();
+
+        // GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
+        // mirror.
+        final IBinder tokenToMirror = setUpDefaultTaskDisplayAreaWindowToken();
+
+        // GIVEN SurfaceControl can successfully mirror the provided surface.
+        Point surfaceSize = new Point(
+                mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().width(),
+                mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().height());
+        SurfaceControl mirroredSurface = surfaceControlMirrors(surfaceSize);
+
+        // WHEN creating the DisplayContent for a new virtual display.
+        final DisplayContent virtualDisplay = new TestDisplayContent.Builder(mAtm,
+                mDisplayInfo).build();
+
+        // THEN mirroring is initiated for the default display's DisplayArea.
+        assertThat(virtualDisplay.mTokenToMirror).isEqualTo(tokenToMirror);
+
+        float xScale = 0.7f;
+        float yScale = 2f;
+        Rect displayAreaBounds = new Rect(0, 0, Math.round(surfaceSize.x * xScale),
+                Math.round(surfaceSize.y * yScale));
+        virtualDisplay.updateMirroredSurface(mTransaction, displayAreaBounds);
+
+        // THEN content in the captured DisplayArea is scaled to fit the surface size.
+        verify(mTransaction, atLeastOnce()).setMatrix(mirroredSurface, 1.0f / yScale, 0, 0,
+                1.0f / yScale);
+        // THEN captured content is positioned in the centre of the output surface.
+        float scaledWidth = displayAreaBounds.width() / xScale;
+        float xInset = (surfaceSize.x - scaledWidth) / 2;
+        verify(mTransaction, atLeastOnce()).setPosition(mirroredSurface, xInset, 0);
+
+        mockSession.finishMocking();
+    }
+
+    private class TestToken extends Binder {
+    }
+
+    /**
+     * Creates a WindowToken associated with the default task DisplayArea, in order for that
+     * DisplayArea to be mirrored.
+     */
+    private IBinder setUpDefaultTaskDisplayAreaWindowToken() {
+        // GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
+        // mirror.
+        final IBinder tokenToMirror = new TestToken();
+        doReturn(tokenToMirror).when(mWm.mDisplayManagerInternal).getWindowTokenClientToMirror(
+                anyInt());
+
+        // GIVEN the default task display area is represented by the WindowToken.
+        spyOn(mWm.mWindowContextListenerController);
+        doReturn(mDefaultDisplay.getDefaultTaskDisplayArea()).when(
+                mWm.mWindowContextListenerController).getContainer(any());
+        return tokenToMirror;
+    }
+
+    /**
+     * SurfaceControl successfully creates a mirrored surface of the given size.
+     */
+    private SurfaceControl surfaceControlMirrors(Point surfaceSize) {
+        // Do not set the parent, since the mirrored surface is the root of a new surface hierarchy.
+        SurfaceControl mirroredSurface = new SurfaceControl.Builder()
+                .setName("mirroredSurface")
+                .setBufferSize(surfaceSize.x, surfaceSize.y)
+                .setCallsite("mirrorSurface")
+                .build();
+        doReturn(mirroredSurface).when(() -> SurfaceControl.mirrorSurface(any()));
+        doReturn(surfaceSize).when(mWm.mDisplayManagerInternal).getDisplaySurfaceDefaultSize(
+                anyInt());
+        return mirroredSurface;
+    }
+
     private void removeRootTaskTests(Runnable runnable) {
         final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
         final Task rootTask1 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
@@ -2201,10 +2352,10 @@
                 ACTIVITY_TYPE_STANDARD, ON_TOP);
         final Task rootTask4 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, ON_TOP);
-        final Task task1 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask1).build();
-        final Task task2 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask2).build();
-        final Task task3 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask3).build();
-        final Task task4 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(rootTask4).build();
+        final Task task1 = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask1).build();
+        final Task task2 = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask2).build();
+        final Task task3 = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask3).build();
+        final Task task4 = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask4).build();
 
         // Reordering root tasks while removing root tasks.
         doAnswer(invocation -> {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 3c7c4fd..4957ab9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -115,7 +115,7 @@
         spyOn(mNavBarWindow);
 
         // Disabling this call for most tests since it can override the systemUiFlags when called.
-        doReturn(false).when(mDisplayPolicy).updateSystemUiVisibilityLw();
+        doNothing().when(mDisplayPolicy).updateSystemBarAttributes();
 
         updateDisplayFrames();
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index 7cb7c79d..8a6db2c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -117,7 +117,7 @@
         Task rootTask = mTestDisplay.getDefaultTaskDisplayArea()
                 .createRootTask(TEST_WINDOWING_MODE, ACTIVITY_TYPE_STANDARD, /* onTop */ true);
         mTestTask = new TaskBuilder(mSupervisor).setComponent(TEST_COMPONENT)
-                .setParentTask(rootTask).build();
+                .setParentTaskFragment(rootTask).build();
         mTestTask.mUserId = TEST_USER_ID;
         mTestTask.mLastNonFullscreenBounds = TEST_BOUNDS;
         mTestTask.setHasBeenVisible(true);
@@ -353,7 +353,7 @@
         final Task anotherTaskOfTheSameUser = new TaskBuilder(mSupervisor)
                 .setComponent(ALTERNATIVE_COMPONENT)
                 .setUserId(TEST_USER_ID)
-                .setParentTask(stack)
+                .setParentTaskFragment(stack)
                 .build();
         anotherTaskOfTheSameUser.setWindowingMode(WINDOWING_MODE_FREEFORM);
         anotherTaskOfTheSameUser.setBounds(200, 300, 400, 500);
@@ -365,7 +365,7 @@
         final Task anotherTaskOfDifferentUser = new TaskBuilder(mSupervisor)
                 .setComponent(TEST_COMPONENT)
                 .setUserId(ALTERNATIVE_USER_ID)
-                .setParentTask(stack)
+                .setParentTaskFragment(stack)
                 .build();
         anotherTaskOfDifferentUser.setWindowingMode(WINDOWING_MODE_FREEFORM);
         anotherTaskOfDifferentUser.setBounds(300, 400, 500, 600);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index e3c38b0..22e687a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -331,7 +331,7 @@
         // other task
         Task task1 = createTaskBuilder(".Task1")
                 .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
-                .setParentTask(mTaskContainer.getRootHomeTask()).build();
+                .setParentTaskFragment(mTaskContainer.getRootHomeTask()).build();
         Task task2 = createTaskBuilder(".Task1")
                 .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
                 .build();
@@ -471,8 +471,8 @@
         final Task root = createTaskBuilder(".CreatedByOrganizerRoot").build();
         root.mCreatedByOrganizer = true;
         // Add organized and non-organized child.
-        final Task child1 = createTaskBuilder(".Task1").setParentTask(root).build();
-        final Task child2 = createTaskBuilder(".Task2").setParentTask(root).build();
+        final Task child1 = createTaskBuilder(".Task1").setParentTaskFragment(root).build();
+        final Task child2 = createTaskBuilder(".Task2").setParentTaskFragment(root).build();
         doReturn(true).when(child1).isOrganized();
         doReturn(false).when(child2).isOrganized();
         mRecentTasks.add(root);
@@ -508,7 +508,8 @@
         // tasks because their intents are identical.
         mRecentTasks.add(task1);
         // Go home to trigger the removal of untracked tasks.
-        mRecentTasks.add(createTaskBuilder(".Home").setParentTask(mTaskContainer.getRootHomeTask())
+        mRecentTasks.add(createTaskBuilder(".Home")
+                .setParentTaskFragment(mTaskContainer.getRootHomeTask())
                 .build());
         triggerIdleToTrim();
 
@@ -675,7 +676,7 @@
     public void testVisibleTasks_excludedFromRecents_firstTaskNotVisible() {
         // Create some set of tasks, some of which are visible and some are not
         Task homeTask = createTaskBuilder("com.android.pkg1", ".HomeTask")
-                .setParentTask(mTaskContainer.getRootHomeTask())
+                .setParentTaskFragment(mTaskContainer.getRootHomeTask())
                 .build();
         homeTask.mUserSetupComplete = true;
         mRecentTasks.add(homeTask);
@@ -696,7 +697,7 @@
         t1.mUserSetupComplete = true;
         mRecentTasks.add(t1);
         Task homeTask = createTaskBuilder("com.android.pkg1", ".HomeTask")
-                .setParentTask(mTaskContainer.getRootHomeTask())
+                .setParentTaskFragment(mTaskContainer.getRootHomeTask())
                 .build();
         homeTask.mUserSetupComplete = true;
         mRecentTasks.add(homeTask);
@@ -788,6 +789,19 @@
     }
 
     @Test
+    public void testVisibleEmbeddedTask_expectNotVisible() {
+        Task task = createTaskBuilder(".Task")
+                .setFlags(FLAG_ACTIVITY_NEW_TASK)
+                .build();
+        doReturn(true).when(task).isEmbedded();
+        mRecentTasks.add(task);
+
+        assertThat(mCallbacksRecorder.mAdded).hasSize(1);
+        assertFalse("embedded task should not be visible recents",
+                mRecentTasks.isVisibleRecentTask(task));
+    }
+
+    @Test
     public void testFreezeTaskListOrder_reorderExistingTask() {
         // Add some tasks
         mRecentTasks.add(mTasks.get(0));
@@ -936,10 +950,10 @@
 
         // Add a number of tasks (beyond the max) but ensure that nothing is trimmed because all
         // the tasks belong in stacks above the home stack
-        mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTask(homeStack).build());
-        mRecentTasks.add(createTaskBuilder(".Task1").setParentTask(aboveHomeStack).build());
-        mRecentTasks.add(createTaskBuilder(".Task2").setParentTask(aboveHomeStack).build());
-        mRecentTasks.add(createTaskBuilder(".Task3").setParentTask(aboveHomeStack).build());
+        mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTaskFragment(homeStack).build());
+        mRecentTasks.add(createTaskBuilder(".Task1").setParentTaskFragment(aboveHomeStack).build());
+        mRecentTasks.add(createTaskBuilder(".Task2").setParentTaskFragment(aboveHomeStack).build());
+        mRecentTasks.add(createTaskBuilder(".Task3").setParentTaskFragment(aboveHomeStack).build());
 
         triggerTrimAndAssertNoTasksTrimmed();
     }
@@ -957,11 +971,11 @@
         // Add a number of tasks (beyond the max) but ensure that only the task in the stack behind
         // the home stack is trimmed once a new task is added
         final Task behindHomeTask = createTaskBuilder(".Task1")
-                .setParentTask(behindHomeStack)
+                .setParentTaskFragment(behindHomeStack)
                 .build();
         mRecentTasks.add(behindHomeTask);
-        mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTask(homeStack).build());
-        mRecentTasks.add(createTaskBuilder(".Task2").setParentTask(aboveHomeStack).build());
+        mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTaskFragment(homeStack).build());
+        mRecentTasks.add(createTaskBuilder(".Task2").setParentTaskFragment(aboveHomeStack).build());
 
         triggerTrimAndAssertTrimmed(behindHomeTask);
     }
@@ -977,10 +991,12 @@
 
         // Add a number of tasks (beyond the max) on each display, ensure that the tasks are not
         // removed
-        mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTask(homeTask).build());
-        mRecentTasks.add(createTaskBuilder(".Task1").setParentTask(otherDisplayRootTask).build());
-        mRecentTasks.add(createTaskBuilder(".Task2").setParentTask(otherDisplayRootTask).build());
-        mRecentTasks.add(createTaskBuilder(".HomeTask2").setParentTask(homeTask).build());
+        mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTaskFragment(homeTask).build());
+        mRecentTasks.add(createTaskBuilder(".Task1").setParentTaskFragment(otherDisplayRootTask)
+                .build());
+        mRecentTasks.add(createTaskBuilder(".Task2").setParentTaskFragment(otherDisplayRootTask)
+                .build());
+        mRecentTasks.add(createTaskBuilder(".HomeTask2").setParentTaskFragment(homeTask).build());
 
         triggerTrimAndAssertNoTasksTrimmed();
     }
@@ -1010,7 +1026,7 @@
         Task t1 = createTaskBuilder("com.android.pkg1", ".Task1").build();
         mRecentTasks.add(t1);
         mRecentTasks.add(createTaskBuilder("com.android.pkg1", ".HomeTask")
-                .setParentTask(mTaskContainer.getRootHomeTask()).build());
+                .setParentTaskFragment(mTaskContainer.getRootHomeTask()).build());
         Task t2 = createTaskBuilder("com.android.pkg2", ".Task2").build();
         mRecentTasks.add(t2);
         mRecentTasks.add(createTaskBuilder("com.android.pkg1", ".PipTask")
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index b6cfa8e..89ae5ed 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -51,6 +51,7 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.IInterface;
+import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 import android.view.IRemoteAnimationFinishedCallback;
 import android.view.IRemoteAnimationRunner;
@@ -274,6 +275,32 @@
     }
 
     @Test
+    public void testOpeningTaskWithTopFinishingActivity() {
+        final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "win");
+        final Task task = win.getTask();
+        final ActivityRecord topFinishing = new ActivityBuilder(mAtm).setTask(task).build();
+        // Now the task contains:
+        //     - Activity[1] (top, finishing, no window)
+        //     - Activity[0] (has window)
+        topFinishing.finishing = true;
+        spyOn(mDisplayContent.mAppTransition);
+        doReturn(mController).when(mDisplayContent.mAppTransition).getRemoteAnimationController();
+        task.applyAnimationUnchecked(null /* lp */, true /* enter */, TRANSIT_OLD_TASK_OPEN,
+                false /* isVoiceInteraction */, null /* sources */);
+        mController.goodToGo(TRANSIT_OLD_TASK_OPEN);
+        mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+        final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
+                ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+        try {
+            verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_OPEN),
+                    appsCaptor.capture(), any(), any(), any());
+        } catch (RemoteException ignored) {
+        }
+        assertEquals(1, appsCaptor.getValue().length);
+        assertEquals(RemoteAnimationTarget.MODE_OPENING, appsCaptor.getValue()[0].mode);
+    }
+
+    @Test
     public void testChangeToSmallerSize() throws Exception {
         final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
         mDisplayContent.mChangingContainers.add(win.mActivityRecord);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
index 6cc60ea..030733b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -191,7 +191,6 @@
         final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
 
         // Root task removal is deferred if one of its child is animating.
-        doReturn(true).when(rootTask).hasWindowsAlive();
         doReturn(rootTask).when(task).getAnimatingContainer(
                 eq(TRANSITION | CHILDREN), anyInt());
 
@@ -330,7 +329,7 @@
 
         // Create primary splitscreen root task.
         final Task primarySplitScreen = new TaskBuilder(mAtm.mTaskSupervisor)
-                .setParentTask(organizer.mPrimary)
+                .setParentTaskFragment(organizer.mPrimary)
                 .setOnTop(true)
                 .build();
 
@@ -506,8 +505,8 @@
                 targetActivity);
         final ComponentName alias = new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME,
                 aliasActivity);
-        final Task parentTask = new TaskBuilder(mAtm.mTaskSupervisor).build();
-        final Task task = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(parentTask).build();
+        final Task parentTask = new TaskBuilder(mSupervisor).build();
+        final Task task = new TaskBuilder(mSupervisor).setParentTaskFragment(parentTask).build();
         task.origActivity = alias;
         task.realActivity = target;
         new ActivityBuilder(mAtm).setComponent(target).setTask(task).setTargetActivity(
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 8d4acbb..f2eb709 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -168,7 +168,7 @@
     @Test
     public void testTaskLayerRank() {
         final Task rootTask = new TaskBuilder(mSupervisor).build();
-        final Task task1 = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
+        final Task task1 = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask).build();
         new ActivityBuilder(mAtm).setTask(task1).build().mVisibleRequested = true;
         mWm.mRoot.rankTaskLayers();
 
@@ -523,7 +523,8 @@
         final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
         final Task targetRootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, false /* onTop */);
-        final Task targetTask = new TaskBuilder(mSupervisor).setParentTask(targetRootTask).build();
+        final Task targetTask = new TaskBuilder(mSupervisor).setParentTaskFragment(targetRootTask)
+                .build();
 
         // Create Recents on secondary display.
         final TestDisplayContent secondDisplay = addNewDisplayContentAt(
diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
index c44c22f..cb85884 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
@@ -166,7 +166,7 @@
         final Task task = new TaskBuilder(mAtm.mTaskSupervisor)
                 .setComponent(new ComponentName(mContext.getPackageName(), className))
                 .setTaskId(taskId)
-                .setParentTask(stack)
+                .setParentTaskFragment(stack)
                 .build();
         task.lastActiveTime = lastActiveTime;
         final ActivityRecord activity = new ActivityBuilder(mAtm)
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 5989497..6407c92 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -40,6 +40,11 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_FIXED_ORIENTATION;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED;
+import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE;
 import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS;
 import static com.android.server.wm.ActivityRecord.State.RESUMED;
 import static com.android.server.wm.ActivityRecord.State.STOPPED;
@@ -55,7 +60,9 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.same;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doCallRealMethod;
 
@@ -101,10 +108,14 @@
 
     private Task mTask;
     private ActivityRecord mActivity;
+    private ActivityMetricsLogger mActivityMetricsLogger;
     private Properties mInitialConstrainDisplayApisFlags;
 
     @Before
     public void setUp() throws Exception {
+        mActivityMetricsLogger = mock(ActivityMetricsLogger.class);
+        clearInvocations(mActivityMetricsLogger);
+        doReturn(mActivityMetricsLogger).when(mAtm.mTaskSupervisor).getActivityMetricsLogger();
         mInitialConstrainDisplayApisFlags = DeviceConfig.getProperties(
                 NAMESPACE_CONSTRAIN_DISPLAY_APIS);
         DeviceConfig.setProperties(
@@ -320,6 +331,11 @@
         assertScaled();
         // Activity is sandboxed due to size compat mode.
         assertActivityMaxBoundsSandboxed();
+
+        final WindowState appWindow = addWindowToActivity(mActivity);
+        assertTrue(mActivity.hasSizeCompatBounds());
+        assertEquals("App window must use size compat bounds for layout in screen space",
+                mActivity.getBounds(), appWindow.getBounds());
     }
 
     @Test
@@ -1934,13 +1950,19 @@
         setUpDisplaySizeWithApp(1000, 2500);
 
         assertFalse(mActivity.areBoundsLetterboxed());
+        verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED);
 
         prepareUnresizable(mActivity, /* maxAspect= */ 2, SCREEN_ORIENTATION_PORTRAIT);
         assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
         assertTrue(mActivity.areBoundsLetterboxed());
 
+        verifyLogAppCompatState(mActivity,
+                APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO);
+
         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
+        verifyLogAppCompatState(mActivity,
+                APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE);
         rotateDisplay(mActivity.mDisplayContent, ROTATION_0);
 
         // After returning to the original rotation, bounds are computed in
@@ -1950,6 +1972,18 @@
         assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
         assertTrue(mActivity.areBoundsLetterboxed());
+        verifyLogAppCompatState(mActivity,
+                APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO);
+
+        // After setting the visibility of the activity to false, areBoundsLetterboxed() still
+        // returns true but the NOT_VISIBLE App Compat state is logged.
+        mActivity.setVisibility(false);
+        assertTrue(mActivity.areBoundsLetterboxed());
+        verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE);
+        mActivity.setVisibility(true);
+        assertTrue(mActivity.areBoundsLetterboxed());
+        verifyLogAppCompatState(mActivity,
+                APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO);
     }
 
     @Test
@@ -1958,12 +1992,15 @@
         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
 
         assertFalse(mActivity.areBoundsLetterboxed());
+        verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED);
 
         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
 
         assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
         assertTrue(mActivity.areBoundsLetterboxed());
+        verifyLogAppCompatState(mActivity,
+                APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_FIXED_ORIENTATION);
     }
 
     @Test
@@ -1973,12 +2010,70 @@
         prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
 
         assertFalse(mActivity.areBoundsLetterboxed());
+        verifyLogAppCompatState(mActivity, APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED);
 
         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
 
         assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
         assertTrue(mActivity.inSizeCompatMode());
         assertTrue(mActivity.areBoundsLetterboxed());
+        verifyLogAppCompatState(mActivity,
+                APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE);
+    }
+
+    /**
+     * Tests that all three paths in which aspect ratio logic can be applied yield the same
+     * result, which is that aspect ratio is respected on app bounds. The three paths are
+     * fixed orientation, no fixed orientation but fixed aspect ratio, and size compat mode.
+     */
+    @Test
+    public void testAllAspectRatioLogicConsistent() {
+        // Create display that has all stable insets and does not rotate. Make sure that status bar
+        // height is greater than notch height so that stable bounds do not equal app bounds.
+        final int notchHeight = 75;
+        final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1080, 600)
+                .setSystemDecorations(true).setNotch(notchHeight)
+                .setStatusBarHeight(notchHeight + 20).setCanRotate(false).build();
+
+        // Create task on test display.
+        final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
+
+        // Target min aspect ratio must be larger than parent aspect ratio to be applied.
+        final float targetMinAspectRatio = 3.0f;
+
+        // Create fixed portait activity with min aspect ratio greater than parent aspect ratio.
+        final ActivityRecord fixedOrientationActivity = new ActivityBuilder(mAtm)
+                .setTask(task).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+                .setMinAspectRatio(targetMinAspectRatio).build();
+        final Rect fixedOrientationAppBounds = new Rect(fixedOrientationActivity.getConfiguration()
+                .windowConfiguration.getAppBounds());
+
+        // Create activity with no fixed orientation and min aspect ratio greater than parent aspect
+        // ratio.
+        final ActivityRecord minAspectRatioActivity = new ActivityBuilder(mAtm).setTask(task)
+                .setMinAspectRatio(targetMinAspectRatio).build();
+        final Rect minAspectRatioAppBounds = new Rect(minAspectRatioActivity.getConfiguration()
+                .windowConfiguration.getAppBounds());
+
+        // Create unresizeable fixed portait activity with min aspect ratio greater than parent
+        // aspect ratio.
+        final ActivityRecord sizeCompatActivity = new ActivityBuilder(mAtm)
+                .setTask(task).setResizeMode(RESIZE_MODE_UNRESIZEABLE)
+                .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
+                .setMinAspectRatio(targetMinAspectRatio).build();
+        // Resize display running unresizeable activity to make it enter size compat mode.
+        resizeDisplay(display, 1800, 1000);
+        final Rect sizeCompatAppBounds = new Rect(sizeCompatActivity.getConfiguration()
+                .windowConfiguration.getAppBounds());
+
+        // Check that aspect ratio of app bounds is equal to the min aspect ratio.
+        final float delta = 0.01f;
+        assertEquals(targetMinAspectRatio, ActivityRecord
+                .computeAspectRatio(fixedOrientationAppBounds), delta);
+        assertEquals(targetMinAspectRatio, ActivityRecord
+                .computeAspectRatio(minAspectRatioAppBounds), delta);
+        assertEquals(targetMinAspectRatio, ActivityRecord
+                .computeAspectRatio(sizeCompatAppBounds), delta);
     }
 
     private void assertHorizontalPositionForDifferentDisplayConfigsForLandscapeActivity(
@@ -2137,6 +2232,11 @@
                 .isEqualTo(activity.getWindowConfiguration().getBounds());
     }
 
+    private void verifyLogAppCompatState(ActivityRecord activity, int state) {
+        verify(mActivityMetricsLogger, atLeastOnce()).logAppCompatState(
+                argThat(r -> activity == r && r.getAppCompatState() == state));
+    }
+
     static Configuration rotateDisplay(DisplayContent display, int rotation) {
         final Configuration c = new Configuration();
         display.getDisplayRotation().setRotation(rotation);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index a8e1753..8e7ba4bc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -258,6 +258,7 @@
         final ActivityManagerInternal amInternal = mAmService.mInternal;
         spyOn(amInternal);
         doNothing().when(amInternal).trimApplications();
+        doNothing().when(amInternal).scheduleAppGcs();
         doNothing().when(amInternal).updateCpuStats();
         doNothing().when(amInternal).updateOomAdj();
         doNothing().when(amInternal).updateBatteryStats(any(), anyInt(), anyInt(), anyBoolean());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index c45c18d..d68edba 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -620,7 +620,7 @@
         final Task pinnedRootTask = mRootWindowContainer.getDefaultTaskDisplayArea()
                 .createRootTask(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP);
         final Task pinnedTask = new TaskBuilder(mAtm.mTaskSupervisor)
-                .setParentTask(pinnedRootTask).build();
+                .setParentTaskFragment(pinnedRootTask).build();
         new ActivityBuilder(mAtm).setActivityFlags(FLAG_ALWAYS_FOCUSABLE)
                 .setTask(pinnedTask).build();
         pinnedRootTask.moveToFront("movePinnedRootTaskToFront");
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index c35f317..5c79f5c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -326,7 +326,8 @@
         mTransaction.startActivityInTaskFragment(
                 mFragmentToken, null /* callerToken */, new Intent(), null /* activityOptions */);
         mTransaction.reparentActivityToTaskFragment(mFragmentToken, mock(IBinder.class));
-        mTransaction.setAdjacentTaskFragments(mFragmentToken, mock(IBinder.class));
+        mTransaction.setAdjacentTaskFragments(mFragmentToken, mock(IBinder.class),
+                null /* options */);
 
         // It is expected to fail for the mock TaskFragmentCreationParams. It is ok as we are
         // testing the security check here.
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 5e4c67c..e528a4a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -1716,7 +1716,7 @@
         final Task rootTask = display.getDefaultTaskDisplayArea()
                 .createRootTask(display.getWindowingMode(), ACTIVITY_TYPE_STANDARD, true);
         rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
-        final Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
+        final Task task = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask).build();
         // Just work around the unnecessary adjustments for bounds.
         task.getWindowConfiguration().setBounds(bounds);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 629e452..67e8c87 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -126,8 +126,15 @@
         final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
         final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
 
-        task.removeIfPossible();
-        // Assert that the container was removed.
+        task.remove(false /* withTransition */, "testRemoveContainer");
+        // There is still an activity to be destroyed, so the task is not removed immediately.
+        assertNotNull(task.getParent());
+        assertTrue(rootTask.hasChild());
+        assertTrue(task.hasChild());
+        assertTrue(activity.finishing);
+
+        activity.destroyed("testRemoveContainer");
+        // Assert that the container was removed after the activity is destroyed.
         assertNull(task.getParent());
         assertEquals(0, task.getChildCount());
         assertNull(activity.getParent());
@@ -420,7 +427,7 @@
         TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea();
         Task rootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_FREEFORM,
                 ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
+        Task task = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask).build();
         final Configuration parentConfig = rootTask.getConfiguration();
         parentConfig.windowConfiguration.setBounds(parentBounds);
         parentConfig.densityDpi = DisplayMetrics.DENSITY_DEFAULT;
@@ -750,7 +757,7 @@
         DisplayInfo displayInfo = new DisplayInfo();
         mAtm.mContext.getDisplay().getDisplayInfo(displayInfo);
         final int displayHeight = displayInfo.logicalHeight;
-        final Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
+        final Task task = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask).build();
         final Configuration inOutConfig = new Configuration();
         final Configuration parentConfig = new Configuration();
         final int longSide = 1200;
@@ -1368,7 +1375,7 @@
         TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea();
         Task rootTask = taskDisplayArea.createRootTask(windowingMode, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
-        Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
+        Task task = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask).build();
 
         final Configuration parentConfig = rootTask.getConfiguration();
         parentConfig.windowConfiguration.setAppBounds(parentBounds);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index ce2d748..0e504d3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -19,7 +19,9 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -77,6 +79,7 @@
         private int mPosition = POSITION_BOTTOM;
         protected final ActivityTaskManagerService mService;
         private boolean mSystemDecorations = false;
+        private int mStatusBarHeight = 0;
 
         Builder(ActivityTaskManagerService service, int width, int height) {
             mService = service;
@@ -125,6 +128,10 @@
                     Insets.of(0, height, 0, 0), null, new Rect(20, 0, 80, height), null, null);
             return this;
         }
+        Builder setStatusBarHeight(int height) {
+            mStatusBarHeight = height;
+            return this;
+        }
         Builder setCanRotate(boolean canRotate) {
             mCanRotate = canRotate;
             return this;
@@ -158,6 +165,14 @@
                 doReturn(false).when(displayPolicy).hasStatusBar();
                 doReturn(false).when(newDisplay).supportsSystemDecorations();
             }
+            if (mStatusBarHeight > 0) {
+                doReturn(true).when(displayPolicy).hasStatusBar();
+                doAnswer(invocation -> {
+                    Rect inOutInsets = (Rect) invocation.getArgument(0);
+                    inOutInsets.top = mStatusBarHeight;
+                    return null;
+                }).when(displayPolicy).convertNonDecorInsetsToStableInsets(any(), anyInt());
+            }
             Configuration c = new Configuration();
             newDisplay.computeScreenConfiguration(c);
             c.windowConfiguration.setWindowingMode(mWindowingMode);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 45e5f8e..6d60bcf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -20,6 +20,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
@@ -41,6 +42,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.window.ITaskOrganizer;
+import android.window.ITransitionPlayer;
 import android.window.TransitionInfo;
 
 import androidx.test.filters.SmallTest;
@@ -444,6 +446,71 @@
                 info.getChange(openInChangeTask.mRemoteToken.toWindowContainerToken()), info));
     }
 
+    @Test
+    public void testIntermediateVisibility() {
+        final TransitionController controller = new TransitionController(mAtm);
+        final ITransitionPlayer player = new ITransitionPlayer.Default();
+        controller.registerTransitionPlayer(player);
+        ITaskOrganizer mockOrg = mock(ITaskOrganizer.class);
+        final Transition openTransition = controller.createTransition(TRANSIT_OPEN);
+
+        // Start out with task2 visible and set up a transition that closes task2 and opens task1
+        final Task task1 = createTask(mDisplayContent);
+        task1.mTaskOrganizer = mockOrg;
+        final ActivityRecord activity1 = createActivityRecord(task1);
+        activity1.mVisibleRequested = false;
+        activity1.setVisible(false);
+        final Task task2 = createTask(mDisplayContent);
+        task2.mTaskOrganizer = mockOrg;
+        final ActivityRecord activity2 = createActivityRecord(task1);
+        activity2.mVisibleRequested = true;
+        activity2.setVisible(true);
+
+        openTransition.collectExistenceChange(task1);
+        openTransition.collectExistenceChange(activity1);
+        openTransition.collectExistenceChange(task2);
+        openTransition.collectExistenceChange(activity2);
+
+        activity1.mVisibleRequested = true;
+        activity1.setVisible(true);
+        activity2.mVisibleRequested = false;
+
+        // Using abort to force-finish the sync (since we can't wait for drawing in unit test).
+        // We didn't call abort on the transition itself, so it will still run onTransactionReady
+        // normally.
+        mWm.mSyncEngine.abort(openTransition.getSyncId());
+
+        // Before finishing openTransition, we are now going to simulate closing task1 to return
+        // back to (open) task2.
+        final Transition closeTransition = controller.createTransition(TRANSIT_CLOSE);
+
+        closeTransition.collectExistenceChange(task1);
+        closeTransition.collectExistenceChange(activity1);
+        closeTransition.collectExistenceChange(task2);
+        closeTransition.collectExistenceChange(activity2);
+
+        activity1.mVisibleRequested = false;
+        activity2.mVisibleRequested = true;
+
+        openTransition.finishTransition();
+
+        // We finished the openTransition. Even though activity1 is visibleRequested=false, since
+        // the closeTransition animation hasn't played yet, make sure that we didn't commit
+        // visible=false on activity1 since it needs to remain visible for the animation.
+        assertTrue(activity1.isVisible());
+        assertTrue(activity2.isVisible());
+
+        // Using abort to force-finish the sync (since we obviously can't wait for drawing).
+        // We didn't call abort on the actual transition, so it will still run onTransactionReady
+        // normally.
+        mWm.mSyncEngine.abort(closeTransition.getSyncId());
+
+        closeTransition.finishTransition();
+
+        assertFalse(activity1.isVisible());
+        assertTrue(activity2.isVisible());
+    }
+
     /** Fill the change map with all the parents of top. Change maps are usually fully populated */
     private static void fillChangeMap(ArrayMap<WindowContainer, Transition.ChangeInfo> changes,
             WindowContainer top) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 39fe952..9160109 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -81,6 +81,7 @@
 import android.window.IWindowContainerTransactionCallback;
 import android.window.StartingWindowInfo;
 import android.window.TaskAppearedInfo;
+import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
 import androidx.test.filters.SmallTest;
@@ -1275,6 +1276,24 @@
         assertTrue(optionsCaptor.getValue().getOriginalOptions().getTransientLaunch());
     }
 
+    @Test
+    public void testResumeTopsWhenLeavingPinned() {
+        final ActivityRecord record = makePipableActivity();
+        final Task rootTask = record.getRootTask();
+
+        clearInvocations(mWm.mAtmService.mRootWindowContainer);
+        final WindowContainerTransaction t = new WindowContainerTransaction();
+        WindowContainerToken wct = rootTask.mRemoteToken.toWindowContainerToken();
+        t.setWindowingMode(wct, WINDOWING_MODE_PINNED);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
+        verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities();
+
+        clearInvocations(mWm.mAtmService.mRootWindowContainer);
+        t.setWindowingMode(wct, WINDOWING_MODE_FULLSCREEN);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
+        verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities();
+    }
+
     /**
      * Verifies that task vanished is called for a specific task.
      */
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 17288c2..b17ea5e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -892,6 +892,40 @@
         assertTrue(mAppWindow.getInsetsState().getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR));
     }
 
+    @Test
+    public void testAdjustImeInsetsVisibilityWhenSwitchingApps() {
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2");
+        final WindowState imeWindow = createWindow(null, TYPE_APPLICATION, "imeWindow");
+        spyOn(imeWindow);
+        doReturn(true).when(imeWindow).isVisible();
+        mDisplayContent.mInputMethodWindow = imeWindow;
+
+        final InsetsStateController controller = mDisplayContent.getInsetsStateController();
+        controller.getImeSourceProvider().setWindow(imeWindow, null, null);
+
+        // Simulate app requests IME with updating all windows Insets State when IME is above app.
+        mDisplayContent.setImeLayeringTarget(app);
+        mDisplayContent.setImeInputTarget(app);
+        assertTrue(mDisplayContent.shouldImeAttachedToApp());
+        controller.getImeSourceProvider().scheduleShowImePostLayout(app);
+        controller.getImeSourceProvider().getSource().setVisible(true);
+        controller.updateAboveInsetsState(imeWindow, false);
+
+        // Expect all app windows behind IME can receive IME insets visible.
+        assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible());
+        assertTrue(app2.getInsetsState().getSource(ITYPE_IME).isVisible());
+
+        // Simulate app plays closing transition to app2.
+        app.mActivityRecord.commitVisibility(false, false);
+        assertTrue(app.mActivityRecord.mLastImeShown);
+        assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
+
+        // Verify the IME insets is visible on app, but not for app2 during app task switching.
+        assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible());
+        assertFalse(app2.getInsetsState().getSource(ITYPE_IME).isVisible());
+    }
+
     @UseTestDisplay(addWindows = { W_ACTIVITY })
     @Test
     public void testUpdateImeControlTargetWhenLeavingMultiWindow() {
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 050fd80..e7ef6ae 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -56,6 +56,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.server.wm.StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER;
 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
 import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
 
 import static org.junit.Assert.assertEquals;
@@ -589,7 +590,7 @@
     Task createTaskInRootTask(Task rootTask, int userId) {
         final Task task = new TaskBuilder(rootTask.mTaskSupervisor)
                 .setUserId(userId)
-                .setParentTask(rootTask)
+                .setParentTaskFragment(rootTask)
                 .build();
         return task;
     }
@@ -675,6 +676,26 @@
         activity.mVisibleRequested = true;
     }
 
+    /**
+     * Creates a {@link TaskFragment} and attach it to the {@code parentTask}.
+     *
+     * @param parentTask the {@link Task} this TaskFragment is going to be attached
+     * @param createEmbeddedTask Sets to {@code true} to create an embedded Task for this
+     *                           TaskFragment. Otherwise, create a {@link ActivityRecord}.
+     * @return the created TaskFragment
+     */
+    static TaskFragment createTaskFragmentWithParentTask(@NonNull Task parentTask,
+            boolean createEmbeddedTask) {
+        final TaskFragmentBuilder builder = new TaskFragmentBuilder(parentTask.mAtmService)
+                .setParentTask(parentTask);
+        if (createEmbeddedTask) {
+            builder.createEmbeddedTask();
+        } else {
+            builder.createActivityCount(1);
+        }
+        return builder.build();
+    }
+
     /** Creates a {@link DisplayContent} that supports IME and adds it to the system. */
     DisplayContent createNewDisplay() {
         return createNewDisplayWithImeSupport(DISPLAY_IME_POLICY_LOCAL);
@@ -1042,7 +1063,7 @@
                         // Apply the root activity info and intent
                         .setActivityInfo(aInfo)
                         .setIntent(intent)
-                        .setParentTask(mParentTask).build();
+                        .setParentTaskFragment(mParentTask).build();
             } else if (mTask == null && mParentTask != null && DisplayContent.alwaysCreateRootTask(
                     mParentTask.getWindowingMode(), mParentTask.getActivityType())) {
                 // The parent task can be the task root.
@@ -1101,6 +1122,64 @@
         }
     }
 
+    static class TaskFragmentBuilder {
+        private final ActivityTaskManagerService mAtm;
+        private Task mParentTask;
+        private boolean mCreateParentTask;
+        private boolean mCreateEmbeddedTask;
+        private int mCreateActivityCount = 0;
+
+        TaskFragmentBuilder(ActivityTaskManagerService service) {
+            mAtm = service;
+        }
+
+        TaskFragmentBuilder setCreateParentTask() {
+            mCreateParentTask = true;
+            return this;
+        }
+
+        TaskFragmentBuilder setParentTask(Task task) {
+            mParentTask = task;
+            return this;
+        }
+
+        /** Creates a child embedded Task and its Activity */
+        TaskFragmentBuilder createEmbeddedTask() {
+            mCreateEmbeddedTask = true;
+            return this;
+        }
+
+        TaskFragmentBuilder createActivityCount(int count) {
+            mCreateActivityCount = count;
+            return this;
+        }
+
+        TaskFragment build() {
+            SystemServicesTestRule.checkHoldsLock(mAtm.mGlobalLock);
+
+            final TaskFragment taskFragment = new TaskFragment(mAtm, null /* fragmentToken */,
+                    false /* createdByOrganizer */);
+            if (mParentTask == null && mCreateParentTask) {
+                mParentTask = new TaskBuilder(mAtm.mTaskSupervisor).build();
+            }
+            if (mParentTask != null) {
+                mParentTask.addChild(taskFragment, POSITION_TOP);
+            }
+            if (mCreateEmbeddedTask) {
+                new TaskBuilder(mAtm.mTaskSupervisor)
+                        .setParentTaskFragment(taskFragment)
+                        .setCreateActivity(true)
+                        .build();
+            }
+            while (mCreateActivityCount > 0) {
+                final ActivityRecord activity = new ActivityBuilder(mAtm).build();
+                taskFragment.addChild(activity);
+                mCreateActivityCount--;
+            }
+            return taskFragment;
+        }
+    }
+
     /**
      * Builder for creating new tasks.
      */
@@ -1121,7 +1200,7 @@
         private IVoiceInteractionSession mVoiceSession;
 
         private boolean mCreateParentTask = false;
-        private Task mParentTask;
+        private TaskFragment mParentTaskFragment;
 
         private boolean mCreateActivity = false;
 
@@ -1205,8 +1284,8 @@
             return this;
         }
 
-        TaskBuilder setParentTask(Task parentTask) {
-            mParentTask = parentTask;
+        TaskBuilder setParentTaskFragment(TaskFragment parentTaskFragment) {
+            mParentTaskFragment = parentTaskFragment;
             return this;
         }
 
@@ -1219,12 +1298,13 @@
             SystemServicesTestRule.checkHoldsLock(mSupervisor.mService.mGlobalLock);
 
             // Create parent task.
-            if (mParentTask == null && mCreateParentTask) {
-                mParentTask = mTaskDisplayArea.createRootTask(
+            if (mParentTaskFragment == null && mCreateParentTask) {
+                mParentTaskFragment = mTaskDisplayArea.createRootTask(
                         WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
             }
-            if (mParentTask != null && !Mockito.mockingDetails(mParentTask).isSpy()) {
-                spyOn(mParentTask);
+            if (mParentTaskFragment != null
+                    && !Mockito.mockingDetails(mParentTaskFragment).isSpy()) {
+                spyOn(mParentTaskFragment);
             }
 
             // Create task.
@@ -1252,13 +1332,15 @@
                     .setOnTop(mOnTop)
                     .setVoiceSession(mVoiceSession);
             final Task task;
-            if (mParentTask == null) {
+            if (mParentTaskFragment == null) {
                 task = builder.setActivityType(mActivityType)
                         .setParent(mTaskDisplayArea)
                         .build();
             } else {
-                task = builder.setParent(mParentTask).build();
-                mParentTask.moveToFront("build-task");
+                task = builder.setParent(mParentTaskFragment).build();
+                if (mParentTaskFragment.asTask() != null) {
+                    mParentTaskFragment.asTask().moveToFront("build-task");
+                }
             }
             spyOn(task);
             task.mUserId = mUserId;
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 7f24c36..85f16eb 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -1193,7 +1193,8 @@
                 } else if (mCurrentFunctions == UsbManager.FUNCTION_MIDI) {
                     titleRes = com.android.internal.R.string.usb_midi_notification_title;
                     id = SystemMessage.NOTE_USB_MIDI;
-                } else if (mCurrentFunctions == UsbManager.FUNCTION_RNDIS) {
+                } else if ((mCurrentFunctions == UsbManager.FUNCTION_RNDIS)
+                        || (mCurrentFunctions == UsbManager.FUNCTION_NCM)) {
                     titleRes = com.android.internal.R.string.usb_tether_notification_title;
                     id = SystemMessage.NOTE_USB_TETHER;
                 } else if (mCurrentFunctions == UsbManager.FUNCTION_ACCESSORY) {
diff --git a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
index 5874b4b..7b6ccd3 100644
--- a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
+++ b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
@@ -25,12 +25,12 @@
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.hardware.SensorPrivacyManager.Sensors;
+import android.hardware.SensorPrivacyManagerInternal;
 import android.hardware.usb.AccessoryFilter;
 import android.hardware.usb.DeviceFilter;
 import android.hardware.usb.UsbAccessory;
-import android.hardware.usb.UsbConstants;
 import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbInterface;
 import android.hardware.usb.UsbManager;
 import android.os.AsyncTask;
 import android.os.Binder;
@@ -52,9 +52,9 @@
 import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
 import com.android.internal.util.dump.DualDumpOutputStream;
+import com.android.server.LocalServices;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -64,7 +64,6 @@
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.nio.charset.StandardCharsets;
 
 /**
  * UsbUserPermissionManager manages usb device or accessory access permissions.
@@ -110,19 +109,20 @@
      */
     @GuardedBy("mLock")
     private boolean mIsCopyPermissionsScheduled;
+    private final SensorPrivacyManagerInternal mSensorPrivacyMgrInternal;
 
     UsbUserPermissionManager(@NonNull Context context,
             @NonNull UsbUserSettingsManager usbUserSettingsManager) {
         mContext = context;
         mUser = context.getUser();
         mUsbUserSettingsManager = usbUserSettingsManager;
+        mSensorPrivacyMgrInternal = LocalServices.getService(SensorPrivacyManagerInternal.class);
         mDisablePermissionDialogs = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_disableUsbPermissionDialogs);
 
         mPermissionsFile = new AtomicFile(new File(
                 Environment.getUserSystemDirectory(mUser.getIdentifier()),
                 "usb_permissions.xml"), "usb-permissions");
-
         synchronized (mLock) {
             readPermissionsLocked();
         }
@@ -195,11 +195,27 @@
      */
     boolean hasPermission(@NonNull UsbDevice device, @NonNull String packageName, int pid,
             int uid) {
-        if (isCameraDevicePresent(device)) {
-            if (!isCameraPermissionGranted(packageName, pid, uid)) {
+        if (device.getHasVideoCapture()) {
+            boolean isCameraPrivacyEnabled = mSensorPrivacyMgrInternal.isSensorPrivacyEnabled(
+                    UserHandle.getUserId(uid), Sensors.CAMERA);
+            if (DEBUG) {
+                Slog.d(TAG, "isCameraPrivacyEnabled: " + isCameraPrivacyEnabled);
+            }
+            if (isCameraPrivacyEnabled || !isCameraPermissionGranted(packageName, pid, uid)) {
                 return false;
             }
         }
+        // Only check for microphone privacy and not RECORD_AUDIO permission, because access to usb
+        // camera device with audio recording capabilities may still be granted with a warning
+        if (device.getHasAudioCapture() && mSensorPrivacyMgrInternal.isSensorPrivacyEnabled(
+                UserHandle.getUserId(uid), Sensors.MICROPHONE)) {
+            if (DEBUG) {
+                Slog.d(TAG,
+                        "Access to device with audio recording capabilities denied because "
+                                + "microphone privacy is enabled.");
+            }
+            return false;
+        }
         synchronized (mLock) {
             if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
                 return true;
@@ -698,7 +714,10 @@
             }
             return;
         }
-        if (isCameraDevicePresent(device)) {
+        // If the app doesn't have camera permission do not request permission to the USB device.
+        // Note that if the USB camera also has a microphone, a warning will be shown to the user if
+        // the app doesn't have RECORD_AUDIO permission.
+        if (device.getHasVideoCapture()) {
             if (!isCameraPermissionGranted(packageName, pid, uid)) {
                 intent.putExtra(UsbManager.EXTRA_DEVICE, device);
                 intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false);
@@ -733,27 +752,4 @@
         requestPermissionDialog(null, accessory,
                 mUsbUserSettingsManager.canBeDefault(accessory, packageName), packageName, pi, uid);
     }
-
-    /**
-     * Check whether a particular device or any of its interfaces
-     * is of class VIDEO.
-     *
-     * @param device The device that needs to get scanned
-     * @return True in case a VIDEO device or interface is present,
-     * False otherwise.
-     */
-    private boolean isCameraDevicePresent(UsbDevice device) {
-        if (device.getDeviceClass() == UsbConstants.USB_CLASS_VIDEO) {
-            return true;
-        }
-
-        for (int i = 0; i < device.getInterfaceCount(); i++) {
-            UsbInterface iface = device.getInterface(i);
-            if (iface.getInterfaceClass() == UsbConstants.USB_CLASS_VIDEO) {
-                return true;
-            }
-        }
-
-        return false;
-    }
 }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 965f126..734172f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -36,6 +36,7 @@
 import android.hardware.soundtrigger.IRecognitionStatusCallback;
 import android.hardware.soundtrigger.SoundTrigger;
 import android.media.AudioFormat;
+import android.media.AudioManagerInternal;
 import android.media.permission.Identity;
 import android.media.permission.PermissionUtil;
 import android.os.Binder;
@@ -44,6 +45,7 @@
 import android.os.IRemoteCallback;
 import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SharedMemory;
@@ -275,6 +277,7 @@
             LocalServices.getService(PermissionManagerServiceInternal.class)
                     .setHotwordDetectionServiceProvider(null);
             mIdentity = null;
+            updateServiceUidForAudioPolicy(Process.INVALID_UID);
         }
         mCancellationTaskFuture.cancel(/* may interrupt */ true);
         if (mAudioFlinger != null) {
@@ -893,6 +896,8 @@
         connection.run(service -> service.ping(new IRemoteCallback.Stub() {
             @Override
             public void sendResult(Bundle bundle) throws RemoteException {
+                // TODO: Exit if the service has been unbound already (though there's a very low
+                // chance this happens).
                 if (DEBUG) {
                     Slog.d(TAG, "updating hotword UID " + Binder.getCallingUid());
                 }
@@ -902,10 +907,21 @@
                 LocalServices.getService(PermissionManagerServiceInternal.class)
                         .setHotwordDetectionServiceProvider(() -> uid);
                 mIdentity = new HotwordDetectionServiceIdentity(uid, mVoiceInteractionServiceUid);
+                updateServiceUidForAudioPolicy(uid);
             }
         }));
     }
 
+    private void updateServiceUidForAudioPolicy(int uid) {
+        mScheduledExecutorService.execute(() -> {
+            final AudioManagerInternal audioManager =
+                    LocalServices.getService(AudioManagerInternal.class);
+            if (audioManager != null) {
+                audioManager.setHotwordDetectionServiceUid(uid);
+            }
+        });
+    }
+
     private static void bestEffortClose(Closeable closeable) {
         try {
             closeable.close();
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerSessionPermissionsDecorator.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerSessionPermissionsDecorator.java
index b9e1fcd..c0c3e6f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerSessionPermissionsDecorator.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerSessionPermissionsDecorator.java
@@ -29,7 +29,6 @@
 import android.media.permission.PermissionUtil;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.ServiceSpecificException;
 import android.text.TextUtils;
 import android.util.Slog;
 
@@ -61,7 +60,7 @@
 
     @Override
     public SoundTrigger.ModuleProperties getDspModuleProperties() throws RemoteException {
-        // No permission needed.
+        // No permission needed here (the app must have the Assistant Role to retrieve the session).
         return mDelegate.getDspModuleProperties();
     }
 
@@ -72,7 +71,9 @@
         if (DEBUG) {
             Slog.d(TAG, "startRecognition");
         }
-        enforcePermissions();
+        if (!isHoldingPermissions()) {
+            return SoundTrigger.STATUS_PERMISSION_DENIED;
+        }
         return mDelegate.startRecognition(i, s, iHotwordRecognitionStatusCallback,
                 recognitionConfig, b);
     }
@@ -81,25 +82,28 @@
     public int stopRecognition(int i,
             IHotwordRecognitionStatusCallback iHotwordRecognitionStatusCallback)
             throws RemoteException {
-        enforcePermissions();
+        // Stopping a model does not require special permissions. Having a handle to the session is
+        // sufficient.
         return mDelegate.stopRecognition(i, iHotwordRecognitionStatusCallback);
     }
 
     @Override
     public int setParameter(int i, int i1, int i2) throws RemoteException {
-        enforcePermissions();
+        if (!isHoldingPermissions()) {
+            return SoundTrigger.STATUS_PERMISSION_DENIED;
+        }
         return mDelegate.setParameter(i, i1, i2);
     }
 
     @Override
     public int getParameter(int i, int i1) throws RemoteException {
-        enforcePermissions();
+        // No permission needed here (the app must have the Assistant Role to retrieve the session).
         return mDelegate.getParameter(i, i1);
     }
 
     @Override
     public SoundTrigger.ModelParamRange queryParameter(int i, int i1) throws RemoteException {
-        enforcePermissions();
+        // No permission needed here (the app must have the Assistant Role to retrieve the session).
         return mDelegate.queryParameter(i, i1);
     }
 
@@ -110,15 +114,21 @@
     }
 
     // TODO: Share this code with SoundTriggerMiddlewarePermission.
-    private void enforcePermissions() {
-        enforcePermissionForPreflight(mContext, mOriginatorIdentity, RECORD_AUDIO);
-        enforcePermissionForPreflight(mContext, mOriginatorIdentity, CAPTURE_AUDIO_HOTWORD);
+    private boolean isHoldingPermissions() {
+        try {
+            enforcePermissionForPreflight(mContext, mOriginatorIdentity, RECORD_AUDIO);
+            enforcePermissionForPreflight(mContext, mOriginatorIdentity, CAPTURE_AUDIO_HOTWORD);
+            return true;
+        } catch (SecurityException e) {
+            Slog.e(TAG, e.toString());
+            return false;
+        }
     }
 
     /**
      * Throws a {@link SecurityException} if originator permanently doesn't have the given
-     * permission, or a {@link ServiceSpecificException} with a {@link
-     * #TEMPORARY_PERMISSION_DENIED} if caller originator doesn't have the given permission.
+     * permission.
+     * Soft (temporary) denials are considered OK for preflight purposes.
      *
      * @param context    A {@link Context}, used for permission checks.
      * @param identity   The identity to check.
@@ -130,15 +140,12 @@
                 permission);
         switch (status) {
             case PermissionChecker.PERMISSION_GRANTED:
+            case PermissionChecker.PERMISSION_SOFT_DENIED:
                 return;
             case PermissionChecker.PERMISSION_HARD_DENIED:
                 throw new SecurityException(
                         TextUtils.formatSimple("Failed to obtain permission %s for identity %s",
                                 permission, toString(identity)));
-            case PermissionChecker.PERMISSION_SOFT_DENIED:
-                throw new ServiceSpecificException(TEMPORARY_PERMISSION_DENIED,
-                        TextUtils.formatSimple("Failed to obtain permission %s for identity %s",
-                                permission, toString(identity)));
             default:
                 throw new RuntimeException("Unexpected permission check result.");
         }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 71541ad..9ea2b7b 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -1896,17 +1896,19 @@
 
                         String serviceComponentName = serviceInfo.getComponentName()
                                 .flattenToShortString();
-
-                        String serviceRecognizerName = new ComponentName(pkg,
-                                voiceInteractionServiceInfo.getRecognitionService())
-                                .flattenToShortString();
+                        if (voiceInteractionServiceInfo.getRecognitionService() == null) {
+                            Slog.e(TAG, "The RecognitionService must be set to avoid boot "
+                                    + "loop on earlier platform version. Also make sure that this "
+                                    + "is a valid RecognitionService when running on Android 11 "
+                                    + "or earlier.");
+                            serviceComponentName = "";
+                        }
 
                         Settings.Secure.putStringForUser(getContext().getContentResolver(),
                                 Settings.Secure.ASSISTANT, serviceComponentName, userId);
                         Settings.Secure.putStringForUser(getContext().getContentResolver(),
                                 Settings.Secure.VOICE_INTERACTION_SERVICE, serviceComponentName,
                                 userId);
-
                         return;
                     }
 
@@ -1947,6 +1949,29 @@
             }
         }
 
+        private void resetServicesIfNoRecognitionService(ComponentName serviceComponent,
+                int userHandle) {
+            for (ResolveInfo resolveInfo : queryInteractorServices(userHandle,
+                    serviceComponent.getPackageName())) {
+                VoiceInteractionServiceInfo serviceInfo =
+                        new VoiceInteractionServiceInfo(
+                                mContext.getPackageManager(),
+                                resolveInfo.serviceInfo);
+                if (!serviceInfo.getSupportsAssist()) {
+                    continue;
+                }
+                if (serviceInfo.getRecognitionService() == null) {
+                    Slog.e(TAG, "The RecognitionService must be set to "
+                            + "avoid boot loop on earlier platform version. "
+                            + "Also make sure that this is a valid "
+                            + "RecognitionService when running on Android 11 "
+                            + "or earlier.");
+                    setCurInteractor(null, userHandle);
+                    resetCurAssistant(userHandle);
+                }
+            }
+        }
+
         PackageMonitor mPackageMonitor = new PackageMonitor() {
             @Override
             public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
@@ -2090,6 +2115,7 @@
 
                         change = isPackageAppearing(curInteractor.getPackageName());
                         if (change != PACKAGE_UNCHANGED) {
+                            resetServicesIfNoRecognitionService(curInteractor, userHandle);
                             // If current interactor is now appearing, for any reason, then
                             // restart our connection with it.
                             if (mImpl != null && curInteractor.getPackageName().equals(
@@ -2112,6 +2138,13 @@
                             initForUser(userHandle);
                             return;
                         }
+                        change = isPackageAppearing(curAssistant.getPackageName());
+                        if (change != PACKAGE_UNCHANGED) {
+                            // It is possible to update Assistant without a voice interactor to one
+                            // with a voice-interactor. We should make sure the recognition service
+                            // is set to avoid boot loop.
+                            resetServicesIfNoRecognitionService(curAssistant, userHandle);
+                        }
                     }
 
                     // There is no interactor, so just deal with a simple recognizer.
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 759afd7..f2f1645 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -775,6 +775,21 @@
             "android.telecom.extra.REMOTE_PHONE_ACCOUNT_HANDLE";
 
     /**
+     * The Telecom call ID of the conference an existing connection should be added to.  This is
+     * required when {@link com.android.services.telephony.TelephonyConnectionService} adds a
+     * {@link Conference} to Telecom using the
+     * {@link ConnectionService#addExistingConnection(PhoneAccountHandle, Connection, Conference)}
+     * API.  That API specifies a parent conference associated with the new existing connection
+     * being added, and there is no equivalent as part of the {@link RemoteConnectionService} API.
+     * This extra key is used to stack the ID of the conference to which the existing connection
+     * will be added so that Telecom can link it up correctly when the {@link RemoteConference}
+     * is added to Telecom by the connection manager.
+     * @hide
+     */
+    public static final String EXTRA_ADD_TO_CONFERENCE_ID =
+            "android.telecom.extra.ADD_TO_CONFERENCE_ID";
+
+    /**
      * Extra key set from a {@link ConnectionService} when using the remote connection APIs
      * (e.g. {@link RemoteConnectionService#createRemoteConnection(PhoneAccountHandle,
      * ConnectionRequest, boolean)}) to create a remote connection.  Provides the receiving
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index bf6a6ef7..efe35d2 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -239,13 +239,9 @@
                     conference.addConnection(c);
                 }
             }
-            if (conference.getConnections().size() == 0) {
-                // A conference was created, but none of its connections are ones that have been
-                // created by, and therefore being tracked by, this remote connection service. It
-                // is of no interest to us.
-                Log.d(this, "addConferenceCall - skipping");
-                return;
-            }
+            // We used to skip adding empty conferences; however in the world of IMS conference
+            // calls we need to add them to the remote connection service because they will always
+            // start with no participants.
 
             conference.setState(parcel.getState());
             conference.setConnectionCapabilities(parcel.getConnectionCapabilities());
@@ -379,6 +375,8 @@
         @Override
         public void addExistingConnection(String callId, ParcelableConnection connection,
                 Session.Info sessionInfo) {
+            Log.i(RemoteConnectionService.this, "addExistingConnection: callId=%s, conn=%s", callId,
+                    connection);
             String callingPackage = mOurConnectionServiceImpl.getApplicationContext().
                     getOpPackageName();
             int callingTargetSdkVersion = mOurConnectionServiceImpl.getApplicationInfo()
@@ -390,6 +388,20 @@
             Bundle newExtras = new Bundle();
             newExtras.putParcelable(Connection.EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE,
                     connection.getPhoneAccount());
+            if (connection.getParentCallId() != null) {
+                RemoteConference parentConf = mConferenceById.get(connection.getParentCallId());
+                // If there is a parent being set, we need to stash the conference ID here.
+                // Telephony can add an existing connection while specifying a parent conference.
+                // There is no equivalent version of that operation as part of the remote connection
+                // API, so we will stash the pre-defined parent's ID in the extras.  When the
+                // connectionmanager copies over the extras from the remote connection to the
+                // actual one, it'll get passed to Telecom so that it can make the association.
+                if (parentConf != null) {
+                    newExtras.putString(Connection.EXTRA_ADD_TO_CONFERENCE_ID, parentConf.getId());
+                    Log.i(this, "addExistingConnection: stash parent of %s as %s",
+                            connection.getParentCallId(), parentConf.getId());
+                }
+            }
             remoteConnection.putExtras(newExtras);
             mConnectionById.put(callId, remoteConnection);
             remoteConnection.registerCallback(new RemoteConnection.Callback() {
diff --git a/telephony/OWNERS b/telephony/OWNERS
index 628c480..4df8a4b 100644
--- a/telephony/OWNERS
+++ b/telephony/OWNERS
@@ -4,13 +4,14 @@
 breadley@google.com
 fionaxu@google.com
 jackyu@google.com
-hallliu@google.com
 rgreenwalt@google.com
 tgunn@google.com
 jminjie@google.com
 shuoq@google.com
-refuhoo@google.com
 nazaninb@google.com
 sarahchin@google.com
-dbright@google.com
 xiaotonj@google.com
+huiwang@google.com
+jayachandranc@google.com
+chinmayd@google.com
+amruthr@google.com
diff --git a/telephony/java/android/telephony/AccessNetworkUtils.java b/telephony/java/android/telephony/AccessNetworkUtils.java
index 6b82045..b5d97ab 100644
--- a/telephony/java/android/telephony/AccessNetworkUtils.java
+++ b/telephony/java/android/telephony/AccessNetworkUtils.java
@@ -567,6 +567,10 @@
      */
     public static int getFrequencyFromNrArfcn(int nrArfcn) {
 
+        if (nrArfcn == PhysicalChannelConfig.CHANNEL_NUMBER_UNKNOWN) {
+            return PhysicalChannelConfig.FREQUENCY_UNKNOWN;
+        }
+
         int globalKhz = 0;
         int rangeOffset = 0;
         int arfcnOffset = 0;
@@ -632,6 +636,10 @@
      */
     public static int getFrequencyFromUarfcn(int band, int uarfcn, boolean isUplink) {
 
+        if (uarfcn == PhysicalChannelConfig.CHANNEL_NUMBER_UNKNOWN) {
+            return PhysicalChannelConfig.FREQUENCY_UNKNOWN;
+        }
+
         int offsetKhz = 0;
         for (UtranBandArfcnFrequency uarfcnFrequency : AccessNetworkConstants.
                 UtranBandArfcnFrequency.values()) {
@@ -702,6 +710,10 @@
      */
     public static int getFrequencyFromArfcn(int band, int arfcn, boolean isUplink) {
 
+        if (arfcn == PhysicalChannelConfig.CHANNEL_NUMBER_UNKNOWN) {
+            return PhysicalChannelConfig.FREQUENCY_UNKNOWN;
+        }
+
         int uplinkFrequencyFirst = 0;
         int arfcnOffset = 0;
         int downlinkOffset = 0;
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java
index 8df41fb..d250088 100644
--- a/telephony/java/android/telephony/PhysicalChannelConfig.java
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.java
@@ -338,7 +338,8 @@
     private void setUplinkFrequency() {
         switch (mNetworkType){
             case TelephonyManager.NETWORK_TYPE_NR:
-                mUplinkFrequency = mDownlinkFrequency;
+                mUplinkFrequency = AccessNetworkUtils.getFrequencyFromNrArfcn(
+                        mUplinkChannelNumber);
                 break;
             case TelephonyManager.NETWORK_TYPE_LTE:
                 mUplinkFrequency = AccessNetworkUtils.getFrequencyFromEarfcn(
diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
index c18ab33..f004824 100644
--- a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
@@ -193,6 +193,10 @@
         return mDelegateBinder;
     }
 
+    public ISipDelegateStateCallback getStateCallbackBinder() {
+        return mStateBinder;
+    }
+
     private void notifyLocalMessageFailedToBeReceived(SipMessage m, int reason) {
         String transactionId = m.getViaBranchParameter();
         SipDelegate d = mDelegate;
diff --git a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
index 1f74c09..13ea9973 100644
--- a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
@@ -21,6 +21,7 @@
 import android.annotation.SystemApi;
 import android.os.Binder;
 import android.os.IBinder;
+import android.os.RemoteException;
 import android.telephony.ims.DelegateMessageCallback;
 import android.telephony.ims.DelegateRequest;
 import android.telephony.ims.DelegateStateCallback;
@@ -33,6 +34,7 @@
 import android.util.Log;
 
 import java.util.ArrayList;
+import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.concurrent.Executor;
 
@@ -49,10 +51,15 @@
 public class SipTransportImplBase {
     private static final String LOG_TAG = "SipTransportIB";
 
-    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+    private final IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
         @Override
         public void binderDied() {
-            mBinderExecutor.execute(() -> binderDiedInternal());
+            // Clean up all binders in this case.
+            mBinderExecutor.execute(() -> binderDiedInternal(null));
+        }
+        @Override
+        public void binderDied(IBinder who) {
+            mBinderExecutor.execute(() -> binderDiedInternal(who));
         }
     };
 
@@ -142,6 +149,7 @@
             ISipDelegateStateCallback cb, ISipDelegateMessageCallback mc) {
         SipDelegateAidlWrapper wrapper = new SipDelegateAidlWrapper(mBinderExecutor, cb, mc);
         mDelegates.add(wrapper);
+        linkDeathRecipient(wrapper);
         createSipDelegate(subId, r, wrapper, wrapper);
     }
 
@@ -155,6 +163,7 @@
         }
 
         if (result != null) {
+            unlinkDeathRecipient(result);
             mDelegates.remove(result);
             destroySipDelegate(result.getDelegate(), reason);
         } else {
@@ -163,12 +172,37 @@
         }
     }
 
-    private void binderDiedInternal() {
-        for (SipDelegateAidlWrapper w : mDelegates) {
-            destroySipDelegate(w.getDelegate(),
-                    SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD);
+    private void linkDeathRecipient(SipDelegateAidlWrapper w) {
+        try {
+            w.getStateCallbackBinder().asBinder().linkToDeath(mDeathRecipient, 0);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "linkDeathRecipient, remote process already died, cleaning up.");
+            mDeathRecipient.binderDied(w.getStateCallbackBinder().asBinder());
         }
-        mDelegates.clear();
+    }
+
+    private void unlinkDeathRecipient(SipDelegateAidlWrapper w) {
+        try {
+            w.getStateCallbackBinder().asBinder().unlinkToDeath(mDeathRecipient, 0);
+        } catch (NoSuchElementException e) {
+            // Ignore this case.
+        }
+    }
+
+    private void binderDiedInternal(IBinder who) {
+        for (SipDelegateAidlWrapper w : mDelegates) {
+            // If the binder itself was not given from the platform, just clean up all binders.
+            if (who == null || w.getStateCallbackBinder().asBinder().equals(who))  {
+                Log.w(LOG_TAG, "Binder death detected for " + w + ", calling destroy and "
+                        + "removing.");
+                mDelegates.remove(w);
+                destroySipDelegate(w.getDelegate(),
+                        SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD);
+                return;
+            }
+        }
+        Log.w(LOG_TAG, "Binder death detected for IBinder " + who + ", but couldn't find matching "
+                + "SipDelegate");
     }
 
     /**
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index 217a72b..7731e09 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -25,11 +25,17 @@
 
 android_test {
     name: "FlickerTests",
-    srcs: ["src/**/*.java", "src/**/*.kt"],
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
     manifest: "AndroidManifest.xml",
     test_config: "AndroidTest.xml",
     platform_apis: true,
     certificate: "platform",
+    optimize: {
+        enabled: false,
+    },
     test_suites: ["device-tests"],
     libs: ["android.test.runner"],
     static_libs: [
@@ -46,6 +52,9 @@
 java_library {
     name: "wm-flicker-common-assertions",
     platform_apis: true,
+    optimize: {
+        enabled: false,
+    },
     srcs: [
         "src/**/*Assertions.java",
         "src/**/*Assertions.kt",
@@ -56,20 +65,23 @@
     static_libs: [
         "flickerlib",
         "truth-prebuilt",
-        "app-helpers-core"
+        "app-helpers-core",
     ],
 }
 
 java_library {
     name: "wm-flicker-common-app-helpers",
     platform_apis: true,
+    optimize: {
+        enabled: false,
+    },
     srcs: [
-        "**/helpers/*"
+        "**/helpers/*",
     ],
     static_libs: [
         "flickerlib",
         "flickertestapplib",
         "truth-prebuilt",
-        "app-helpers-core"
+        "app-helpers-core",
     ],
-}
\ No newline at end of file
+}
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 a540dff..08c9e5d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -14,77 +14,30 @@
  * limitations under the License.
  */
 
+@file:JvmName("CommonAssertions")
 package com.android.server.wm.flicker
 
-import android.platform.helpers.IAppHelper
+import android.content.ComponentName
 import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.NAV_BAR_LAYER_NAME
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.NAV_BAR_WINDOW_NAME
-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
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 
-val HOME_WINDOW_TITLE = arrayOf("Wallpaper", "Launcher")
+val LAUNCHER_COMPONENT = ComponentName("com.google.android.apps.nexuslauncher",
+        "com.google.android.apps.nexuslauncher.NexusLauncherActivity")
 
-fun FlickerTestParameter.statusBarWindowIsAlwaysVisible() {
+fun FlickerTestParameter.statusBarWindowIsVisible() {
     assertWm {
-        this.showsAboveAppWindow(STATUS_BAR_WINDOW_NAME)
+        this.isAboveAppWindowVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
     }
 }
 
-fun FlickerTestParameter.navBarWindowIsAlwaysVisible() {
+fun FlickerTestParameter.navBarWindowIsVisible() {
     assertWm {
-        this.showsAboveAppWindow(NAV_BAR_WINDOW_NAME)
-    }
-}
-
-fun FlickerTestParameter.launcherReplacesAppWindowAsTopWindow(testApp: IAppHelper) {
-    assertWm {
-        this.showsAppWindowOnTop(testApp.getPackage())
-            .then()
-            .showsAppWindowOnTop(*HOME_WINDOW_TITLE)
-    }
-}
-
-fun FlickerTestParameter.launcherWindowBecomesVisible() {
-    assertWm {
-        this.hidesBelowAppWindow(*HOME_WINDOW_TITLE)
-            .then()
-            .showsBelowAppWindow(*HOME_WINDOW_TITLE)
-    }
-}
-
-fun FlickerTestParameter.launcherWindowBecomesInvisible() {
-    assertWm {
-        this.showsBelowAppWindow(*HOME_WINDOW_TITLE)
-            .then()
-            .hidesBelowAppWindow(*HOME_WINDOW_TITLE)
-    }
-}
-
-fun FlickerTestParameter.appWindowAlwaysVisibleOnTop(packageName: String) {
-    assertWm {
-        this.showsAppWindowOnTop(packageName)
-    }
-}
-
-fun FlickerTestParameter.appWindowBecomesVisible(appName: String) {
-    assertWm {
-        this.hidesAppWindow(appName)
-            .then()
-            .showsAppWindow(appName)
-    }
-}
-
-fun FlickerTestParameter.appWindowBecomesInVisible(appName: String) {
-    assertWm {
-        this.showsAppWindow(appName)
-            .then()
-            .hidesAppWindow(appName)
+        this.isAboveAppWindowVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
     }
 }
 
 @JvmOverloads
-fun FlickerTestParameter.noUncoveredRegions(
+fun FlickerTestParameter.entireScreenCovered(
     beginRotation: Int,
     endRotation: Int = beginRotation,
     allStates: Boolean = true
@@ -111,37 +64,21 @@
     }
 }
 
-@JvmOverloads
-fun FlickerTestParameter.navBarLayerIsAlwaysVisible(rotatesScreen: Boolean = false) {
-    if (rotatesScreen) {
-        assertLayers {
-            this.isVisible(NAV_BAR_LAYER_NAME)
-                .then()
-                .isInvisible(NAV_BAR_LAYER_NAME)
-                .then()
-                .isVisible(NAV_BAR_LAYER_NAME)
-        }
-    } else {
-        assertLayers {
-            this.isVisible(NAV_BAR_LAYER_NAME)
-        }
+fun FlickerTestParameter.navBarLayerIsVisible() {
+    assertLayersStart {
+        this.isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+    }
+    assertLayersEnd {
+        this.isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
     }
 }
 
-@JvmOverloads
-fun FlickerTestParameter.statusBarLayerIsAlwaysVisible(rotatesScreen: Boolean = false) {
-    if (rotatesScreen) {
-        assertLayers {
-            this.isVisible(STATUS_BAR_LAYER_NAME)
-                .then()
-                .isInvisible(STATUS_BAR_LAYER_NAME)
-                .then()
-                .isVisible(STATUS_BAR_LAYER_NAME)
-        }
-    } else {
-        assertLayers {
-            this.isVisible(STATUS_BAR_LAYER_NAME)
-        }
+fun FlickerTestParameter.statusBarLayerIsVisible() {
+    assertLayersStart {
+        this.isVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+    }
+    assertLayersEnd {
+        this.isVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
     }
 }
 
@@ -154,10 +91,10 @@
     val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
 
     assertLayersStart {
-        this.visibleRegion(NAV_BAR_LAYER_NAME).coversExactly(startingPos)
+        this.visibleRegion(WindowManagerStateHelper.NAV_BAR_COMPONENT).coversExactly(startingPos)
     }
     assertLayersEnd {
-        this.visibleRegion(NAV_BAR_LAYER_NAME).coversExactly(endingPos)
+        this.visibleRegion(WindowManagerStateHelper.NAV_BAR_COMPONENT).coversExactly(endingPos)
     }
 }
 
@@ -170,54 +107,46 @@
     val endingPos = WindowUtils.getStatusBarPosition(endRotation)
 
     assertLayersStart {
-        this.visibleRegion(STATUS_BAR_LAYER_NAME).coversExactly(startingPos)
+        this.visibleRegion(WindowManagerStateHelper.STATUS_BAR_COMPONENT).coversExactly(startingPos)
     }
     assertLayersEnd {
-        this.visibleRegion(STATUS_BAR_LAYER_NAME).coversExactly(endingPos)
+        this.visibleRegion(WindowManagerStateHelper.STATUS_BAR_COMPONENT).coversExactly(endingPos)
     }
 }
 
-fun FlickerTestParameter.appLayerReplacesLauncher(appName: String) {
+/**
+ * Asserts that:
+ *     [originalLayer] is visible at the start of the trace
+ *     [originalLayer] becomes invisible during the trace and (in the same entry) [newLayer]
+ *         becomes visible
+ *     [newLayer] remains visible until the end of the trace
+ *
+ * @param originalLayer Layer that should be visible at the start
+ * @param newLayer Layer that should be visible at the end
+ * @param ignoreSnapshot If the snapshot layer should be ignored during the transition
+ *     (useful mostly for app launch)
+ */
+fun FlickerTestParameter.replacesLayer(
+    originalLayer: ComponentName,
+    newLayer: ComponentName,
+    ignoreSnapshot: Boolean = false
+) {
     assertLayers {
-        this.isVisible(*HOME_WINDOW_TITLE)
-            .then()
-            .isVisible(appName)
+        val assertion = this.isVisible(originalLayer)
+        if (ignoreSnapshot) {
+            assertion.then()
+                    .isVisible(WindowManagerStateHelper.SNAPSHOT_COMPONENT, isOptional = true)
+        }
+        assertion.then().isVisible(newLayer)
+    }
+
+    assertLayersStart {
+        this.isVisible(originalLayer)
+                .isInvisible(newLayer)
+    }
+
+    assertLayersEnd {
+        this.isInvisible(originalLayer)
+                .isVisible(newLayer)
     }
 }
-
-fun FlickerTestParameter.launcherLayerReplacesApp(testApp: IAppHelper) {
-    assertLayers {
-        this.isVisible(testApp.getPackage())
-            .then()
-            .isInvisible(testApp.getPackage())
-            .isVisible(*HOME_WINDOW_TITLE)
-    }
-}
-
-fun FlickerTestParameter.layerBecomesVisible(packageName: String) {
-    assertLayers {
-        this.isInvisible(packageName)
-            .then()
-            .isVisible(packageName)
-    }
-}
-
-fun FlickerTestParameter.layerBecomesInvisible(packageName: String) {
-    assertLayers {
-        this.isVisible(packageName)
-            .then()
-            .isInvisible(packageName)
-    }
-}
-
-fun FlickerTestParameter.focusChanges(vararg windows: String) {
-    assertEventLog {
-        this.focusChanges(windows)
-    }
-}
-
-fun FlickerTestParameter.focusDoesNotChange() {
-    assertEventLog {
-        this.focusDoesNotChange()
-    }
-}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index 71184c2..90c851d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -1,3 +1,4 @@
+
 /*
  * Copyright (C) 2020 The Android Open Source Project
  *
@@ -16,6 +17,8 @@
 
 package com.android.server.wm.flicker.close
 
+import android.platform.test.annotations.Postsubmit
+import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
@@ -23,6 +26,7 @@
 import com.android.server.wm.flicker.annotation.Group1
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import org.junit.FixMethodOrder
+import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -46,6 +50,13 @@
             }
         }
 
+    @FlakyTest
+    @Test
+    override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+    @Postsubmit
+    override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index 6786279..e8391ed 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -16,6 +16,8 @@
 
 package com.android.server.wm.flicker.close
 
+import android.platform.test.annotations.Postsubmit
+import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
@@ -23,6 +25,7 @@
 import com.android.server.wm.flicker.annotation.Group1
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import org.junit.FixMethodOrder
+import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -46,6 +49,13 @@
             }
         }
 
+    @FlakyTest
+    @Test
+    override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+    @Postsubmit
+    override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index f7f977d..f9e6bab 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -19,30 +19,35 @@
 import android.app.Instrumentation
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
-import androidx.test.filters.FlakyTest
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.LAUNCHER_COMPONENT
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.SimpleAppHelper
 import com.android.server.wm.flicker.helpers.StandardAppHelper
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.launcherReplacesAppWindowAsTopWindow
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.launcherLayerReplacesApp
-import com.android.server.wm.flicker.launcherWindowBecomesVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.flicker.replacesLayer
 import org.junit.Test
 
+/**
+ * Base test class for transitions that close an app back to the launcher screen
+ */
 abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter) {
     protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
     protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
+
+    /**
+     * Specification of the test transition to execute
+     */
     protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = {
         setup {
             eachRun {
@@ -66,29 +71,29 @@
 
     @Presubmit
     @Test
-    open fun navBarWindowIsAlwaysVisible() {
-        testSpec.navBarWindowIsAlwaysVisible()
+    open fun navBarWindowIsVisible() {
+        testSpec.navBarWindowIsVisible()
     }
 
     @Presubmit
     @Test
-    open fun statusBarWindowIsAlwaysVisible() {
-        testSpec.statusBarWindowIsAlwaysVisible()
-    }
-
-    @FlakyTest
-    @Test
-    open fun navBarLayerIsAlwaysVisible() {
-        testSpec.navBarLayerIsAlwaysVisible(rotatesScreen = testSpec.isRotated)
+    open fun statusBarWindowIsVisible() {
+        testSpec.statusBarWindowIsVisible()
     }
 
     @Presubmit
     @Test
-    open fun statusBarLayerIsAlwaysVisible() {
-        testSpec.statusBarLayerIsAlwaysVisible(rotatesScreen = testSpec.isRotated)
+    open fun navBarLayerIsVisible() {
+        testSpec.navBarLayerIsVisible()
     }
 
-    @FlakyTest
+    @Presubmit
+    @Test
+    open fun statusBarLayerIsVisible() {
+        testSpec.statusBarLayerIsVisible()
+    }
+
+    @Presubmit
     @Test
     open fun navBarLayerRotatesAndScales() {
         testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
@@ -118,25 +123,33 @@
 
     @Presubmit
     @Test
-    open fun noUncoveredRegions() {
-        testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
+    open fun entireScreenCovered() {
+        testSpec.entireScreenCovered(testSpec.config.startRotation, Surface.ROTATION_0)
     }
 
     @Presubmit
     @Test
     open fun launcherReplacesAppWindowAsTopWindow() {
-        testSpec.launcherReplacesAppWindowAsTopWindow(testApp)
+        testSpec.assertWm {
+            this.isAppWindowOnTop(testApp.component)
+                    .then()
+                    .isAppWindowOnTop(LAUNCHER_COMPONENT)
+        }
     }
 
     @Presubmit
     @Test
     open fun launcherWindowBecomesVisible() {
-        testSpec.launcherWindowBecomesVisible()
+        testSpec.assertWm {
+            this.isAppWindowInvisible(LAUNCHER_COMPONENT)
+                    .then()
+                    .isAppWindowOnTop(LAUNCHER_COMPONENT)
+        }
     }
 
     @Presubmit
     @Test
     open fun launcherLayerReplacesApp() {
-        testSpec.launcherLayerReplacesApp(testApp)
+        testSpec.replacesLayer(testApp.component, LAUNCHER_COMPONENT)
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
index 83fddae..d224af9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
@@ -61,7 +61,8 @@
         if (wmHelper == null) {
             device.waitForIdle()
         } else {
-            wmHelper.waitImeWindowShown()
+            wmHelper.waitImeShown()
+            wmHelper.waitForAppTransitionIdle()
         }
     }
 
@@ -78,7 +79,7 @@
         if (wmHelper == null) {
             device.waitForIdle()
         } else {
-            wmHelper.waitImeWindowGone()
+            wmHelper.waitImeGone()
         }
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index b5757fd..384d8e8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -18,8 +18,8 @@
 
 import android.app.Instrumentation
 import android.platform.test.annotations.Presubmit
+import android.view.Surface
 import android.view.WindowManagerPolicyConstants
-import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -28,15 +28,15 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -46,6 +46,14 @@
 
 /**
  * Test IME window closing back to app window transitions.
+ *
+ * This test doesn't work on 90 degrees. According to the InputMethodService documentation:
+ *
+ *     Don't show if this is not explicitly requested by the user and the input method
+ *     is fullscreen. That would be too disruptive.
+ *
+ * More details on b/190352379
+ *
  * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToAppTest`
  */
 @RequiresDevice
@@ -79,37 +87,55 @@
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
 
     @Presubmit
     @Test
     fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
         testSpec.assertWm {
-            this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE,
-                WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-                WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME))
+            this.visibleWindowsShownMoreThanOneConsecutiveEntry()
         }
     }
 
     @Presubmit
     @Test
-    fun imeAppWindowIsAlwaysVisible() = testSpec.imeAppWindowIsAlwaysVisible(testApp)
+    fun imeAppWindowIsAlwaysVisible() {
+        testSpec.assertWm {
+            this.isAppWindowOnTop(testApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+    fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
 
-    @FlakyTest
+    @Presubmit
     @Test
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation)
+    fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation)
+
+    @Presubmit
+    @Test
+    fun imeLayerVisibleStart() {
+        testSpec.assertLayersStart {
+            this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
+        }
+    }
+
+    @Presubmit
+    @Test
+    fun imeLayerInvisibleEnd() {
+        testSpec.assertLayersEnd {
+            this.isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+        }
+    }
 
     @Presubmit
     @Test
@@ -117,15 +143,19 @@
 
     @Presubmit
     @Test
-    fun imeAppLayerIsAlwaysVisible() = testSpec.imeAppLayerIsAlwaysVisible(testApp)
+    fun imeAppLayerIsAlwaysVisible() {
+        testSpec.assertLayers {
+            this.isVisible(testApp.component)
+        }
+    }
 
-    @FlakyTest
+    @Presubmit
     @Test
     fun navBarLayerRotatesAndScales() {
         testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
     }
 
-    @FlakyTest
+    @Presubmit
     @Test
     fun statusBarLayerRotatesScales() {
         testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation)
@@ -145,8 +175,11 @@
         fun getParams(): Collection<FlickerTestParameter> {
             return FlickerTestParameterFactory.getInstance()
                 .getConfigNonRotationTests(repetitions = 5,
+                    // b/190352379 (IME doesn't show on app launch in 90 degrees)
+                    supportedRotations = listOf(Surface.ROTATION_0),
                     supportedNavigationModes = listOf(
-                        WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY)
+                        WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
+                        WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)
                 )
         }
     }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index 549e44c..ade215b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -30,14 +30,14 @@
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -47,6 +47,14 @@
 
 /**
  * Test IME window closing back to app window transitions.
+ *
+ * This test doesn't work on 90 degrees. According to the InputMethodService documentation:
+ *
+ *     Don't show if this is not explicitly requested by the user and the input method
+ *     is fullscreen. That would be too disruptive.
+ *
+ * More details on b/190352379
+ *
  * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToHomeTest`
  */
 @RequiresDevice
@@ -75,51 +83,73 @@
             transitions {
                 device.pressHome()
                 wmHelper.waitForHomeActivityVisible()
-                wmHelper.waitImeWindowGone()
+                wmHelper.waitImeGone()
             }
         }
     }
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
 
     @Presubmit
     @Test
     fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
         testSpec.assertWm {
-            this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE,
-                WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-                WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME))
+            this.visibleWindowsShownMoreThanOneConsecutiveEntry()
         }
     }
 
-    @FlakyTest
+    @FlakyTest(bugId = 190189685)
     @Test
-    fun imeWindowBecomesInvisible() = testSpec.imeWindowBecomesInvisible()
-
-    @FlakyTest
-    @Test
-    fun imeAppWindowBecomesInvisible() = testSpec.imeAppWindowBecomesInvisible(testApp)
+    fun imeAppWindowBecomesInvisible() {
+        testSpec.assertWm {
+            this.isAppWindowOnTop(testApp.component)
+                .then()
+                .appWindowNotOnTop(testApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
+    fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation,
         Surface.ROTATION_0)
 
-    @FlakyTest
+    @Presubmit
+    @Test
+    fun imeLayerVisibleStart() {
+        testSpec.assertLayersStart {
+            this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
+        }
+    }
+
+    @Presubmit
+    @Test
+    fun imeLayerInvisibleEnd() {
+        testSpec.assertLayersEnd {
+            this.isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+        }
+    }
+
+    @Presubmit
     @Test
     fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible()
 
     @Presubmit
     @Test
-    fun imeAppLayerBecomesInvisible() = testSpec.imeAppLayerBecomesInvisible(testApp)
+    fun imeAppLayerBecomesInvisible() {
+        testSpec.assertLayers {
+            this.isVisible(testApp.component)
+                    .then()
+                    .isInvisible(testApp.component)
+        }
+    }
 
-    @FlakyTest
+    @Presubmit
     @Test
     fun navBarLayerRotatesAndScales() {
         testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
@@ -133,18 +163,19 @@
 
     @Presubmit
     @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
 
-    @FlakyTest
+    @Presubmit
     @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+    fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
 
     @Presubmit
     @Test
     fun visibleLayersShownMoreThanOneConsecutiveEntry() {
         testSpec.assertLayers {
-            this.visibleLayersShownMoreThanOneConsecutiveEntry(
-                listOf(IME_WINDOW_TITLE, WindowManagerStateHelper.SPLASH_SCREEN_NAME))
+            this.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(
+                    WindowManagerStateHelper.IME_COMPONENT,
+                    WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT))
         }
     }
 
@@ -154,8 +185,11 @@
         fun getParams(): Collection<FlickerTestParameter> {
             return FlickerTestParameterFactory.getInstance()
                 .getConfigNonRotationTests(repetitions = 1,
+                    // b/190352379 (IME doesn't show on app launch in 90 degrees)
+                    supportedRotations = listOf(Surface.ROTATION_0),
                     supportedNavigationModes = listOf(
-                        WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY)
+                        WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
+                        WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)
                 )
         }
     }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index 82ca074..cdfcff3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -28,13 +28,13 @@
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
 import org.junit.Assume
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import org.junit.FixMethodOrder
@@ -61,7 +61,7 @@
         return FlickerBuilder(instrumentation).apply {
             setup {
                 test {
-                    testApp.launchViaIntent()
+                    testApp.launchViaIntent(wmHelper)
                 }
                 eachRun {
                     testApp.openIME(device, wmHelper)
@@ -80,37 +80,42 @@
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
 
     @Presubmit
     @Test
     fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
         testSpec.assertWm {
-            this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE,
-                WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-                WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME))
+            this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(
+                WindowManagerStateHelper.IME_COMPONENT,
+                WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+                WindowManagerStateHelper.SNAPSHOT_COMPONENT))
         }
     }
 
     @Presubmit
     @Test
-    fun imeAppWindowIsAlwaysVisible() = testSpec.imeAppWindowIsAlwaysVisible(testApp)
+    fun imeAppWindowIsAlwaysVisible() {
+        testSpec.assertWm {
+            this.isAppWindowOnTop(testApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    fun statusBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
 
     @Presubmit
     @Test
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation)
+    fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation)
 
     @Presubmit
     @Test
@@ -146,7 +151,11 @@
 
     @Presubmit
     @Test
-    fun imeAppLayerIsAlwaysVisible() = testSpec.imeAppLayerIsAlwaysVisible(testApp)
+    fun imeAppLayerIsAlwaysVisible() {
+        testSpec.assertLayers {
+            this.isVisible(testApp.component)
+        }
+    }
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index 703e4a1..05fc267 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -30,13 +30,13 @@
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -68,7 +68,7 @@
             transitions {
                 device.pressHome()
                 wmHelper.waitForHomeActivityVisible()
-                wmHelper.waitImeWindowGone()
+                wmHelper.waitImeGone()
             }
             teardown {
                 eachRun {
@@ -84,19 +84,20 @@
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
 
     @Presubmit
     @Test
     fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
         testSpec.assertWm {
-            this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE,
-                WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-                WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME))
+            this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(
+                WindowManagerStateHelper.IME_COMPONENT,
+                WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+                WindowManagerStateHelper.SNAPSHOT_COMPONENT))
         }
     }
 
@@ -106,19 +107,25 @@
 
     @FlakyTest
     @Test
-    fun imeAppWindowBecomesInvisible() = testSpec.imeAppWindowBecomesInvisible(testApp)
+    fun imeAppWindowBecomesInvisible() {
+        testSpec.assertWm {
+            this.isAppWindowVisible(testApp.component)
+                    .then()
+                    .isAppWindowInvisible(testApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    fun statusBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
 
     @Presubmit
     @Test
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
+    fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation,
         Surface.ROTATION_0)
 
     @Presubmit
@@ -127,7 +134,13 @@
 
     @Presubmit
     @Test
-    fun imeAppLayerBecomesInvisible() = testSpec.imeAppLayerBecomesInvisible(testApp)
+    fun imeAppLayerBecomesInvisible() {
+        testSpec.assertLayers {
+            this.isVisible(testApp.component)
+                    .then()
+                    .isInvisible(testApp.component)
+        }
+    }
 
     @Presubmit
     @Test
@@ -144,8 +157,9 @@
     @Test
     fun visibleLayersShownMoreThanOneConsecutiveEntry() {
         testSpec.assertLayers {
-            this.visibleLayersShownMoreThanOneConsecutiveEntry(
-                listOf(IME_WINDOW_TITLE, WindowManagerStateHelper.SPLASH_SCREEN_NAME))
+            this.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(
+                    WindowManagerStateHelper.IME_COMPONENT,
+                    WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT))
         }
     }
 
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
index 7e34469..7659d94 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
@@ -14,128 +14,56 @@
  * limitations under the License.
  */
 
+@file:JvmName("CommonAssertions")
 package com.android.server.wm.flicker.ime
 
-import android.platform.helpers.IAppHelper
 import com.android.server.wm.flicker.FlickerTestParameter
-
-const val IME_WINDOW_TITLE = "InputMethod"
-
-fun FlickerTestParameter.imeLayerIsAlwaysVisible(rotatesScreen: Boolean = false) {
-    if (rotatesScreen) {
-        assertLayers {
-            this.isVisible(IME_WINDOW_TITLE)
-                .then()
-                .isInvisible(IME_WINDOW_TITLE)
-                .then()
-                .isVisible(IME_WINDOW_TITLE)
-        }
-    } else {
-        assertLayers {
-            this.isVisible(IME_WINDOW_TITLE)
-        }
-    }
-}
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 
 fun FlickerTestParameter.imeLayerBecomesVisible() {
     assertLayers {
-        this.isInvisible(IME_WINDOW_TITLE)
+        this.isInvisible(WindowManagerStateHelper.IME_COMPONENT)
             .then()
-            .isVisible(IME_WINDOW_TITLE)
+            .isVisible(WindowManagerStateHelper.IME_COMPONENT)
     }
 }
 
 fun FlickerTestParameter.imeLayerBecomesInvisible() {
     assertLayers {
-        this.isVisible(IME_WINDOW_TITLE)
+        this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
             .then()
-            .isInvisible(IME_WINDOW_TITLE)
-    }
-}
-
-fun FlickerTestParameter.imeAppLayerIsAlwaysVisible(testApp: IAppHelper) {
-    assertLayers {
-        this.isVisible(testApp.getPackage())
-    }
-}
-
-fun FlickerTestParameter.imeAppWindowIsAlwaysVisible(testApp: IAppHelper) {
-    assertWm {
-        this.showsAppWindowOnTop(testApp.getPackage())
+            .isInvisible(WindowManagerStateHelper.IME_COMPONENT)
     }
 }
 
 fun FlickerTestParameter.imeWindowIsAlwaysVisible(rotatesScreen: Boolean = false) {
     if (rotatesScreen) {
         assertWm {
-            this.showsNonAppWindow(IME_WINDOW_TITLE)
+            this.isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
                 .then()
-                .hidesNonAppWindow(IME_WINDOW_TITLE)
+                .isNonAppWindowInvisible(WindowManagerStateHelper.IME_COMPONENT)
                 .then()
-                .showsNonAppWindow(IME_WINDOW_TITLE)
+                .isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
         }
     } else {
         assertWm {
-            this.showsNonAppWindow(IME_WINDOW_TITLE)
+            this.isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
         }
     }
 }
 
 fun FlickerTestParameter.imeWindowBecomesVisible() {
     assertWm {
-        this.hidesNonAppWindow(IME_WINDOW_TITLE)
+        this.isNonAppWindowInvisible(WindowManagerStateHelper.IME_COMPONENT)
             .then()
-            .showsNonAppWindow(IME_WINDOW_TITLE)
+            .isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
     }
 }
 
 fun FlickerTestParameter.imeWindowBecomesInvisible() {
     assertWm {
-        this.showsNonAppWindow(IME_WINDOW_TITLE)
+        this.isNonAppWindowVisible(WindowManagerStateHelper.IME_COMPONENT)
             .then()
-            .hidesNonAppWindow(IME_WINDOW_TITLE)
+            .isNonAppWindowInvisible(WindowManagerStateHelper.IME_COMPONENT)
     }
 }
-
-fun FlickerTestParameter.imeAppWindowIsAlwaysVisible(
-    testApp: IAppHelper,
-    rotatesScreen: Boolean = false
-) {
-    if (rotatesScreen) {
-        assertWm {
-            this.showsAppWindow(testApp.getPackage())
-                .then()
-                .hidesAppWindow(testApp.getPackage())
-                .then()
-                .showsAppWindow(testApp.getPackage())
-        }
-    } else {
-        assertWm {
-            this.showsAppWindow(testApp.getPackage())
-        }
-    }
-}
-
-fun FlickerTestParameter.imeAppWindowBecomesVisible(windowName: String) {
-    assertWm {
-        this.hidesAppWindow(windowName)
-            .then()
-            .showsAppWindow(windowName)
-    }
-}
-
-fun FlickerTestParameter.imeAppWindowBecomesInvisible(testApp: IAppHelper) {
-    assertWm {
-        this.showsAppWindowOnTop(testApp.getPackage())
-            .then()
-            .appWindowNotOnTop(testApp.getPackage())
-    }
-}
-
-fun FlickerTestParameter.imeAppLayerBecomesInvisible(testApp: IAppHelper) {
-    assertLayers {
-        this.isVisible(testApp.getPackage())
-            .then()
-            .isInvisible(testApp.getPackage())
-    }
-}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index cae1b16..f35a180 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -28,16 +28,15 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.appWindowAlwaysVisibleOnTop
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -81,11 +80,11 @@
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
 
     @Presubmit
     @Test
@@ -93,19 +92,23 @@
 
     @Presubmit
     @Test
-    fun appWindowAlwaysVisibleOnTop() = testSpec.appWindowAlwaysVisibleOnTop(testApp.`package`)
+    fun appWindowAlwaysVisibleOnTop() {
+        testSpec.assertWm {
+            this.isAppWindowOnTop(testApp.component)
+        }
+    }
 
     @Presubmit
     @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+    fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
 
     @Presubmit
     @Test
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation)
+    fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation)
 
     @Presubmit
     @Test
@@ -115,7 +118,7 @@
     @Test
     fun layerAlwaysVisible() {
         testSpec.assertLayers {
-            this.isVisible(testApp.`package`)
+            this.isVisible(testApp.component)
         }
     }
 
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index b7673d5..3bcf793 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -17,6 +17,7 @@
 package com.android.server.wm.flicker.ime
 
 import android.app.Instrumentation
+import android.content.ComponentName
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import android.view.WindowManagerPolicyConstants
@@ -26,23 +27,22 @@
 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_COMPONENT
 import com.android.server.wm.flicker.annotation.Group2
 import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
 import com.android.server.wm.flicker.helpers.reopenAppFromOverview
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.launcherWindowBecomesInvisible
-import com.android.server.wm.flicker.appLayerReplacesLauncher
+import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -61,7 +61,6 @@
 class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
     private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
     private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation)
-    private val testAppComponentName = ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME
 
     @FlickerBuilderProvider
     fun buildFlicker(): FlickerBuilder {
@@ -73,14 +72,14 @@
                 }
                 eachRun {
                     device.pressRecentApps()
-                    wmHelper.waitImeWindowGone()
+                    wmHelper.waitImeGone()
                     wmHelper.waitForAppTransitionIdle()
                     this.setRotation(testSpec.config.startRotation)
                 }
             }
             transitions {
                 device.reopenAppFromOverview(wmHelper)
-                wmHelper.waitImeWindowShown()
+                wmHelper.waitImeShown()
             }
             teardown {
                 test {
@@ -92,23 +91,34 @@
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
 
     @Presubmit
     @Test
     fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+        val component = ComponentName("", "RecentTaskScreenshotSurface")
         testSpec.assertWm {
-            this.visibleWindowsShownMoreThanOneConsecutiveEntry()
+            this.visibleWindowsShownMoreThanOneConsecutiveEntry(
+                    ignoreWindows = listOf(WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+                            WindowManagerStateHelper.SNAPSHOT_COMPONENT,
+                            component)
+            )
         }
     }
 
     @Presubmit
     @Test
-    fun launcherWindowBecomesInvisible() = testSpec.launcherWindowBecomesInvisible()
+    fun launcherWindowBecomesInvisible() {
+        testSpec.assertWm {
+            this.isAppWindowVisible(LAUNCHER_COMPONENT)
+                    .then()
+                    .isAppWindowInvisible(LAUNCHER_COMPONENT)
+        }
+    }
 
     @Presubmit
     @Test
@@ -116,30 +126,57 @@
 
     @Presubmit
     @Test
-    fun imeAppWindowIsAlwaysVisible() = testSpec.imeAppWindowIsAlwaysVisible(testApp, true)
+    fun imeAppWindowVisibility() {
+        // the app starts visible in live tile, then becomes invisible during animation and
+        // is again launched. Since we log 1x per frame, sometimes the activity visibility and
+        // the app visibility are updated together, sometimes not, thus ignore activity check
+        // at the start
+        testSpec.assertWm {
+            this.isAppWindowVisible(testApp.component, ignoreActivity = true)
+                    .then()
+                    .isAppWindowInvisible(testApp.component, ignoreActivity = true)
+                    .then()
+                    .isAppWindowVisible(testApp.component)
+        }
+    }
 
     @Presubmit
     @Test
     // During testing the launcher is always in portrait mode
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
+    fun entireScreenCovered() = testSpec.entireScreenCovered(testSpec.config.startRotation,
         testSpec.config.endRotation)
 
     @Presubmit
     @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
 
     @Presubmit
     @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+    fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
 
     @Presubmit
     @Test
-    fun imeLayerIsAlwaysVisible() = testSpec.imeLayerIsAlwaysVisible(true)
+    fun imeLayerIsBecomesVisible() {
+        testSpec.assertLayers {
+            this.isVisible(WindowManagerStateHelper.IME_COMPONENT)
+                    .then()
+                    .isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+                    .then()
+                    .isVisible(WindowManagerStateHelper.IME_COMPONENT)
+        }
+    }
 
     @Presubmit
     @Test
-    fun appLayerReplacesLauncher() =
-        testSpec.appLayerReplacesLauncher(testAppComponentName.className)
+    fun appLayerReplacesLauncher() {
+        testSpec.assertLayers {
+            this.isVisible(LAUNCHER_COMPONENT)
+                .then()
+                .isVisible(WindowManagerStateHelper.SNAPSHOT_COMPONENT, isOptional = true)
+                .then()
+                .isVisible(testApp.component)
+        }
+    }
 
     @Presubmit
     @Test
@@ -156,8 +193,14 @@
     @Presubmit
     @Test
     fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+        // depends on how much of the animation transactions are sent to SF at once
+        // sometimes this layer appears for 2-3 frames, sometimes for only 1
+        val recentTaskComponent = ComponentName("", "RecentTaskScreenshotSurface")
         testSpec.assertLayers {
-            this.visibleLayersShownMoreThanOneConsecutiveEntry()
+            this.visibleLayersShownMoreThanOneConsecutiveEntry(
+                    listOf(WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+                    WindowManagerStateHelper.SNAPSHOT_COMPONENT, recentTaskComponent)
+            )
         }
     }
 
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
index 0cae37c..f9dd88e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
@@ -17,27 +17,26 @@
 package com.android.server.wm.flicker.ime
 
 import android.app.Instrumentation
+import android.content.ComponentName
 import android.platform.test.annotations.Presubmit
+import android.view.Surface
 import android.view.WindowManagerPolicyConstants
-import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
-
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.FlickerBuilderProvider
 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.annotation.Group2
-import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
 import com.android.server.wm.flicker.helpers.SimpleAppHelper
 import com.android.server.wm.flicker.helpers.WindowUtils
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -54,10 +53,11 @@
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @Group2
+@Presubmit
 class SwitchImeWindowsFromGestureNavTest(private val testSpec: FlickerTestParameter) {
     private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
     private val testApp = SimpleAppHelper(instrumentation)
-    private val imeTestApp = ImeAppHelper(instrumentation)
+    private val imeTestApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation)
 
     @FlickerBuilderProvider
     fun buildFlicker(): FlickerBuilder {
@@ -66,7 +66,13 @@
                 eachRun {
                     this.setRotation(testSpec.config.startRotation)
                     testApp.launchViaIntent(wmHelper)
+                    wmHelper.waitForFullScreenApp(testApp.component)
+                    wmHelper.waitForAppTransitionIdle()
+
                     imeTestApp.launchViaIntent(wmHelper)
+                    wmHelper.waitForFullScreenApp(testApp.component)
+                    wmHelper.waitForAppTransitionIdle()
+
                     imeTestApp.openIME(device, wmHelper)
                 }
             }
@@ -74,57 +80,86 @@
                 eachRun {
                     device.pressHome()
                     wmHelper.waitForHomeActivityVisible()
-                }
-                test {
-                    imeTestApp.exit(wmHelper)
+                    testApp.exit()
+                    imeTestApp.exit()
                 }
             }
             transitions {
                 // [Step1]: Swipe right from imeTestApp to testApp task
+                createTag(TAG_IME_VISIBLE)
                 val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
-                val displayCenterX = displayBounds.bounds.width() / 2
-                device.swipe(displayCenterX, displayBounds.bounds.height(),
-                        displayBounds.bounds.width(), displayBounds.bounds.height(), 20)
+                device.swipe(0, displayBounds.bounds.height(),
+                        displayBounds.bounds.width(), displayBounds.bounds.height(), 50)
+
                 wmHelper.waitForFullScreenApp(testApp.component)
+                wmHelper.waitForAppTransitionIdle()
+                createTag(TAG_IME_INVISIBLE)
             }
             transitions {
                 // [Step2]: Swipe left to back to imeTestApp task
                 val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
-                val displayCenterX = displayBounds.bounds.width() / 2
                 device.swipe(displayBounds.bounds.width(), displayBounds.bounds.height(),
-                        displayCenterX, displayBounds.bounds.height(), 20)
+                        0, displayBounds.bounds.height(), 50)
                 wmHelper.waitForFullScreenApp(imeTestApp.component)
             }
         }
     }
 
-    @FlakyTest
     @Test
-    fun imeAppWindowIsAlwaysVisible() = testSpec.imeAppWindowIsAlwaysVisible(imeTestApp)
+    fun imeAppWindowVisibility() {
+        val component = ComponentName(imeTestApp.`package`, "")
+        testSpec.assertWm {
+            this.isAppWindowOnTop(component)
+                    .then()
+                    .isAppWindowVisible(component, ignoreActivity = true)
+        }
+    }
 
-    @FlakyTest
     @Test
-    fun imeLayerBecomesVisible() = testSpec.imeLayerBecomesVisible()
+    fun navBarLayerIsVisibleAroundSwitching() {
+        testSpec.assertLayersStart {
+            isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+        }
+        testSpec.assertLayersEnd {
+            isVisible(WindowManagerStateHelper.NAV_BAR_COMPONENT)
+        }
+    }
 
-    @FlakyTest
     @Test
-    fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible()
+    fun statusBarLayerIsVisibleAroundSwitching() {
+        testSpec.assertLayersStart {
+            isVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+        }
+        testSpec.assertLayersEnd {
+            isVisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+        }
+    }
 
-    @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    fun imeLayerIsVisibleWhenSwitchingToImeApp() {
+        testSpec.assertLayersStart {
+            isVisible(WindowManagerStateHelper.IME_COMPONENT)
+        }
+        testSpec.assertLayersTag(TAG_IME_VISIBLE) {
+            isVisible(WindowManagerStateHelper.IME_COMPONENT)
+        }
+        testSpec.assertLayersEnd {
+            isVisible(WindowManagerStateHelper.IME_COMPONENT)
+        }
+    }
 
-    @FlakyTest
     @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    fun imeLayerIsInvisibleWhenSwitchingToTestApp() {
+        testSpec.assertLayersTag(TAG_IME_INVISIBLE) {
+            isInvisible(WindowManagerStateHelper.IME_COMPONENT)
+        }
+    }
 
-    @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
 
-    @FlakyTest
     @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+    fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
@@ -134,10 +169,13 @@
                     .getConfigNonRotationTests(
                             repetitions = 3,
                             supportedNavigationModes = listOf(
-                                    WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
                                     WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
-                            )
+                            ),
+                            supportedRotations = listOf(Surface.ROTATION_0)
                     )
         }
+
+        private const val TAG_IME_VISIBLE = "imeVisible"
+        private const val TAG_IME_INVISIBLE = "imeInVisible"
     }
 }
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
deleted file mode 100644
index 01e34d9..0000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
+++ /dev/null
@@ -1,29 +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 com.android.server.wm.flicker.launch
-
-import android.platform.helpers.IAppHelper
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.HOME_WINDOW_TITLE
-
-fun FlickerTestParameter.appWindowReplacesLauncherAsTopWindow(testApp: IAppHelper) {
-    assertWm {
-        this.showsAppWindowOnTop(*HOME_WINDOW_TITLE)
-            .then()
-            .showsAppWindowOnTop("Snapshot", testApp.getPackage())
-    }
-}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index 9ff0bdf..e6dc852 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -64,35 +64,17 @@
 
     @FlakyTest
     @Test
-    override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
-        super.visibleLayersShownMoreThanOneConsecutiveEntry()
-    }
-
-    @FlakyTest
-    @Test
-    override fun navBarLayerIsAlwaysVisible() {
-        super.navBarLayerIsAlwaysVisible()
-    }
-
-    @FlakyTest
-    @Test
     override fun navBarLayerRotatesAndScales() {
         super.navBarLayerRotatesAndScales()
     }
 
-    @FlakyTest
-    @Test
-    override fun statusBarLayerIsAlwaysVisible() {
-        super.statusBarLayerIsAlwaysVisible()
-    }
-
-    @FlakyTest
+    @FlakyTest(bugId = 192721431)
     @Test
     override fun appLayerReplacesLauncher() {
         super.appLayerReplacesLauncher()
     }
 
-    @FlakyTest
+    @FlakyTest(bugId = 192721431)
     @Test
     override fun appWindowReplacesLauncherAsTopWindow() {
         super.appWindowReplacesLauncherAsTopWindow()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index b073a7c..7833e2f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -65,34 +65,10 @@
 
     @FlakyTest
     @Test
-    override fun navBarLayerIsAlwaysVisible() {
-        super.navBarLayerIsAlwaysVisible()
-    }
-
-    @FlakyTest
-    @Test
-    override fun statusBarLayerIsAlwaysVisible() {
-        super.statusBarLayerIsAlwaysVisible()
-    }
-
-    @FlakyTest
-    @Test
     override fun navBarLayerRotatesAndScales() {
         super.navBarLayerRotatesAndScales()
     }
 
-    @FlakyTest
-    @Test
-    override fun statusBarLayerRotatesScales() {
-        super.statusBarLayerRotatesScales()
-    }
-
-    @FlakyTest
-    @Test
-    override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
-        super.visibleLayersShownMoreThanOneConsecutiveEntry()
-    }
-
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
index b304d5f..860a5ae 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
@@ -22,24 +22,24 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.appLayerReplacesLauncher
+import com.android.server.wm.flicker.LAUNCHER_COMPONENT
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.helpers.SimpleAppHelper
 import com.android.server.wm.flicker.helpers.StandardAppHelper
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.navBarWindowIsVisible
 import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.replacesLayer
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerIsVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.launcherWindowBecomesInvisible
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.SNAPSHOT_COMPONENT
 import org.junit.Test
 
 abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
@@ -71,14 +71,14 @@
 
     @Presubmit
     @Test
-    open fun navBarWindowIsAlwaysVisible() {
-        testSpec.navBarWindowIsAlwaysVisible()
+    open fun navBarWindowIsVisible() {
+        testSpec.navBarWindowIsVisible()
     }
 
     @Presubmit
     @Test
-    open fun navBarLayerIsAlwaysVisible() {
-        testSpec.navBarLayerIsAlwaysVisible(rotatesScreen = testSpec.isRotated)
+    open fun navBarLayerIsVisible() {
+        testSpec.navBarLayerIsVisible()
     }
 
     @Presubmit
@@ -89,14 +89,14 @@
 
     @Presubmit
     @Test
-    open fun statusBarWindowIsAlwaysVisible() {
-        testSpec.statusBarWindowIsAlwaysVisible()
+    open fun statusBarWindowIsVisible() {
+        testSpec.statusBarWindowIsVisible()
     }
 
     @Presubmit
     @Test
-    open fun statusBarLayerIsAlwaysVisible() {
-        testSpec.statusBarLayerIsAlwaysVisible(rotatesScreen = testSpec.isRotated)
+    open fun statusBarLayerIsVisible() {
+        testSpec.statusBarLayerIsVisible()
     }
 
     @Presubmit
@@ -124,31 +124,43 @@
     @Presubmit
     @Test
     // During testing the launcher is always in portrait mode
-    open fun noUncoveredRegions() {
-        testSpec.noUncoveredRegions(Surface.ROTATION_0, testSpec.config.endRotation)
+    open fun entireScreenCovered() {
+        testSpec.entireScreenCovered(Surface.ROTATION_0, testSpec.config.endRotation)
     }
 
     @Presubmit
     @Test
     open fun focusChanges() {
-        testSpec.focusChanges("NexusLauncherActivity", testApp.`package`)
+        testSpec.assertEventLog {
+            this.focusChanges("NexusLauncherActivity", testApp.`package`)
+        }
     }
 
     @Presubmit
     @Test
     open fun appLayerReplacesLauncher() {
-        testSpec.appLayerReplacesLauncher(testApp.`package`)
+        testSpec.replacesLayer(LAUNCHER_COMPONENT, testApp.component)
     }
 
     @Presubmit
     @Test
     open fun appWindowReplacesLauncherAsTopWindow() {
-        testSpec.appWindowReplacesLauncherAsTopWindow(testApp)
+        testSpec.assertWm {
+            this.isAppWindowOnTop(LAUNCHER_COMPONENT)
+                    .then()
+                    .isAppWindowOnTop(SNAPSHOT_COMPONENT, isOptional = true)
+                    .then()
+                    .isAppWindowOnTop(testApp.component)
+        }
     }
 
     @Presubmit
     @Test
     open fun launcherWindowBecomesInvisible() {
-        testSpec.launcherWindowBecomesInvisible()
+        testSpec.assertWm {
+            this.isAppWindowVisible(LAUNCHER_COMPONENT)
+                    .then()
+                    .isAppWindowInvisible(LAUNCHER_COMPONENT)
+        }
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index e2705c7..b509c61 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -67,15 +67,7 @@
 
     @FlakyTest
     @Test
-    override fun navBarLayerIsAlwaysVisible() {
-        super.navBarLayerIsAlwaysVisible()
-    }
-
-    @FlakyTest
-    @Test
-    override fun navBarLayerRotatesAndScales() {
-        super.navBarLayerRotatesAndScales()
-    }
+    override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 69e8a8d..73986b6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -25,7 +25,13 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerIsVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsVisible
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.ROTATION_COMPONENT
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -61,39 +67,62 @@
         super.focusDoesNotChange()
     }
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun screenshotLayerBecomesInvisible() {
         testSpec.assertLayers {
-            this.isVisible(testApp.getPackage())
+            this.isVisible(testApp.component)
                 .then()
-                .isVisible(SCREENSHOT_LAYER)
+                .isVisible(ROTATION_COMPONENT)
                 .then()
-                .isVisible(testApp.getPackage())
+                .isVisible(testApp.component)
         }
     }
 
-    @Postsubmit
-    @Test
-    override fun statusBarLayerRotatesScales() {
-        super.statusBarLayerRotatesScales()
-    }
-
     @Presubmit
     @Test
-    override fun navBarWindowIsAlwaysVisible() {
-        super.navBarWindowIsAlwaysVisible()
+    fun statusBarWindowIsVisible() {
+        testSpec.statusBarWindowIsVisible()
+    }
+
+    @Postsubmit
+    @Test
+    fun statusBarLayerIsVisible() {
+        testSpec.statusBarLayerIsVisible()
+    }
+
+    @Presubmit
+    @Test
+    fun statusBarLayerRotatesScales() {
+        testSpec.statusBarLayerRotatesScales(
+            testSpec.config.startRotation, testSpec.config.endRotation)
+    }
+
+    @Presubmit
+    @Test
+    override fun navBarWindowIsVisible() {
+        super.navBarWindowIsVisible()
+    }
+
+    @Postsubmit
+    @Test
+    override fun navBarLayerIsVisible() {
+        super.navBarLayerIsVisible()
     }
 
     @FlakyTest
     @Test
-    override fun statusBarLayerIsAlwaysVisible() {
-        super.statusBarLayerIsAlwaysVisible()
+    override fun navBarLayerRotatesAndScales() {
+        super.navBarLayerRotatesAndScales()
+    }
+
+    @Postsubmit
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+        super.visibleLayersShownMoreThanOneConsecutiveEntry()
     }
 
     companion object {
-        private const val SCREENSHOT_LAYER = "RotationLayer"
-
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): Collection<FlickerTestParameter> {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index 4b888cd..2b0b3c2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -17,25 +17,21 @@
 package com.android.server.wm.flicker.rotation
 
 import android.app.Instrumentation
+import android.content.ComponentName
 import android.platform.test.annotations.Presubmit
-import androidx.test.filters.FlakyTest
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.focusDoesNotChange
 import com.android.server.wm.flicker.helpers.StandardAppHelper
 import com.android.server.wm.flicker.helpers.WindowUtils
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerIsVisible
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.navBarWindowIsVisible
+import com.android.server.wm.flicker.entireScreenCovered
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import org.junit.Test
 
@@ -69,19 +65,19 @@
         }
     }
 
-    @FlakyTest
+    @Presubmit
     @Test
-    open fun navBarWindowIsAlwaysVisible() {
-        testSpec.navBarWindowIsAlwaysVisible()
+    open fun navBarWindowIsVisible() {
+        testSpec.navBarWindowIsVisible()
     }
 
-    @FlakyTest
+    @Presubmit
     @Test
-    open fun navBarLayerIsAlwaysVisible() {
-        testSpec.navBarLayerIsAlwaysVisible(rotatesScreen = true)
+    open fun navBarLayerIsVisible() {
+        testSpec.navBarLayerIsVisible()
     }
 
-    @FlakyTest
+    @Presubmit
     @Test
     open fun navBarLayerRotatesAndScales() {
         testSpec.navBarLayerRotatesAndScales(
@@ -90,31 +86,12 @@
 
     @Presubmit
     @Test
-    open fun statusBarWindowIsAlwaysVisible() {
-        testSpec.statusBarWindowIsAlwaysVisible()
-    }
-
-    @FlakyTest
-    @Test
-    open fun statusBarLayerIsAlwaysVisible() {
-        testSpec.statusBarLayerIsAlwaysVisible(rotatesScreen = true)
-    }
-
-    @FlakyTest
-    @Test
-    open fun statusBarLayerRotatesScales() {
-        testSpec.statusBarLayerRotatesScales(
-            testSpec.config.startRotation, testSpec.config.endRotation)
-    }
-
-    @FlakyTest
-    @Test
     open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
         testSpec.assertLayers {
             this.visibleLayersShownMoreThanOneConsecutiveEntry(
-                ignoreLayers = listOf(WindowManagerStateHelper.SPLASH_SCREEN_NAME,
-                    WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME,
-                    "SecondaryHomeHandle"
+                ignoreLayers = listOf(WindowManagerStateHelper.SPLASH_SCREEN_COMPONENT,
+                    WindowManagerStateHelper.SNAPSHOT_COMPONENT,
+                    ComponentName("", "SecondaryHomeHandle")
                 )
             )
         }
@@ -130,22 +107,24 @@
 
     @Presubmit
     @Test
-    open fun noUncoveredRegions() {
-        testSpec.noUncoveredRegions(testSpec.config.startRotation,
+    open fun entireScreenCovered() {
+        testSpec.entireScreenCovered(testSpec.config.startRotation,
             testSpec.config.endRotation, allStates = false)
     }
 
     @Presubmit
     @Test
     open fun focusDoesNotChange() {
-        testSpec.focusDoesNotChange()
+        testSpec.assertEventLog {
+            this.focusDoesNotChange()
+        }
     }
 
     @Presubmit
     @Test
     open fun appLayerRotates_StartingPos() {
         testSpec.assertLayersStart {
-            this.visibleRegion(testApp.getPackage()).coversExactly(startingPos)
+            this.visibleRegion(testApp.component).coversExactly(startingPos)
         }
     }
 
@@ -153,7 +132,7 @@
     @Test
     open fun appLayerRotates_EndingPos() {
         testSpec.assertLayersEnd {
-            this.visibleRegion(testApp.getPackage()).coversExactly(endingPos)
+            this.visibleRegion(testApp.component).coversExactly(endingPos)
         }
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index b153bec..b97b977 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -18,6 +18,7 @@
 
 import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
+import android.view.WindowManager
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -27,6 +28,7 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper
 import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -60,42 +62,91 @@
             }
         }
 
-    @FlakyTest(bugId = 140855415)
+    @Presubmit
     @Test
-    override fun statusBarWindowIsAlwaysVisible() {
-        super.statusBarWindowIsAlwaysVisible()
+    fun appWindowFullScreen() {
+        testSpec.assertWm {
+            this.invoke("isFullScreen") {
+                val appWindow = it.windowState(testApp.`package`)
+                val flags = appWindow.windowState?.attributes?.flags ?: 0
+                appWindow.verify("isFullScreen")
+                    .that(flags.and(WindowManager.LayoutParams.FLAG_FULLSCREEN))
+                    .isGreaterThan(0)
+            }
+        }
     }
 
-    @FlakyTest(bugId = 140855415)
+    @Presubmit
     @Test
-    override fun statusBarLayerIsAlwaysVisible() {
-        super.statusBarLayerIsAlwaysVisible()
+    fun appWindowSeamlessRotation() {
+        testSpec.assertWm {
+            this.invoke("isRotationSeamless") {
+                val appWindow = it.windowState(testApp.`package`)
+                val rotationAnimation = appWindow.windowState?.attributes?.rotationAnimation ?: 0
+                appWindow.verify("isRotationSeamless")
+                    .that(rotationAnimation
+                        .and(WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS))
+                    .isGreaterThan(0)
+            }
+        }
     }
 
     @Presubmit
     @Test
     fun appLayerAlwaysVisible() {
         testSpec.assertLayers {
-            isVisible(testApp.`package`)
-        }
-    }
-
-    @FlakyTest(bugId = 185400889)
-    @Test
-    fun appLayerRotates() {
-        testSpec.assertLayers {
-            this.coversExactly(startingPos, testApp.`package`)
-                .then()
-                .coversExactly(endingPos, testApp.`package`)
+            isVisible(testApp.component)
         }
     }
 
     @Postsubmit
     @Test
+    fun appLayerRotates() {
+        testSpec.assertLayers {
+            this.coversExactly(startingPos, testApp.component)
+                .then()
+                .coversExactly(endingPos, testApp.component)
+        }
+    }
+
+    @Presubmit
+    @Test
+    fun statusBarWindowIsAlwaysInvisible() {
+        testSpec.assertWm {
+            this.isAboveAppWindowInvisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+        }
+    }
+
+    @Presubmit
+    @Test
+    fun statusBarLayerIsAlwaysInvisible() {
+        testSpec.assertLayers {
+            this.isInvisible(WindowManagerStateHelper.STATUS_BAR_COMPONENT)
+        }
+    }
+
+    @Presubmit
+    @Test
     override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
         super.visibleLayersShownMoreThanOneConsecutiveEntry()
     }
 
+    @Postsubmit
+    @Test
+    override fun navBarWindowIsVisible() {
+        super.navBarWindowIsVisible()
+    }
+
+    @Postsubmit
+    @Test
+    override fun navBarLayerIsVisible() {
+        super.navBarLayerIsVisible()
+    }
+
+    @FlakyTest
+    @Test
+    override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
     companion object {
         private val testFactory = FlickerTestParameterFactory.getInstance()
 
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
index 4708cfd..c55e7c2 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
@@ -23,5 +23,6 @@
     <EditText android:id="@+id/plain_text_input"
               android:layout_height="wrap_content"
               android:layout_width="match_parent"
+	      android:imeOptions="flagNoExtractUi"
               android:inputType="text"/>
 </LinearLayout>
diff --git a/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java b/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java
index 72db55b..e9026e2 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java
@@ -87,8 +87,8 @@
                         eq(mVcnNetworkProvider),
                         argThat(
                                 score ->
-                                        score.getLegacyInt()
-                                                == Vcn.getNetworkScore().getLegacyInt()),
+                                        score.getLegacyInt() == Vcn.getNetworkScore().getLegacyInt()
+                                                && score.isTransportPrimary()),
                         any(),
                         any(),
                         cbCaptor.capture());
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index 96f6512..cbcbf05 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -27,7 +27,7 @@
 
 static ApiVersion sDevelopmentSdkLevel = 10000;
 static const auto sDevelopmentSdkCodeNames = std::unordered_set<StringPiece>({
-    "Q", "R", "S"
+    "Q", "R", "S", "Sv2"
 });
 
 static const std::vector<std::pair<uint16_t, ApiVersion>> sAttrIdMap = {