Merge "Add some more scenes, replace linear with uniform" into sc-dev
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
index 1bd90a8..cfcb4e7 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "7197701"
+    build_id: "7351002"
     target: "CtsShim"
     source_file: "aosp_arm64/CtsShimPriv.apk"
   }
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
index 544bca02..0948e47 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "7197701"
+    build_id: "7351002"
     target: "CtsShim"
     source_file: "aosp_arm64/CtsShim.apk"
   }
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
index 72386bb..db64475 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "7197701"
+    build_id: "7351002"
     target: "CtsShim"
     source_file: "aosp_x86_64/CtsShimPriv.apk"
   }
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
index 893eac2..80812df 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "7197701"
+    build_id: "7351002"
     target: "CtsShim"
     source_file: "aosp_x86_64/CtsShim.apk"
   }
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
index afd8e29..ac63653 100644
--- a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
@@ -144,7 +144,7 @@
         while (state.keepRunning()) {
             state.pauseTiming();
             // Invalidate cache.
-            resourcesManager.applyConfigurationToResourcesLocked(
+            resourcesManager.applyConfigurationToResources(
                     resourcesManager.getConfiguration(), null);
             state.resumeTiming();
 
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java
index aca0fc0..f0b04fc 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java
@@ -19,8 +19,6 @@
 import android.annotation.Nullable;
 import android.util.ArrayMap;
 
-import com.android.internal.util.Preconditions;
-
 import java.util.Map;
 import java.util.Objects;
 
@@ -117,30 +115,26 @@
     /**
      * Builder for {@link AppSearchBatchResult} objects.
      *
-     * <p>Once {@link #build} is called, the instance can no longer be used.
-     *
      * @param <KeyType> The type of the keys for which the results will be reported.
      * @param <ValueType> The type of the result objects for successful results.
      */
     public static final class Builder<KeyType, ValueType> {
-        private final Map<KeyType, ValueType> mSuccesses = new ArrayMap<>();
-        private final Map<KeyType, AppSearchResult<ValueType>> mFailures = new ArrayMap<>();
-        private final Map<KeyType, AppSearchResult<ValueType>> mAll = new ArrayMap<>();
+        private ArrayMap<KeyType, ValueType> mSuccesses = new ArrayMap<>();
+        private ArrayMap<KeyType, AppSearchResult<ValueType>> mFailures = new ArrayMap<>();
+        private ArrayMap<KeyType, AppSearchResult<ValueType>> mAll = new ArrayMap<>();
         private boolean mBuilt = false;
 
         /**
          * Associates the {@code key} with the provided successful return value.
          *
          * <p>Any previous mapping for a key, whether success or failure, is deleted.
-         *
-         * @throws IllegalStateException if the builder has already been used.
          */
         @SuppressWarnings("MissingGetterMatchingBuilder") // See getSuccesses
         @NonNull
         public Builder<KeyType, ValueType> setSuccess(
                 @NonNull KeyType key, @Nullable ValueType result) {
-            Preconditions.checkState(!mBuilt, "Builder has already been used");
             Objects.requireNonNull(key);
+            resetIfBuilt();
             return setResult(key, AppSearchResult.newSuccessfulResult(result));
         }
 
@@ -148,8 +142,6 @@
          * Associates the {@code key} with the provided failure code and error message.
          *
          * <p>Any previous mapping for a key, whether success or failure, is deleted.
-         *
-         * @throws IllegalStateException if the builder has already been used.
          */
         @SuppressWarnings("MissingGetterMatchingBuilder") // See getFailures
         @NonNull
@@ -157,8 +149,8 @@
                 @NonNull KeyType key,
                 @AppSearchResult.ResultCode int resultCode,
                 @Nullable String errorMessage) {
-            Preconditions.checkState(!mBuilt, "Builder has already been used");
             Objects.requireNonNull(key);
+            resetIfBuilt();
             return setResult(key, AppSearchResult.newFailedResult(resultCode, errorMessage));
         }
 
@@ -166,16 +158,14 @@
          * Associates the {@code key} with the provided {@code result}.
          *
          * <p>Any previous mapping for a key, whether success or failure, is deleted.
-         *
-         * @throws IllegalStateException if the builder has already been used.
          */
         @SuppressWarnings("MissingGetterMatchingBuilder") // See getAll
         @NonNull
         public Builder<KeyType, ValueType> setResult(
                 @NonNull KeyType key, @NonNull AppSearchResult<ValueType> result) {
-            Preconditions.checkState(!mBuilt, "Builder has already been used");
             Objects.requireNonNull(key);
             Objects.requireNonNull(result);
+            resetIfBuilt();
             if (result.isSuccess()) {
                 mSuccesses.put(key, result.getResultValue());
                 mFailures.remove(key);
@@ -189,14 +179,21 @@
 
         /**
          * Builds an {@link AppSearchBatchResult} object from the contents of this {@link Builder}.
-         *
-         * @throws IllegalStateException if the builder has already been used.
          */
         @NonNull
         public AppSearchBatchResult<KeyType, ValueType> build() {
-            Preconditions.checkState(!mBuilt, "Builder has already been used");
             mBuilt = true;
-            return new AppSearchBatchResult<>(mSuccesses, mFailures, mAll);
+            return new AppSearchBatchResult<>(
+                    new ArrayMap<>(mSuccesses), new ArrayMap<>(mFailures), new ArrayMap<>(mAll));
+        }
+
+        private void resetIfBuilt() {
+            if (mBuilt) {
+                mSuccesses = new ArrayMap<>(mSuccesses);
+                mFailures = new ArrayMap<>(mFailures);
+                mAll = new ArrayMap<>(mAll);
+                mBuilt = false;
+            }
         }
     }
 }
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
index 736deab..cc48ccb 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
@@ -892,129 +892,121 @@
     @Override
     @NonNull
     public String toString() {
-        return formatGenericDocumentString(this, /*indentLevel=*/ 0);
+        StringBuilder stringBuilder = new StringBuilder();
+        appendGenericDocumentString(this, /*indentLevel=*/ 0, stringBuilder);
+        return stringBuilder.toString();
     }
 
-    @NonNull
-    private static String formatGenericDocumentString(
-            @NonNull GenericDocument document, int indentLevel) {
-        StringBuilder stringBuilder = new StringBuilder();
-        stringBuilder.append(getIndent(indentLevel)).append("{\n");
+    private static void appendGenericDocumentString(
+            @NonNull GenericDocument document, int indentLevel, @NonNull StringBuilder builder) {
+        Objects.requireNonNull(document);
+        Objects.requireNonNull(builder);
 
-        String indentLevelOneString = getIndent(indentLevel + 1);
+        builder.append(getIndent(indentLevel)).append("{\n");
 
-        stringBuilder
-                .append(indentLevelOneString)
+        String indent1 = getIndent(indentLevel + 1);
+
+        builder.append(indent1)
                 .append("namespace: \"")
                 .append(document.getNamespace())
                 .append("\",\n");
 
-        stringBuilder
-                .append(indentLevelOneString)
-                .append("id: \"")
-                .append(document.getId())
-                .append("\",\n");
+        builder.append(indent1).append("id: \"").append(document.getId()).append("\",\n");
 
-        stringBuilder
-                .append(indentLevelOneString)
-                .append("score: " + document.getScore())
-                .append(",\n");
+        builder.append(indent1).append("score: ").append(document.getScore()).append(",\n");
 
-        stringBuilder
-                .append(indentLevelOneString)
+        builder.append(indent1)
                 .append("schemaType: \"")
                 .append(document.getSchemaType())
                 .append("\",\n");
 
-        stringBuilder
-                .append(indentLevelOneString)
-                .append("creationTimestampMillis: " + document.getCreationTimestampMillis())
+        builder.append(indent1)
+                .append("creationTimestampMillis: ")
+                .append(document.getCreationTimestampMillis())
                 .append(",\n");
 
-        stringBuilder
-                .append(indentLevelOneString)
-                .append("timeToLiveMillis: " + document.getTtlMillis())
+        builder.append(indent1)
+                .append("timeToLiveMillis: ")
+                .append(document.getTtlMillis())
                 .append(",\n");
 
-        stringBuilder.append(indentLevelOneString).append("properties: {\n");
+        builder.append(indent1).append("properties: {\n");
 
-        int idx = 0;
-        for (String propertyName : document.getPropertyNames()) {
-            Object property = document.getProperty(propertyName);
-            stringBuilder
-                    .append(getIndent(indentLevel + 2))
+        String[] sortedProperties = document.getPropertyNames().toArray(new String[0]);
+        Arrays.sort(sortedProperties);
+
+        for (int i = 0; i < sortedProperties.length; i++) {
+            Object property = document.getProperty(sortedProperties[i]);
+            builder.append(getIndent(indentLevel + 2))
                     .append("\"")
-                    .append(propertyName)
+                    .append(sortedProperties[i])
                     .append("\"")
                     .append(": ");
-            stringBuilder.append(getPropertyString(property, indentLevel + 2));
-            if (idx != document.getPropertyNames().size() - 1) {
-                stringBuilder.append(",\n");
+            appendPropertyString(property, indentLevel + 2, builder);
+            if (i != sortedProperties.length - 1) {
+                builder.append(",\n");
             }
-            ++idx;
         }
 
-        stringBuilder.append("\n");
-        stringBuilder.append(indentLevelOneString).append("}");
+        builder.append("\n");
+        builder.append(indent1).append("}");
 
-        stringBuilder.append("\n");
-        stringBuilder.append(getIndent(indentLevel)).append("}");
-
-        return stringBuilder.toString();
+        builder.append("\n");
+        builder.append(getIndent(indentLevel)).append("}");
     }
 
     /**
-     * Creates string for property.
+     * Appends a string for the given property to the given builder.
      *
      * @param property property object to create string for.
      * @param indentLevel base indent level for property.
+     * @param builder the builder to append to.
      */
-    @NonNull
-    private static String getPropertyString(@NonNull Object property, int indentLevel) {
+    private static void appendPropertyString(
+            @NonNull Object property, int indentLevel, @NonNull StringBuilder builder) {
         Objects.requireNonNull(property);
+        Objects.requireNonNull(builder);
 
-        StringBuilder str = new StringBuilder("[");
-
+        builder.append("[");
         if (property instanceof GenericDocument[]) {
             GenericDocument[] documentValues = (GenericDocument[]) property;
             for (int i = 0; i < documentValues.length; ++i) {
-                str.append("\n");
-                str.append(formatGenericDocumentString(documentValues[i], indentLevel + 1));
+                builder.append("\n");
+                appendGenericDocumentString(documentValues[i], indentLevel + 1, builder);
                 if (i != documentValues.length - 1) {
-                    str.append(", ");
+                    builder.append(", ");
                 }
-                str.append("\n");
+                builder.append("\n");
             }
-            str.append(getIndent(indentLevel));
+            builder.append(getIndent(indentLevel));
         } else {
             int propertyArrLength = Array.getLength(property);
             for (int i = 0; i < propertyArrLength; i++) {
                 Object propertyElement = Array.get(property, i);
                 if (propertyElement instanceof String) {
-                    str.append("\"").append(propertyElement).append("\"");
+                    builder.append("\"").append(propertyElement).append("\"");
                 } else if (propertyElement instanceof byte[]) {
-                    str.append(Arrays.toString((byte[]) propertyElement));
+                    builder.append(Arrays.toString((byte[]) propertyElement));
                 } else {
-                    str.append(propertyElement);
+                    builder.append(propertyElement);
                 }
                 if (i != propertyArrLength - 1) {
-                    str.append(", ");
+                    builder.append(", ");
                 }
             }
         }
 
-        str.append("]");
-        return str.toString();
+        builder.append("]");
     }
 
-    /** Creates string for given indent level. */
+    /** Appends a string for given indent level to the given builder. */
     @NonNull
     private static String getIndent(int indentLevel) {
-        StringBuilder indentedString = new StringBuilder();
+        StringBuilder builder = new StringBuilder();
         for (int i = 0; i < indentLevel; ++i) {
-            indentedString.append("  ");
+            builder.append("  ");
         }
-        return indentedString.toString();
+        return builder.toString();
     }
 
     /**
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 b6b7fde..777f9fe 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -234,6 +234,18 @@
         }
     }
 
+    @Override
+    public void onUserStopping(@NonNull TargetUser user) {
+        synchronized (mUnlockedUserIdsLocked) {
+            mUnlockedUserIdsLocked.remove(user.getUserIdentifier());
+            try {
+                mImplInstanceManager.closeAndRemoveAppSearchImplForUser(user.getUserIdentifier());
+            } catch (Throwable t) {
+                Log.e(TAG, "Error handling user stopping.", t);
+            }
+        }
+    }
+
     private void verifyUserUnlocked(int callingUserId) {
         if (isUserLocked(callingUserId)) {
             throw new IllegalStateException("User " + callingUserId + " is locked or not running.");
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
index 94ee830..b815de4 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
@@ -117,11 +117,29 @@
      */
     public void removeAppSearchImplForUser(@UserIdInt int userId) {
         synchronized (mInstancesLocked) {
+            // no need to close and persist data to disk since we are removing them now.
             mInstancesLocked.remove(userId);
         }
     }
 
     /**
+     * Close and remove an instance of {@link AppSearchImpl} for the given user.
+     *
+     * <p>All mutation apply to this {@link AppSearchImpl} will be persisted to disk.
+     *
+     * @param userId The multi-user userId of the user that need to be removed.
+     */
+    public void closeAndRemoveAppSearchImplForUser(@UserIdInt int userId) {
+        synchronized (mInstancesLocked) {
+            AppSearchImpl appSearchImpl = mInstancesLocked.get(userId);
+            if (appSearchImpl != null) {
+                appSearchImpl.close();
+                mInstancesLocked.remove(userId);
+            }
+        }
+    }
+
+    /**
      * Gets an instance of AppSearchImpl for the given user.
      *
      * <p>This method should only be called by an initialized SearchSession, which has been already
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java
index ecc774c..6b443b3 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java
@@ -128,14 +128,14 @@
         return new SearchResult.MatchInfo.Builder(propertyPath)
                 .setExactMatchRange(
                         new SearchResult.MatchRange(
-                                snippetMatchProto.getExactMatchPosition(),
-                                snippetMatchProto.getExactMatchPosition()
-                                        + snippetMatchProto.getExactMatchBytes()))
+                                snippetMatchProto.getExactMatchUtf16Position(),
+                                snippetMatchProto.getExactMatchUtf16Position()
+                                        + snippetMatchProto.getExactMatchUtf16Length()))
                 .setSnippetRange(
                         new SearchResult.MatchRange(
-                                snippetMatchProto.getWindowPosition(),
-                                snippetMatchProto.getWindowPosition()
-                                        + snippetMatchProto.getWindowBytes()))
+                                snippetMatchProto.getWindowUtf16Position(),
+                                snippetMatchProto.getWindowUtf16Position()
+                                        + snippetMatchProto.getWindowUtf16Length()))
                 .build();
     }
 }
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java
index cf640c1..ea5263a 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java
@@ -43,13 +43,16 @@
                 CALL_TYPE_SET_SCHEMA,
                 CALL_TYPE_PUT_DOCUMENTS,
                 CALL_TYPE_GET_DOCUMENTS,
-                CALL_TYPE_REMOVE_DOCUMENTS,
+                CALL_TYPE_REMOVE_DOCUMENTS_BY_ID,
                 CALL_TYPE_PUT_DOCUMENT,
                 CALL_TYPE_GET_DOCUMENT,
-                CALL_TYPE_REMOVE_DOCUMENT,
-                CALL_TYPE_QUERY,
+                CALL_TYPE_REMOVE_DOCUMENT_BY_ID,
+                CALL_TYPE_SEARCH,
                 CALL_TYPE_OPTIMIZE,
                 CALL_TYPE_FLUSH,
+                CALL_TYPE_GLOBAL_SEARCH,
+                CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH,
+                CALL_TYPE_REMOVE_DOCUMENT_BY_SEARCH,
             })
     @Retention(RetentionPolicy.SOURCE)
     public @interface CallType {}
@@ -59,13 +62,16 @@
     public static final int CALL_TYPE_SET_SCHEMA = 2;
     public static final int CALL_TYPE_PUT_DOCUMENTS = 3;
     public static final int CALL_TYPE_GET_DOCUMENTS = 4;
-    public static final int CALL_TYPE_REMOVE_DOCUMENTS = 5;
+    public static final int CALL_TYPE_REMOVE_DOCUMENTS_BY_ID = 5;
     public static final int CALL_TYPE_PUT_DOCUMENT = 6;
     public static final int CALL_TYPE_GET_DOCUMENT = 7;
-    public static final int CALL_TYPE_REMOVE_DOCUMENT = 8;
-    public static final int CALL_TYPE_QUERY = 9;
+    public static final int CALL_TYPE_REMOVE_DOCUMENT_BY_ID = 8;
+    public static final int CALL_TYPE_SEARCH = 9;
     public static final int CALL_TYPE_OPTIMIZE = 10;
     public static final int CALL_TYPE_FLUSH = 11;
+    public static final int CALL_TYPE_GLOBAL_SEARCH = 12;
+    public static final int CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH = 13;
+    public static final int CALL_TYPE_REMOVE_DOCUMENT_BY_SEARCH = 14;
 
     @NonNull private final GeneralStats mGeneralStats;
     @CallType private final int mCallType;
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index d07a3b9..85d85aa 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-Ibbf4260deb720ce724be81ee4394ea96181ee0f7
+I0216abecc41d020f16ed8947a9f37b710afd331e
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
index 941cea9..71b4f36 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
@@ -17,6 +17,7 @@
 package com.android.server.appsearch.testing;
 
 import android.annotation.NonNull;
+import android.annotation.UserIdInt;
 import android.app.appsearch.AppSearchBatchResult;
 import android.app.appsearch.AppSearchManager;
 import android.app.appsearch.AppSearchResult;
@@ -37,6 +38,7 @@
 import android.app.appsearch.StorageInfo;
 import android.app.appsearch.exceptions.AppSearchException;
 import android.content.Context;
+import android.os.UserHandle;
 
 import androidx.test.core.app.ApplicationProvider;
 
@@ -58,18 +60,29 @@
     private final AppSearchSession mAppSearchSession;
     private final ExecutorService mExecutor;
 
+    /** Creates the SearchSessionShim with given SearchContext. */
     @NonNull
     public static ListenableFuture<AppSearchSessionShim> createSearchSession(
             @NonNull AppSearchManager.SearchContext searchContext) {
-        return createSearchSession(searchContext, Executors.newCachedThreadPool());
+        Context context = ApplicationProvider.getApplicationContext();
+        return createSearchSession(context, searchContext, Executors.newCachedThreadPool());
     }
 
-    /**  Creates the SearchSession with given ExecutorService. */
+    /** Creates the SearchSessionShim with given SearchContext for the given user. */
     @NonNull
     public static ListenableFuture<AppSearchSessionShim> createSearchSession(
+            @NonNull AppSearchManager.SearchContext searchContext, @UserIdInt int userId) {
+        Context context = ApplicationProvider.getApplicationContext()
+                .createContextAsUser(new UserHandle(userId), /*flags=*/ 0);
+        return createSearchSession(context, searchContext, Executors.newCachedThreadPool());
+    }
+
+    /**  Creates the SearchSession with given Context and ExecutorService. */
+    @NonNull
+    public static ListenableFuture<AppSearchSessionShim> createSearchSession(
+            @NonNull Context context,
             @NonNull AppSearchManager.SearchContext searchContext,
             @NonNull ExecutorService executor) {
-        Context context = ApplicationProvider.getApplicationContext();
         AppSearchManager appSearchManager = context.getSystemService(AppSearchManager.class);
         SettableFuture<AppSearchResult<AppSearchSession>> future = SettableFuture.create();
         appSearchManager.createSearchSession(searchContext, executor, future::set);
diff --git a/core/api/current.txt b/core/api/current.txt
index 324ad33..98c2d40 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -11154,6 +11154,7 @@
     field public static final String ACTION_VIEW = "android.intent.action.VIEW";
     field public static final String ACTION_VIEW_LOCUS = "android.intent.action.VIEW_LOCUS";
     field @RequiresPermission(android.Manifest.permission.START_VIEW_PERMISSION_USAGE) public static final String ACTION_VIEW_PERMISSION_USAGE = "android.intent.action.VIEW_PERMISSION_USAGE";
+    field @RequiresPermission(android.Manifest.permission.START_VIEW_PERMISSION_USAGE) public static final String ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD = "android.intent.action.VIEW_PERMISSION_USAGE_FOR_PERIOD";
     field public static final String ACTION_VOICE_COMMAND = "android.intent.action.VOICE_COMMAND";
     field @Deprecated public static final String ACTION_WALLPAPER_CHANGED = "android.intent.action.WALLPAPER_CHANGED";
     field public static final String ACTION_WEB_SEARCH = "android.intent.action.WEB_SEARCH";
@@ -11206,6 +11207,7 @@
     field public static final String EXTRA_ASSIST_INPUT_HINT_KEYBOARD = "android.intent.extra.ASSIST_INPUT_HINT_KEYBOARD";
     field public static final String EXTRA_ASSIST_PACKAGE = "android.intent.extra.ASSIST_PACKAGE";
     field public static final String EXTRA_ASSIST_UID = "android.intent.extra.ASSIST_UID";
+    field public static final String EXTRA_ATTRIBUTION_TAGS = "android.intent.extra.ATTRIBUTION_TAGS";
     field public static final String EXTRA_AUTO_LAUNCH_SINGLE_CHOICE = "android.intent.extra.AUTO_LAUNCH_SINGLE_CHOICE";
     field public static final String EXTRA_BCC = "android.intent.extra.BCC";
     field public static final String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT";
@@ -11231,6 +11233,7 @@
     field public static final String EXTRA_DONT_KILL_APP = "android.intent.extra.DONT_KILL_APP";
     field public static final String EXTRA_DURATION_MILLIS = "android.intent.extra.DURATION_MILLIS";
     field public static final String EXTRA_EMAIL = "android.intent.extra.EMAIL";
+    field public static final String EXTRA_END_TIME = "android.intent.extra.END_TIME";
     field public static final String EXTRA_EXCLUDE_COMPONENTS = "android.intent.extra.EXCLUDE_COMPONENTS";
     field public static final String EXTRA_FROM_STORAGE = "android.intent.extra.FROM_STORAGE";
     field public static final String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
@@ -11245,6 +11248,7 @@
     field public static final String EXTRA_NOT_UNKNOWN_SOURCE = "android.intent.extra.NOT_UNKNOWN_SOURCE";
     field public static final String EXTRA_ORIGINATING_URI = "android.intent.extra.ORIGINATING_URI";
     field public static final String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
+    field public static final String EXTRA_PERMISSION_GROUP_NAME = "android.intent.extra.PERMISSION_GROUP_NAME";
     field public static final String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER";
     field public static final String EXTRA_PROCESS_TEXT = "android.intent.extra.PROCESS_TEXT";
     field public static final String EXTRA_PROCESS_TEXT_READONLY = "android.intent.extra.PROCESS_TEXT_READONLY";
@@ -11267,6 +11271,7 @@
     field @Deprecated public static final String EXTRA_SHORTCUT_NAME = "android.intent.extra.shortcut.NAME";
     field public static final String EXTRA_SHUTDOWN_USERSPACE_ONLY = "android.intent.extra.SHUTDOWN_USERSPACE_ONLY";
     field public static final String EXTRA_SPLIT_NAME = "android.intent.extra.SPLIT_NAME";
+    field public static final String EXTRA_START_TIME = "android.intent.extra.START_TIME";
     field public static final String EXTRA_STREAM = "android.intent.extra.STREAM";
     field public static final String EXTRA_SUBJECT = "android.intent.extra.SUBJECT";
     field public static final String EXTRA_SUSPENDED_PACKAGE_EXTRAS = "android.intent.extra.SUSPENDED_PACKAGE_EXTRAS";
@@ -13079,7 +13084,6 @@
     method public void reportShortcutUsed(String);
     method @WorkerThread public boolean requestPinShortcut(@NonNull android.content.pm.ShortcutInfo, @Nullable android.content.IntentSender);
     method @WorkerThread public boolean setDynamicShortcuts(@NonNull java.util.List<android.content.pm.ShortcutInfo>);
-    method public void updateShortcutVisibility(@NonNull String, @Nullable byte[], boolean);
     method @WorkerThread public boolean updateShortcuts(@NonNull java.util.List<android.content.pm.ShortcutInfo>);
     field public static final int FLAG_MATCH_CACHED = 8; // 0x8
     field public static final int FLAG_MATCH_DYNAMIC = 2; // 0x2
@@ -46814,6 +46818,11 @@
     method public void onActionProviderVisibilityChanged(boolean);
   }
 
+  @UiThread public interface AttachedSurfaceControl {
+    method public boolean applyTransactionOnDraw(@NonNull android.view.SurfaceControl.Transaction);
+    method @Nullable public android.view.SurfaceControl.Transaction buildReparentTransaction(@NonNull android.view.SurfaceControl);
+  }
+
   public final class Choreographer {
     method public static android.view.Choreographer getInstance();
     method public void postFrameCallback(android.view.Choreographer.FrameCallback);
@@ -48540,6 +48549,7 @@
     method public android.view.WindowInsets dispatchApplyWindowInsets(android.view.WindowInsets);
     method public boolean dispatchCapturedPointerEvent(android.view.MotionEvent);
     method public void dispatchConfigurationChanged(android.content.res.Configuration);
+    method public void dispatchCreateViewTranslationRequest(@NonNull java.util.Map<android.view.autofill.AutofillId,long[]>, @NonNull int[], @NonNull android.view.translation.TranslationCapability, @NonNull java.util.List<android.view.translation.ViewTranslationRequest>);
     method public void dispatchDisplayHint(int);
     method public boolean dispatchDragEvent(android.view.DragEvent);
     method protected void dispatchDraw(android.graphics.Canvas);
@@ -48561,7 +48571,6 @@
     method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
     method public void dispatchProvideAutofillStructure(@NonNull android.view.ViewStructure, int);
     method public void dispatchProvideStructure(android.view.ViewStructure);
-    method public void dispatchRequestTranslation(@NonNull java.util.Map<android.view.autofill.AutofillId,long[]>, @NonNull int[], @NonNull android.view.translation.TranslationCapability, @NonNull java.util.List<android.view.translation.ViewTranslationRequest>);
     method protected void dispatchRestoreInstanceState(android.util.SparseArray<android.os.Parcelable>);
     method protected void dispatchSaveInstanceState(android.util.SparseArray<android.os.Parcelable>);
     method public void dispatchScrollCaptureSearch(@NonNull android.graphics.Rect, @NonNull android.graphics.Point, @NonNull java.util.function.Consumer<android.view.ScrollCaptureTarget>);
@@ -48714,6 +48723,7 @@
     method public final int getRight();
     method protected float getRightFadingEdgeStrength();
     method protected int getRightPaddingOffset();
+    method @Nullable public android.view.AttachedSurfaceControl getRootSurfaceControl();
     method public android.view.View getRootView();
     method public android.view.WindowInsets getRootWindowInsets();
     method public float getRotation();
@@ -48758,7 +48768,6 @@
     method @Nullable public android.graphics.drawable.Drawable getVerticalScrollbarThumbDrawable();
     method @Nullable public android.graphics.drawable.Drawable getVerticalScrollbarTrackDrawable();
     method public int getVerticalScrollbarWidth();
-    method @Nullable public android.view.ViewRoot getViewRoot();
     method @Nullable public android.view.translation.ViewTranslationResponse getViewTranslationResponse();
     method public android.view.ViewTreeObserver getViewTreeObserver();
     method public int getVisibility();
@@ -49834,11 +49843,6 @@
     method public android.view.ViewPropertyAnimator zBy(float);
   }
 
-  @UiThread public interface ViewRoot {
-    method public boolean applyTransactionOnDraw(@NonNull android.view.SurfaceControl.Transaction);
-    method @Nullable public android.view.SurfaceControl.Transaction buildReparentTransaction(@NonNull android.view.SurfaceControl);
-  }
-
   public abstract class ViewStructure {
     ctor public ViewStructure();
     method public abstract int addChildCount(int);
@@ -50025,6 +50029,7 @@
     method @ColorInt public int getNavigationBarDividerColor();
     method public android.transition.Transition getReenterTransition();
     method public android.transition.Transition getReturnTransition();
+    method @Nullable public android.view.AttachedSurfaceControl getRootSurfaceControl();
     method public android.transition.Transition getSharedElementEnterTransition();
     method public android.transition.Transition getSharedElementExitTransition();
     method public android.transition.Transition getSharedElementReenterTransition();
@@ -50034,7 +50039,6 @@
     method @NonNull public java.util.List<android.graphics.Rect> getSystemGestureExclusionRects();
     method public long getTransitionBackgroundFadeDuration();
     method public android.transition.TransitionManager getTransitionManager();
-    method @Nullable public android.view.ViewRoot getViewRoot();
     method public abstract int getVolumeControlStream();
     method public android.view.WindowManager getWindowManager();
     method public final android.content.res.TypedArray getWindowStyle();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 96a23b2..62ffac7 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2399,12 +2399,9 @@
     field public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
     field public static final String ACTION_USER_ADDED = "android.intent.action.USER_ADDED";
     field public static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
-    field @RequiresPermission(android.Manifest.permission.START_VIEW_PERMISSION_USAGE) public static final String ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD = "android.intent.action.VIEW_PERMISSION_USAGE_FOR_PERIOD";
     field public static final String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST";
     field public static final String CATEGORY_LEANBACK_SETTINGS = "android.intent.category.LEANBACK_SETTINGS";
-    field public static final String EXTRA_ATTRIBUTION_TAGS = "android.intent.extra.ATTRIBUTION_TAGS";
     field public static final String EXTRA_CALLING_PACKAGE = "android.intent.extra.CALLING_PACKAGE";
-    field public static final String EXTRA_END_TIME = "android.intent.extra.END_TIME";
     field public static final String EXTRA_FORCE_FACTORY_RESET = "android.intent.extra.FORCE_FACTORY_RESET";
     field public static final String EXTRA_INSTANT_APP_ACTION = "android.intent.extra.INSTANT_APP_ACTION";
     field public static final String EXTRA_INSTANT_APP_BUNDLES = "android.intent.extra.INSTANT_APP_BUNDLES";
@@ -2416,13 +2413,11 @@
     field public static final String EXTRA_LONG_VERSION_CODE = "android.intent.extra.LONG_VERSION_CODE";
     field public static final String EXTRA_ORIGINATING_UID = "android.intent.extra.ORIGINATING_UID";
     field public static final String EXTRA_PACKAGES = "android.intent.extra.PACKAGES";
-    field public static final String EXTRA_PERMISSION_GROUP_NAME = "android.intent.extra.PERMISSION_GROUP_NAME";
     field public static final String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME";
     field public static final String EXTRA_REASON = "android.intent.extra.REASON";
     field public static final String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK";
     field public static final String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED";
     field public static final String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME";
-    field public static final String EXTRA_START_TIME = "android.intent.extra.START_TIME";
     field public static final String EXTRA_UNKNOWN_INSTANT_APP = "android.intent.extra.UNKNOWN_INSTANT_APP";
     field public static final String EXTRA_VERIFICATION_BUNDLE = "android.intent.extra.VERIFICATION_BUNDLE";
     field public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 67108864; // 0x4000000
@@ -14919,6 +14914,7 @@
 
   public static interface WebViewProvider.ViewDelegate {
     method public default void autofill(android.util.SparseArray<android.view.autofill.AutofillValue>);
+    method public default void dispatchCreateViewTranslationRequest(@NonNull java.util.Map<android.view.autofill.AutofillId,long[]>, @NonNull int[], @Nullable android.view.translation.TranslationCapability, @NonNull java.util.List<android.view.translation.ViewTranslationRequest>);
     method public boolean dispatchKeyEvent(android.view.KeyEvent);
     method public android.view.View findFocus(android.view.View);
     method public android.view.accessibility.AccessibilityNodeProvider getAccessibilityNodeProvider();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index f458107..c8e365e 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -865,6 +865,10 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.ProviderInfoList> CREATOR;
   }
 
+  public final class SharedLibraryInfo implements android.os.Parcelable {
+    method @NonNull public java.util.List<java.lang.String> getAllCodePaths();
+  }
+
   public final class ShortcutInfo implements android.os.Parcelable {
     method public boolean isVisibleToPublisher();
   }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ff210e1..a667767 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5887,7 +5887,7 @@
 
     public final void applyConfigurationToResources(Configuration config) {
         synchronized (mResourcesManager) {
-            mResourcesManager.applyConfigurationToResourcesLocked(config, null);
+            mResourcesManager.applyConfigurationToResources(config, null);
         }
     }
 
@@ -5975,7 +5975,7 @@
 
         synchronized (mResourcesManager) {
             // Update all affected Resources objects to use new ResourcesImpl
-            mResourcesManager.applyNewResourceDirsLocked(ai, oldResDirs);
+            mResourcesManager.applyNewResourceDirs(ai, oldResDirs);
         }
     }
 
@@ -6231,7 +6231,7 @@
 
                                 synchronized (mResourcesManager) {
                                     // Update affected Resources objects to use new ResourcesImpl
-                                    mResourcesManager.applyNewResourceDirsLocked(aInfo, oldResDirs);
+                                    mResourcesManager.applyNewResourceDirs(aInfo, oldResDirs);
                                 }
                             } catch (RemoteException e) {
                             }
@@ -6474,7 +6474,7 @@
              * reflect configuration changes. The configuration object passed
              * in AppBindData can be safely assumed to be up to date
              */
-            mResourcesManager.applyConfigurationToResourcesLocked(data.config, data.compatInfo);
+            mResourcesManager.applyConfigurationToResources(data.config, data.compatInfo);
             mCurDefaultDisplayDpi = data.config.densityDpi;
 
             // This calls mResourcesManager so keep it within the synchronized block.
@@ -7509,7 +7509,7 @@
 
                 // We need to apply this change to the resources immediately, because upon returning
                 // the view hierarchy will be informed about it.
-                if (mResourcesManager.applyConfigurationToResourcesLocked(globalConfig,
+                if (mResourcesManager.applyConfigurationToResources(globalConfig,
                         null /* compat */,
                         mInitialApplication.getResources().getDisplayAdjustments())) {
                     mConfigurationController.updateLocaleListFromAppContext(
diff --git a/core/java/android/app/ConfigurationController.java b/core/java/android/app/ConfigurationController.java
index 0dbe3ba..6d92201 100644
--- a/core/java/android/app/ConfigurationController.java
+++ b/core/java/android/app/ConfigurationController.java
@@ -107,8 +107,7 @@
             mCompatConfiguration = new Configuration();
         }
         mCompatConfiguration.setTo(mConfiguration);
-        if (mResourcesManager.applyCompatConfigurationLocked(displayDensity,
-                mCompatConfiguration)) {
+        if (mResourcesManager.applyCompatConfiguration(displayDensity, mCompatConfiguration)) {
             config = mCompatConfiguration;
         }
         return config;
@@ -199,7 +198,7 @@
                 // configuration also needs to set to the adjustments for consistency.
                 appResources.getDisplayAdjustments().getConfiguration().updateFrom(config);
             }
-            mResourcesManager.applyConfigurationToResourcesLocked(config, compat,
+            mResourcesManager.applyConfigurationToResources(config, compat,
                     appResources.getDisplayAdjustments());
             updateLocaleListFromAppContext(app.getApplicationContext());
 
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 2dcdd07..c6847aa 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5662,11 +5662,6 @@
                         R.dimen.call_notification_collapsible_indent);
             }
             big.setBoolean(R.id.actions, "setEmphasizedMode", emphazisedMode);
-            if (p.mCallStyleActions) {
-                // Use "wrap_content" (unlike normal emphasized mode) and allow prioritizing the
-                // required actions (Answer, Decline, and Hang Up).
-                big.setBoolean(R.id.actions, "setPrioritizedWrapMode", true);
-            }
             if (numActions > 0 && !p.mHideActions) {
                 big.setViewVisibility(R.id.actions_container, View.VISIBLE);
                 big.setViewVisibility(R.id.actions, View.VISIBLE);
@@ -5683,7 +5678,7 @@
                         // Clear the drawable
                         button.setInt(R.id.action0, "setBackgroundResource", 0);
                     }
-                    if (p.mCallStyleActions && i > 0) {
+                    if (emphazisedMode && i > 0) {
                         // Clear start margin from non-first buttons to reduce the gap between them.
                         //  (8dp remaining gap is from all buttons' standard 4dp inset).
                         button.setViewLayoutMarginDimen(R.id.action0, RemoteViews.MARGIN_START, 0);
@@ -6103,26 +6098,21 @@
                 // change the background bgColor
                 CharSequence title = action.title;
                 ColorStateList[] outResultColor = new ColorStateList[1];
-                int background = getBackgroundColor(p);
+                int background = getSecondaryAccentColor(p);
                 if (isLegacy()) {
                     title = ContrastColorUtil.clearColorSpans(title);
                 } else {
                     title = ensureColorSpanContrast(title, background, outResultColor);
                 }
                 button.setTextViewText(R.id.action0, processTextSpans(title));
-                final int textColor;
                 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();
-                    textColor = ContrastColorUtil.resolvePrimaryColor(mContext,
-                            background, mInNightMode);
-                } else if (mTintActionButtons && !mInNightMode && !isBackgroundColorized(p)) {
-                    textColor = getAccentColor(p);
-                } else {
-                    textColor = getPrimaryTextColor(p);
                 }
+                final int textColor = ContrastColorUtil.resolvePrimaryColor(mContext,
+                        background, mInNightMode);
                 button.setTextColor(R.id.action0, textColor);
                 // We only want about 20% alpha for the ripple
                 final int rippleColor = (textColor & 0x00ffffff) | 0x33000000;
@@ -6130,11 +6120,10 @@
                         ColorStateList.valueOf(rippleColor));
                 button.setColorStateList(R.id.action0, "setButtonBackground",
                         ColorStateList.valueOf(background));
-                button.setBoolean(R.id.action0, "setHasStroke", !hasColorOverride);
                 if (p.mCallStyleActions) {
                     button.setImageViewIcon(R.id.action0, action.getIcon());
                     boolean priority = action.getExtras().getBoolean(CallStyle.KEY_ACTION_PRIORITY);
-                    button.setBoolean(R.id.action0, "setWrapModePriority", priority);
+                    button.setBoolean(R.id.action0, "setIsPriority", priority);
                     int minWidthDimen =
                             priority ? R.dimen.call_notification_system_action_min_width : 0;
                     button.setIntDimen(R.id.action0, "setMinimumWidth", minWidthDimen);
@@ -6320,6 +6309,22 @@
         }
 
         /**
+         * Gets the secondary accent color for colored UI elements.  If we're tinting with the theme
+         * accent, this is the theme accent color, otherwise this would be identical to
+         * {@link #getSmallIconColor(StandardTemplateParams)}.
+         */
+        private @ColorInt int getSecondaryAccentColor(StandardTemplateParams p) {
+            if (isBackgroundColorized(p)) {
+                return getSecondaryTextColor(p);
+            }
+            int color = obtainThemeColor(R.attr.colorAccentSecondary, COLOR_INVALID);
+            if (color != COLOR_INVALID) {
+                return color;
+            }
+            return getContrastColor(p);
+        }
+
+        /**
          * Gets the "surface protection" color from the theme, or a variant of the normal background
          * color when colorized, or when not using theme color tints.
          */
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 74134e1..792336d 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -75,6 +75,11 @@
     private static ResourcesManager sResourcesManager;
 
     /**
+     * Internal lock object
+     */
+    private final Object mLock = new Object();
+
+    /**
      * The global compatibility settings.
      */
     private CompatibilityInfo mResCompatibilityInfo;
@@ -275,7 +280,7 @@
      * try as hard as possible to release any open FDs.
      */
     public void invalidatePath(String path) {
-        synchronized (this) {
+        synchronized (mLock) {
             int count = 0;
 
             for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
@@ -304,7 +309,7 @@
     }
 
     public Configuration getConfiguration() {
-        synchronized (this) {
+        synchronized (mLock) {
             return mResConfiguration;
         }
     }
@@ -351,13 +356,15 @@
         config.compatSmallestScreenWidthDp = config.smallestScreenWidthDp;
     }
 
-    public boolean applyCompatConfigurationLocked(int displayDensity,
+    public boolean applyCompatConfiguration(int displayDensity,
             @NonNull Configuration compatConfiguration) {
-        if (mResCompatibilityInfo != null && !mResCompatibilityInfo.supportsScreen()) {
-            mResCompatibilityInfo.applyToConfiguration(displayDensity, compatConfiguration);
-            return true;
+        synchronized (mLock) {
+            if (mResCompatibilityInfo != null && !mResCompatibilityInfo.supportsScreen()) {
+                mResCompatibilityInfo.applyToConfiguration(displayDensity, compatConfiguration);
+                return true;
+            }
+            return false;
         }
-        return false;
     }
 
     /**
@@ -376,7 +383,7 @@
         final Pair<Integer, DisplayAdjustments> key =
                 Pair.create(displayId, displayAdjustmentsCopy);
         SoftReference<Display> sd;
-        synchronized (this) {
+        synchronized (mLock) {
             sd = mAdjustedDisplays.get(key);
         }
         if (sd != null) {
@@ -392,7 +399,7 @@
         }
         final Display display = dm.getCompatibleDisplay(displayId, key.second);
         if (display != null) {
-            synchronized (this) {
+            synchronized (mLock) {
                 mAdjustedDisplays.put(key, new SoftReference<>(display));
             }
         }
@@ -407,7 +414,7 @@
      * @param resources The {@link Resources} backing the display adjustments.
      */
     public Display getAdjustedDisplay(final int displayId, Resources resources) {
-        synchronized (this) {
+        synchronized (mLock) {
             final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
             if (dm == null) {
                 // may be null early in system startup
@@ -425,7 +432,7 @@
         ApkAssets apkAssets;
 
         // Optimistically check if this ApkAssets exists somewhere else.
-        synchronized (this) {
+        synchronized (mLock) {
             final WeakReference<ApkAssets> apkAssetsRef = mCachedApkAssets.get(key);
             if (apkAssetsRef != null) {
                 apkAssets = apkAssetsRef.get();
@@ -447,7 +454,7 @@
                     key.sharedLib ? ApkAssets.PROPERTY_DYNAMIC : 0);
         }
 
-        synchronized (this) {
+        synchronized (mLock) {
             mCachedApkAssets.put(key, new WeakReference<>(apkAssets));
         }
 
@@ -559,7 +566,7 @@
      * @hide
      */
     public void dump(String prefix, PrintWriter printWriter) {
-        synchronized (this) {
+        synchronized (mLock) {
             IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
             for (int i = 0; i < prefix.length() / 2; i++) {
                 pw.increaseIndent();
@@ -688,7 +695,7 @@
      */
     boolean isSameResourcesOverrideConfig(@Nullable IBinder activityToken,
             @Nullable Configuration overrideConfig) {
-        synchronized (this) {
+        synchronized (mLock) {
             final ActivityResources activityResources
                     = activityToken != null ? mActivityResourceReferences.get(activityToken) : null;
             if (activityResources == null) {
@@ -834,7 +841,7 @@
                         + " with key=" + key);
             }
 
-            synchronized (this) {
+            synchronized (mLock) {
                 // Force the creation of an ActivityResourcesStruct.
                 getOrCreateActivityResourcesStructLocked(token);
             }
@@ -842,7 +849,7 @@
             // Update any existing Activity Resources references.
             updateResourcesForActivity(token, overrideConfig, displayId);
 
-            synchronized (this) {
+            synchronized (mLock) {
                 Resources resources = findResourcesForActivityLocked(token, key,
                         classLoader);
                 if (resources != null) {
@@ -868,7 +875,7 @@
      */
     private void rebaseKeyForActivity(IBinder activityToken, ResourcesKey key,
             boolean overridesActivityDisplay) {
-        synchronized (this) {
+        synchronized (mLock) {
             final ActivityResources activityResources =
                     getOrCreateActivityResourcesStructLocked(activityToken);
 
@@ -960,7 +967,7 @@
         Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
                 "ResourcesManager#createApkAssetsSupplierNotLocked");
         try {
-            if (DEBUG && Thread.holdsLock(this)) {
+            if (DEBUG && Thread.holdsLock(mLock)) {
                 Slog.w(TAG, "Calling thread " + Thread.currentThread().getName()
                     + " is holding mLock", new Throwable());
             }
@@ -994,7 +1001,7 @@
     @Nullable
     private Resources createResources(@NonNull ResourcesKey key, @NonNull ClassLoader classLoader,
             @Nullable ApkAssetsSupplier apkSupplier) {
-        synchronized (this) {
+        synchronized (mLock) {
             if (DEBUG) {
                 Throwable here = new Throwable();
                 here.fillInStackTrace();
@@ -1015,7 +1022,7 @@
             @NonNull ResourcesKey key, @NonNull Configuration initialOverrideConfig,
             @Nullable Integer overrideDisplayId, @NonNull ClassLoader classLoader,
             @Nullable ApkAssetsSupplier apkSupplier) {
-        synchronized (this) {
+        synchronized (mLock) {
             if (DEBUG) {
                 Throwable here = new Throwable();
                 here.fillInStackTrace();
@@ -1130,7 +1137,7 @@
             if (displayId == INVALID_DISPLAY) {
                 throw new IllegalArgumentException("displayId can not be INVALID_DISPLAY");
             }
-            synchronized (this) {
+            synchronized (mLock) {
                 final ActivityResources activityResources =
                         getOrCreateActivityResourcesStructLocked(activityToken);
 
@@ -1269,67 +1276,64 @@
 
     public final boolean applyConfigurationToResources(@NonNull Configuration config,
             @Nullable CompatibilityInfo compat) {
-        synchronized(this) {
-            return applyConfigurationToResourcesLocked(config, compat, null /* adjustments */);
-        }
-    }
-
-    public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
-            @Nullable CompatibilityInfo compat) {
-        return applyConfigurationToResourcesLocked(config, compat, null /* adjustments */);
+        return applyConfigurationToResources(config, compat, null /* adjustments */);
     }
 
     /** Applies the global configuration to the managed resources. */
-    public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
+    public final boolean applyConfigurationToResources(@NonNull Configuration config,
             @Nullable CompatibilityInfo compat, @Nullable DisplayAdjustments adjustments) {
-        try {
-            Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
-                    "ResourcesManager#applyConfigurationToResourcesLocked");
+        synchronized (mLock) {
+            try {
+                Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
+                        "ResourcesManager#applyConfigurationToResources");
 
-            if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
-                if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
-                        + mResConfiguration.seq + ", newSeq=" + config.seq);
-                return false;
-            }
-
-            // Things might have changed in display manager, so clear the cached displays.
-            mAdjustedDisplays.clear();
-
-            int changes = mResConfiguration.updateFrom(config);
-            if (compat != null && (mResCompatibilityInfo == null ||
-                    !mResCompatibilityInfo.equals(compat))) {
-                mResCompatibilityInfo = compat;
-                changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
-                        | ActivityInfo.CONFIG_SCREEN_SIZE
-                        | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
-            }
-
-            DisplayMetrics displayMetrics = getDisplayMetrics();
-            if (adjustments != null) {
-                // Currently the only case where the adjustment takes effect is to simulate placing
-                // an app in a rotated display.
-                adjustments.adjustGlobalAppMetrics(displayMetrics);
-            }
-            Resources.updateSystemConfiguration(config, displayMetrics, compat);
-
-            ApplicationPackageManager.configurationChanged();
-
-            Configuration tmpConfig = new Configuration();
-
-            for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
-                ResourcesKey key = mResourceImpls.keyAt(i);
-                WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
-                ResourcesImpl r = weakImplRef != null ? weakImplRef.get() : null;
-                if (r != null) {
-                    applyConfigurationToResourcesLocked(config, compat, tmpConfig, key, r);
-                } else {
-                    mResourceImpls.removeAt(i);
+                if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
+                    if (DEBUG || DEBUG_CONFIGURATION) {
+                        Slog.v(TAG, "Skipping new config: curSeq="
+                                + mResConfiguration.seq + ", newSeq=" + config.seq);
+                    }
+                    return false;
                 }
-            }
 
-            return changes != 0;
-        } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+                // Things might have changed in display manager, so clear the cached displays.
+                mAdjustedDisplays.clear();
+
+                int changes = mResConfiguration.updateFrom(config);
+                if (compat != null && (mResCompatibilityInfo == null
+                        || !mResCompatibilityInfo.equals(compat))) {
+                    mResCompatibilityInfo = compat;
+                    changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
+                            | ActivityInfo.CONFIG_SCREEN_SIZE
+                            | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+                }
+
+                DisplayMetrics displayMetrics = getDisplayMetrics();
+                if (adjustments != null) {
+                    // Currently the only case where the adjustment takes effect is to simulate
+                    // placing an app in a rotated display.
+                    adjustments.adjustGlobalAppMetrics(displayMetrics);
+                }
+                Resources.updateSystemConfiguration(config, displayMetrics, compat);
+
+                ApplicationPackageManager.configurationChanged();
+
+                Configuration tmpConfig = new Configuration();
+
+                for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
+                    ResourcesKey key = mResourceImpls.keyAt(i);
+                    WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
+                    ResourcesImpl r = weakImplRef != null ? weakImplRef.get() : null;
+                    if (r != null) {
+                        applyConfigurationToResourcesLocked(config, compat, tmpConfig, key, r);
+                    } else {
+                        mResourceImpls.removeAt(i);
+                    }
+                }
+
+                return changes != 0;
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
+            }
         }
     }
 
@@ -1378,7 +1382,7 @@
      * @param libAssets The library asset paths to add.
      */
     public void appendLibAssetsForMainAssetPath(String assetPath, String[] libAssets) {
-        synchronized (this) {
+        synchronized (mLock) {
             // Record which ResourcesImpl need updating
             // (and what ResourcesKey they should update to).
             final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>();
@@ -1414,54 +1418,56 @@
     }
 
     // TODO(adamlesinski): Make this accept more than just overlay directories.
-    final void applyNewResourceDirsLocked(@NonNull final ApplicationInfo appInfo,
+    void applyNewResourceDirs(@NonNull final ApplicationInfo appInfo,
             @Nullable final String[] oldPaths) {
-        try {
-            Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
-                    "ResourcesManager#applyNewResourceDirsLocked");
+        synchronized (mLock) {
+            try {
+                Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
+                        "ResourcesManager#applyNewResourceDirsLocked");
 
-            String baseCodePath = appInfo.getBaseCodePath();
+                String baseCodePath = appInfo.getBaseCodePath();
 
-            final int myUid = Process.myUid();
-            String[] newSplitDirs = appInfo.uid == myUid
-                    ? appInfo.splitSourceDirs
-                    : appInfo.splitPublicSourceDirs;
+                final int myUid = Process.myUid();
+                String[] newSplitDirs = appInfo.uid == myUid
+                        ? appInfo.splitSourceDirs
+                        : appInfo.splitPublicSourceDirs;
 
-            // ApplicationInfo is mutable, so clone the arrays to prevent outside modification
-            String[] copiedSplitDirs = ArrayUtils.cloneOrNull(newSplitDirs);
-            String[] copiedResourceDirs = combinedOverlayPaths(appInfo.resourceDirs,
-                    appInfo.overlayPaths);
+                // ApplicationInfo is mutable, so clone the arrays to prevent outside modification
+                String[] copiedSplitDirs = ArrayUtils.cloneOrNull(newSplitDirs);
+                String[] copiedResourceDirs = combinedOverlayPaths(appInfo.resourceDirs,
+                        appInfo.overlayPaths);
 
-            final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>();
-            final int implCount = mResourceImpls.size();
-            for (int i = 0; i < implCount; i++) {
-                final ResourcesKey key = mResourceImpls.keyAt(i);
-                final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
-                final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
+                final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceKeys = new ArrayMap<>();
+                final int implCount = mResourceImpls.size();
+                for (int i = 0; i < implCount; i++) {
+                    final ResourcesKey key = mResourceImpls.keyAt(i);
+                    final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
+                    final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
 
-                if (impl == null) {
-                    continue;
+                    if (impl == null) {
+                        continue;
+                    }
+
+                    if (key.mResDir == null
+                            || key.mResDir.equals(baseCodePath)
+                            || ArrayUtils.contains(oldPaths, key.mResDir)) {
+                        updatedResourceKeys.put(impl, new ResourcesKey(
+                                baseCodePath,
+                                copiedSplitDirs,
+                                copiedResourceDirs,
+                                key.mLibDirs,
+                                key.mDisplayId,
+                                key.mOverrideConfiguration,
+                                key.mCompatInfo,
+                                key.mLoaders
+                        ));
+                    }
                 }
 
-                if (key.mResDir == null
-                        || key.mResDir.equals(baseCodePath)
-                        || ArrayUtils.contains(oldPaths, key.mResDir)) {
-                    updatedResourceKeys.put(impl, new ResourcesKey(
-                            baseCodePath,
-                            copiedSplitDirs,
-                            copiedResourceDirs,
-                            key.mLibDirs,
-                            key.mDisplayId,
-                            key.mOverrideConfiguration,
-                            key.mCompatInfo,
-                            key.mLoaders
-                    ));
-                }
+                redirectResourcesToNewImplLocked(updatedResourceKeys);
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
             }
-
-            redirectResourcesToNewImplLocked(updatedResourceKeys);
-        } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
         }
     }
 
@@ -1556,7 +1562,7 @@
     public boolean overrideTokenDisplayAdjustments(IBinder token,
             @Nullable Consumer<DisplayAdjustments> override) {
         boolean handled = false;
-        synchronized (this) {
+        synchronized (mLock) {
             final ActivityResources tokenResources = mActivityResourceReferences.get(token);
             if (tokenResources == null) {
                 return false;
@@ -1589,7 +1595,7 @@
         @Override
         public void onLoadersChanged(@NonNull Resources resources,
                 @NonNull List<ResourcesLoader> newLoader) {
-            synchronized (ResourcesManager.this) {
+            synchronized (mLock) {
                 final ResourcesKey oldKey = findKeyForResourceImplLocked(resources.getImpl());
                 if (oldKey == null) {
                     throw new IllegalArgumentException("Cannot modify resource loaders of"
@@ -1617,7 +1623,7 @@
          **/
         @Override
         public void onLoaderUpdated(@NonNull ResourcesLoader loader) {
-            synchronized (ResourcesManager.this) {
+            synchronized (mLock) {
                 final ArrayMap<ResourcesImpl, ResourcesKey> updatedResourceImplKeys =
                         new ArrayMap<>();
 
diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java
index c5c4277..dfef47d 100644
--- a/core/java/android/bluetooth/le/ScanFilter.java
+++ b/core/java/android/bluetooth/le/ScanFilter.java
@@ -586,6 +586,10 @@
          * @throws IllegalArgumentException If the {@code deviceAddress} is invalid.
          */
         public Builder setDeviceAddress(String deviceAddress) {
+            if (deviceAddress == null) {
+                mDeviceAddress = deviceAddress;
+                return this;
+            }
             return setDeviceAddress(deviceAddress, BluetoothDevice.ADDRESS_TYPE_PUBLIC);
         }
 
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 7c7cfdb..bbb49fb 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2022,12 +2022,9 @@
      * <p>
      * Output: Nothing.
      * </p>
-     *
-     * @hide
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     @RequiresPermission(android.Manifest.permission.START_VIEW_PERMISSION_USAGE)
-    @SystemApi
     public static final String ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD =
             "android.intent.action.VIEW_PERMISSION_USAGE_FOR_PERIOD";
 
@@ -2188,12 +2185,7 @@
      * <p>
      * Type: String
      * </p>
-     *
-     * E.g. {@link android.Manifest.permission_group.CONTACTS}
-     *
-     * @hide
      */
-    @SystemApi
     public static final String EXTRA_PERMISSION_GROUP_NAME =
             "android.intent.extra.PERMISSION_GROUP_NAME";
 
@@ -5342,28 +5334,19 @@
      * {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD}
      *
      * E.g. an attribution tag could be location_provider, com.google.android.gms.*, etc.
-     *
-     * @hide
      */
-    @SystemApi
     public static final String EXTRA_ATTRIBUTION_TAGS = "android.intent.extra.ATTRIBUTION_TAGS";
 
     /**
      * A long representing the start timestamp (epoch time in millis) of the permission usage
      * when used with {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD}
-     *
-     * @hide
      */
-    @SystemApi
     public static final String EXTRA_START_TIME = "android.intent.extra.START_TIME";
 
     /**
      * A long representing the end timestamp (epoch time in millis) of the permission usage when
      * used with {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD}
-     *
-     * @hide
      */
-    @SystemApi
     public static final String EXTRA_END_TIME = "android.intent.extra.END_TIME";
 
     /**
diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl
index 9d381ef..804a06b 100644
--- a/core/java/android/content/pm/IShortcutService.aidl
+++ b/core/java/android/content/pm/IShortcutService.aidl
@@ -81,7 +81,4 @@
     AndroidFuture<ParceledListSlice> getShortcuts(String packageName, int matchFlags, int userId);
 
     AndroidFuture pushDynamicShortcut(String packageName, in ShortcutInfo shortcut, int userId);
-
-    AndroidFuture updateShortcutVisibility(String callingPkg, String packageName,
-            in byte[] certificate, in boolean visible, int userId);
 }
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index a60e642..13ff602 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -20,6 +20,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -29,6 +30,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * This class provides information for a shared library. There are
@@ -190,7 +192,8 @@
      *
      * @hide
      */
-    public List<String> getAllCodePaths() {
+    @TestApi
+    public @NonNull List<String> getAllCodePaths() {
         if (getPath() != null) {
             // Builtin library.
             ArrayList<String> list = new ArrayList<>();
@@ -198,7 +201,7 @@
             return list;
         } else {
             // Static or dynamic library.
-            return mCodePaths;
+            return Objects.requireNonNull(mCodePaths);
         }
     }
 
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index 2a36c11..d77fa91 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -40,7 +40,6 @@
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.UserHandle;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.infra.AndroidFuture;
@@ -790,23 +789,6 @@
         }
     }
 
-    /**
-     * Granting another app the access to the shortcuts you own. You must provide the package name
-     * and their SHA256 certificate digest in order to granting the access.
-     *
-     * Once granted, the other app can retain a copy of all the shortcuts you own when calling
-     * {@link LauncherApps#getShortcuts(LauncherApps.ShortcutQuery, UserHandle)}.
-     */
-    public void updateShortcutVisibility(@NonNull final String packageName,
-            @Nullable final byte[] certificate, final boolean visible) {
-        try {
-            getFutureOrThrow(mService.updateShortcutVisibility(mContext.getPackageName(),
-                    packageName, certificate, visible, injectMyUserId()));
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
     private static <T> T getFutureOrThrow(@NonNull AndroidFuture<T> future) {
         try {
             return future.get();
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index 8bc3734..aa26520 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -39,6 +39,9 @@
           "include-filter": "android.incrementalinstall.cts.IncrementalFeatureTest"
         }
       ]
+    },
+    {
+      "name": "CtsPackageManagerBootTestCases"
     }
   ],
   "postsubmit": [
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index 75fa1c1..a9d70c5 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -397,7 +397,7 @@
         }
 
         // CompatibilityMode is global state.
-        if (!ParsingPackageUtils.sCompatibilityModeEnabled) {
+        if (!android.content.pm.PackageParser.sCompatibilityModeEnabled) {
             ai.disableCompatibilityMode();
         }
 
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index 80b5078..8e3de61 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -21,9 +21,11 @@
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.graphics.ImageFormat;
+import android.hardware.camera2.extension.IAdvancedExtenderImpl;
 import android.hardware.camera2.extension.ICameraExtensionsProxyService;
 import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
 import android.hardware.camera2.extension.IPreviewExtenderImpl;
+import android.hardware.camera2.extension.LatencyRange;
 import android.hardware.camera2.extension.SizeList;
 import android.hardware.camera2.params.ExtensionSessionConfiguration;
 import android.hardware.camera2.params.StreamConfigurationMap;
@@ -220,6 +222,7 @@
         private InitializerFuture mInitFuture = null;
         private ServiceConnection mConnection = null;
         private ICameraExtensionsProxyService mProxy = null;
+        private boolean mSupportsAdvancedExtensions = false;
 
         // Singleton, don't allow construction
         private CameraExtensionManagerGlobal() {}
@@ -245,6 +248,11 @@
                     public void onServiceConnected(ComponentName component, IBinder binder) {
                         mProxy = ICameraExtensionsProxyService.Stub.asInterface(binder);
                         mInitFuture.setStatus(true);
+                        try {
+                            mSupportsAdvancedExtensions = mProxy.advancedExtensionsSupported();
+                        } catch (RemoteException e) {
+                            Log.e(TAG, "Remote IPC failed!");
+                        }
                     }
                 };
                 ctx.bindService(intent, mConnection, Context.BIND_AUTO_CREATE |
@@ -334,6 +342,10 @@
             }
         }
 
+        public boolean areAdvancedExtensionsSupported() {
+            return mSupportsAdvancedExtensions;
+        }
+
         public IPreviewExtenderImpl initializePreviewExtension(int extensionType)
                 throws RemoteException {
             synchronized (mLock) {
@@ -355,6 +367,17 @@
                 }
             }
         }
+
+        public IAdvancedExtenderImpl initializeAdvancedExtension(int extensionType)
+                throws RemoteException {
+            synchronized (mLock) {
+                if (mProxy != null) {
+                    return mProxy.initializeAdvancedExtension(extensionType);
+                } else {
+                    return null;
+                }
+            }
+        }
     }
 
     /**
@@ -374,23 +397,60 @@
     /**
      * @hide
      */
+    public static boolean areAdvancedExtensionsSupported() {
+        return CameraExtensionManagerGlobal.get().areAdvancedExtensionsSupported();
+    }
+
+    /**
+     * @hide
+     */
     public static boolean isExtensionSupported(String cameraId, int extensionType,
             CameraCharacteristics chars) {
-        Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders;
+        if (areAdvancedExtensionsSupported()) {
+            try {
+                IAdvancedExtenderImpl extender = initializeAdvancedExtension(extensionType);
+                return extender.isExtensionAvailable(cameraId);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to query extension availability! Extension service does not"
+                        + " respond!");
+                return false;
+            }
+        } else {
+            Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders;
+            try {
+                extenders = initializeExtension(extensionType);
+            } catch (IllegalArgumentException e) {
+                return false;
+            }
+
+            try {
+                return extenders.first.isExtensionAvailable(cameraId, chars.getNativeMetadata()) &&
+                        extenders.second.isExtensionAvailable(cameraId, chars.getNativeMetadata());
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to query extension availability! Extension service does not"
+                        + " respond!");
+                return false;
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public static IAdvancedExtenderImpl initializeAdvancedExtension(@Extension int extensionType) {
+        IAdvancedExtenderImpl extender;
         try {
-            extenders = initializeExtension(extensionType);
-        } catch (IllegalArgumentException e) {
-            return false;
+            extender = CameraExtensionManagerGlobal.get().initializeAdvancedExtension(
+                    extensionType);
+        } catch (RemoteException e) {
+            throw new IllegalStateException("Failed to initialize extension: " + extensionType);
         }
 
-        try {
-            return extenders.first.isExtensionAvailable(cameraId, chars.getNativeMetadata()) &&
-                extenders.second.isExtensionAvailable(cameraId, chars.getNativeMetadata());
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to query extension availability! Extension service does not"
-                    + " respond!");
-            return false;
+        if (extender == null) {
+            throw new IllegalArgumentException("Unknown extension: " + extensionType);
         }
+
+        return extender;
     }
 
     /**
@@ -487,13 +547,21 @@
                 throw new IllegalArgumentException("Unsupported extension");
             }
 
-            Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
-                    initializeExtension(extension);
             StreamConfigurationMap streamMap = mChars.get(
                     CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
-            extenders.first.init(mCameraId, mChars.getNativeMetadata());
-            return generateSupportedSizes(extenders.first.getSupportedResolutions(),
-                    ImageFormat.PRIVATE, streamMap);
+            if (areAdvancedExtensionsSupported()) {
+                IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
+                extender.init(mCameraId);
+                return generateSupportedSizes(
+                        extender.getSupportedPreviewOutputResolutions(mCameraId),
+                        ImageFormat.PRIVATE, streamMap);
+            } else {
+                Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
+                        initializeExtension(extension);
+                extenders.first.init(mCameraId, mChars.getNativeMetadata());
+                return generateSupportedSizes(extenders.first.getSupportedResolutions(),
+                        ImageFormat.PRIVATE, streamMap);
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to query the extension supported sizes! Extension service does"
                     + " not respond!");
@@ -536,31 +604,47 @@
                     throw new IllegalArgumentException("Unsupported extension");
                 }
 
-                Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
-                        initializeExtension(extension);
                 StreamConfigurationMap streamMap = mChars.get(
                         CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
-                if (format == ImageFormat.YUV_420_888) {
-                    extenders.second.init(mCameraId, mChars.getNativeMetadata());
-                    if (extenders.second.getCaptureProcessor() == null) {
-                        // Extensions that don't implement any capture processor are limited to
-                        // JPEG only!
-                        return new ArrayList<>();
+                if (areAdvancedExtensionsSupported()) {
+                    switch(format) {
+                        case ImageFormat.YUV_420_888:
+                        case ImageFormat.JPEG:
+                            break;
+                        default:
+                            throw new IllegalArgumentException("Unsupported format: " + format);
                     }
-                    return generateSupportedSizes(extenders.second.getSupportedResolutions(),
-                            format, streamMap);
-                } else if (format == ImageFormat.JPEG) {
-                    extenders.second.init(mCameraId, mChars.getNativeMetadata());
-                    if (extenders.second.getCaptureProcessor() != null) {
-                        // The framework will perform the additional encoding pass on the
-                        // processed YUV_420 buffers.
-                        return generateJpegSupportedSizes(
-                                extenders.second.getSupportedResolutions(), streamMap);
-                    } else {
-                        return generateSupportedSizes(null, format, streamMap);
-                    }
+                    IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
+                    extender.init(mCameraId);
+                    return generateSupportedSizes(extender.getSupportedCaptureOutputResolutions(
+                            mCameraId), format, streamMap);
                 } else {
-                    throw new IllegalArgumentException("Unsupported format: " + format);
+                    if (format == ImageFormat.YUV_420_888) {
+                        Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
+                                initializeExtension(extension);
+                        extenders.second.init(mCameraId, mChars.getNativeMetadata());
+                        if (extenders.second.getCaptureProcessor() == null) {
+                            // Extensions that don't implement any capture processor are limited to
+                            // JPEG only!
+                            return new ArrayList<>();
+                        }
+                        return generateSupportedSizes(extenders.second.getSupportedResolutions(),
+                                format, streamMap);
+                    } else if (format == ImageFormat.JPEG) {
+                        Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
+                                initializeExtension(extension);
+                        extenders.second.init(mCameraId, mChars.getNativeMetadata());
+                        if (extenders.second.getCaptureProcessor() != null) {
+                            // The framework will perform the additional encoding pass on the
+                            // processed YUV_420 buffers.
+                            return generateJpegSupportedSizes(
+                                    extenders.second.getSupportedResolutions(), streamMap);
+                        } else {
+                            return generateSupportedSizes(null, format, streamMap);
+                        }
+                    } else {
+                        throw new IllegalArgumentException("Unsupported format: " + format);
+                    }
                 }
             } finally {
                 unregisterClient(clientId);
@@ -608,6 +692,23 @@
             if (!isExtensionSupported(mCameraId, extension, mChars)) {
                 throw new IllegalArgumentException("Unsupported extension");
             }
+
+            if (areAdvancedExtensionsSupported()) {
+                IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
+                extender.init(mCameraId);
+                android.hardware.camera2.extension.Size sz =
+                        new android.hardware.camera2.extension.Size();
+                sz.width = captureOutputSize.getWidth();
+                sz.height = captureOutputSize.getHeight();
+                LatencyRange latencyRange = extender.getEstimatedCaptureLatencyRange(mCameraId,
+                        sz, format);
+                if (latencyRange != null) {
+                    return new Range(latencyRange.min, latencyRange.max);
+                }
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to query the extension capture latency! Extension service does"
+                    + " not respond!");
         } finally {
             unregisterClient(clientId);
         }
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 6ff68c1..d32341f 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -230,6 +230,33 @@
     }
 
     /**
+     * Takes ownership of the passed-in properties object
+     *
+     * <p>For internal use only</p>
+     * @hide
+     */
+    public CaptureResult(String cameraId, CameraMetadataNative results, CaptureRequest parent,
+            int requestId, long frameNumber) {
+        if (results == null) {
+            throw new IllegalArgumentException("results was null");
+        }
+
+        if (parent == null) {
+            throw new IllegalArgumentException("parent was null");
+        }
+
+        mResults = CameraMetadataNative.move(results);
+        if (mResults.isEmpty()) {
+            throw new AssertionError("Results must not be empty");
+        }
+        setNativeInstance(mResults);
+        mCameraId = cameraId;
+        mRequest = parent;
+        mSequenceId = requestId;
+        mFrameNumber = frameNumber;
+    }
+
+    /**
      * Returns a copy of the underlying {@link CameraMetadataNative}.
      * @hide
      */
diff --git a/core/java/android/hardware/camera2/TotalCaptureResult.java b/core/java/android/hardware/camera2/TotalCaptureResult.java
index df8eecc..ac7f2ca 100644
--- a/core/java/android/hardware/camera2/TotalCaptureResult.java
+++ b/core/java/android/hardware/camera2/TotalCaptureResult.java
@@ -94,6 +94,36 @@
     }
 
     /**
+     * Takes ownership of the passed-in camera metadata and the partial results
+     *
+     * @param partials a list of partial results; {@code null} will be substituted for an empty list
+     * @hide
+     */
+    public TotalCaptureResult(String logicalCameraId, CameraMetadataNative results,
+            CaptureRequest parent, int requestId, long frameNumber, List<CaptureResult> partials,
+            int sessionId, PhysicalCaptureResultInfo[] physicalResults) {
+        super(logicalCameraId, results, parent, requestId, frameNumber);
+
+        if (partials == null) {
+            mPartialResults = new ArrayList<>();
+        } else {
+            mPartialResults = partials;
+        }
+
+        mSessionId = sessionId;
+
+        mPhysicalCaptureResults = new HashMap<String, TotalCaptureResult>();
+        for (PhysicalCaptureResultInfo onePhysicalResult : physicalResults) {
+            TotalCaptureResult physicalResult = new TotalCaptureResult(
+                    onePhysicalResult.getCameraId(), onePhysicalResult.getCameraMetadata(),
+                    parent, requestId, frameNumber, /*partials*/null, sessionId,
+                    new PhysicalCaptureResultInfo[0]);
+            mPhysicalCaptureResults.put(onePhysicalResult.getCameraId(),
+                    physicalResult);
+        }
+    }
+
+    /**
      * Creates a request-less result.
      *
      * <p><strong>For testing only.</strong></p>
diff --git a/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl b/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl
new file mode 100644
index 0000000..a61bb33
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl
@@ -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 android.hardware.camera2.extension;
+
+import android.hardware.camera2.extension.Size;
+import android.hardware.camera2.extension.OutputConfigId;
+import android.view.Surface;
+
+/** @hide */
+parcelable CameraOutputConfig
+{
+    Size size;
+    Surface surface;
+    int imageFormat;
+    int capacity;
+
+    const int TYPE_SURFACE = 0;
+    const int TYPE_IMAGEREADER = 1;
+    const int TYPE_MULTIRES_IMAGEREADER = 2;
+    int type;
+
+    OutputConfigId outputId;
+    int surfaceGroupId;
+    String physicalCameraId;
+    List<OutputConfigId> surfaceSharingOutputConfigs;
+}
diff --git a/core/java/android/hardware/camera2/extension/CameraSessionConfig.aidl b/core/java/android/hardware/camera2/extension/CameraSessionConfig.aidl
new file mode 100644
index 0000000..97ce183
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/CameraSessionConfig.aidl
@@ -0,0 +1,27 @@
+/**
+ * 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.hardware.camera2.extension;
+
+import android.hardware.camera2.extension.CameraOutputConfig;
+import android.hardware.camera2.impl.CameraMetadataNative;
+
+/** @hide */
+parcelable CameraSessionConfig
+{
+    List<CameraOutputConfig> outputConfigs;
+    CameraMetadataNative sessionParameter;
+    int sessionTemplateId;
+}
diff --git a/core/java/android/hardware/camera2/extension/CaptureFailure.aidl b/core/java/android/hardware/camera2/extension/CaptureFailure.aidl
new file mode 100644
index 0000000..d48696c
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/CaptureFailure.aidl
@@ -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 android.hardware.camera2.extension;
+
+import android.hardware.camera2.CaptureRequest;
+
+/** @hide */
+parcelable CaptureFailure
+{
+    CaptureRequest request;
+    int reason;
+    boolean dropped;
+    int sequenceId;
+    long frameNumber;
+    String errorPhysicalCameraId;
+}
diff --git a/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl b/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl
new file mode 100644
index 0000000..f279c59
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl
@@ -0,0 +1,33 @@
+/**
+ * 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.hardware.camera2.extension;
+
+import android.hardware.camera2.extension.ISessionProcessorImpl;
+import android.hardware.camera2.extension.LatencyRange;
+import android.hardware.camera2.extension.Size;
+import android.hardware.camera2.extension.SizeList;
+
+/** @hide */
+interface IAdvancedExtenderImpl
+{
+    boolean isExtensionAvailable(in String cameraId);
+    void init(in String cameraId);
+    LatencyRange getEstimatedCaptureLatencyRange(in String cameraId, in Size outputSize,
+            int format);
+    @nullable List<SizeList> getSupportedPreviewOutputResolutions(in String cameraId);
+    @nullable List<SizeList> getSupportedCaptureOutputResolutions(in String cameraId);
+    ISessionProcessorImpl getSessionProcessor();
+}
diff --git a/core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl b/core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl
index 2a6d22c..bc29e9a 100644
--- a/core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl
+++ b/core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl
@@ -15,6 +15,7 @@
  */
 package android.hardware.camera2.extension;
 
+import android.hardware.camera2.extension.IAdvancedExtenderImpl;
 import android.hardware.camera2.extension.IPreviewExtenderImpl;
 import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
 
@@ -23,6 +24,8 @@
 {
     long registerClient();
     void unregisterClient(long clientId);
+    boolean advancedExtensionsSupported();
     @nullable IPreviewExtenderImpl initializePreviewExtension(int extensionType);
     @nullable IImageCaptureExtenderImpl initializeImageExtension(int extensionType);
+    @nullable IAdvancedExtenderImpl initializeAdvancedExtension(int extensionType);
 }
diff --git a/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl b/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl
new file mode 100644
index 0000000..6ab0ad2
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/ICaptureCallback.aidl
@@ -0,0 +1,28 @@
+/**
+ * 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.hardware.camera2.extension;
+
+import android.hardware.camera2.extension.Request;
+
+/** @hide */
+interface ICaptureCallback
+{
+    void onCaptureStarted(int captureSequenceId, long timestamp);
+    void onCaptureProcessStarted(int captureSequenceId);
+    void onCaptureFailed(int captureSequenceId);
+    void onCaptureSequenceCompleted(int captureSequenceId);
+    void onCaptureSequenceAborted(int captureSequenceId);
+}
diff --git a/core/java/android/hardware/camera2/extension/IImageProcessorImpl.aidl b/core/java/android/hardware/camera2/extension/IImageProcessorImpl.aidl
new file mode 100644
index 0000000..f365469
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/IImageProcessorImpl.aidl
@@ -0,0 +1,25 @@
+/**
+ * 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.hardware.camera2.extension;
+
+import android.hardware.camera2.extension.OutputConfigId;
+import android.hardware.camera2.extension.ParcelImage;
+
+/** @hide */
+interface IImageProcessorImpl
+{
+    void onNextImageAvailable(in OutputConfigId outputConfigId, in ParcelImage image);
+}
diff --git a/core/java/android/hardware/camera2/extension/IRequestCallback.aidl b/core/java/android/hardware/camera2/extension/IRequestCallback.aidl
new file mode 100644
index 0000000..5f308b7
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/IRequestCallback.aidl
@@ -0,0 +1,32 @@
+/**
+ * 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.hardware.camera2.extension;
+
+import android.hardware.camera2.extension.CaptureFailure;
+import android.hardware.camera2.extension.ParcelCaptureResult;
+import android.hardware.camera2.extension.ParcelTotalCaptureResult;
+
+/** @hide */
+interface IRequestCallback
+{
+    void onCaptureStarted(int requestId, long frameNumber, long timestamp);
+    void onCaptureProgressed(int requestId, in ParcelCaptureResult partialResult);
+    void onCaptureCompleted(int requestId, in ParcelTotalCaptureResult totalCaptureResult);
+    void onCaptureFailed(int requestId, in CaptureFailure captureFailure);
+    void onCaptureBufferLost(int requestId, long frameNumber, int outputStreamId);
+    void onCaptureSequenceCompleted(int sequenceId, long frameNumber);
+    void onCaptureSequenceAborted(int sequenceId);
+}
diff --git a/core/java/android/hardware/camera2/extension/IRequestProcessorImpl.aidl b/core/java/android/hardware/camera2/extension/IRequestProcessorImpl.aidl
new file mode 100644
index 0000000..52595a8
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/IRequestProcessorImpl.aidl
@@ -0,0 +1,32 @@
+/**
+ * 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.hardware.camera2.extension;
+
+import android.hardware.camera2.extension.IImageProcessorImpl;
+import android.hardware.camera2.extension.IRequestCallback;
+import android.hardware.camera2.extension.OutputConfigId;
+import android.hardware.camera2.extension.Request;
+
+/** @hide */
+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);
+    void abortCaptures();
+    void stopRepeating();
+}
diff --git a/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl b/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl
new file mode 100644
index 0000000..6fdf4df
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl
@@ -0,0 +1,34 @@
+/**
+ * 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.hardware.camera2.extension;
+
+import android.hardware.camera2.extension.CameraSessionConfig;
+import android.hardware.camera2.extension.ICaptureCallback;
+import android.hardware.camera2.extension.IRequestProcessorImpl;
+import android.hardware.camera2.extension.OutputSurface;
+
+/** @hide */
+interface ISessionProcessorImpl
+{
+    CameraSessionConfig initSession(in String cameraId, in OutputSurface previewSurface,
+            in OutputSurface imageCaptureSurface);
+    void deInitSession();
+    void onCaptureSessionStart(IRequestProcessorImpl requestProcessor);
+    void onCaptureSessionEnd();
+    int startRepeating(in ICaptureCallback callback);
+    void stopRepeating();
+    int startCapture(in ICaptureCallback callback, int jpegRotation, int jpegQuality);
+}
diff --git a/core/java/android/hardware/camera2/extension/LatencyRange.aidl b/core/java/android/hardware/camera2/extension/LatencyRange.aidl
new file mode 100644
index 0000000..9bedbb0
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/LatencyRange.aidl
@@ -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 android.hardware.camera2.extension;
+
+/** @hide */
+parcelable LatencyRange
+{
+    long min;
+    long max;
+}
diff --git a/core/java/android/hardware/camera2/extension/OutputConfigId.aidl b/core/java/android/hardware/camera2/extension/OutputConfigId.aidl
new file mode 100644
index 0000000..b27f29a
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/OutputConfigId.aidl
@@ -0,0 +1,22 @@
+/**
+ * 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.hardware.camera2.extension;
+
+/** @hide */
+parcelable OutputConfigId
+{
+    int id;
+}
diff --git a/core/java/android/hardware/camera2/extension/OutputSurface.aidl b/core/java/android/hardware/camera2/extension/OutputSurface.aidl
new file mode 100644
index 0000000..8415379
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/OutputSurface.aidl
@@ -0,0 +1,27 @@
+/**
+ * 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.hardware.camera2.extension;
+
+import android.hardware.camera2.extension.Size;
+import android.view.Surface;
+
+/** @hide */
+parcelable OutputSurface
+{
+    Surface surface;
+    Size size;
+    int imageFormat;
+}
diff --git a/core/java/android/hardware/camera2/extension/ParcelCaptureResult.aidl b/core/java/android/hardware/camera2/extension/ParcelCaptureResult.aidl
new file mode 100644
index 0000000..f99b256
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/ParcelCaptureResult.aidl
@@ -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 android.hardware.camera2.extension;
+
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.impl.CameraMetadataNative;
+
+/** @hide */
+parcelable ParcelCaptureResult
+{
+    String cameraId;
+    CameraMetadataNative results;
+    CaptureRequest parent;
+    int sequenceId;
+    long frameNumber;
+}
diff --git a/core/java/android/hardware/camera2/extension/ParcelTotalCaptureResult.aidl b/core/java/android/hardware/camera2/extension/ParcelTotalCaptureResult.aidl
new file mode 100644
index 0000000..8021a57
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/ParcelTotalCaptureResult.aidl
@@ -0,0 +1,34 @@
+/**
+ * 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.hardware.camera2.extension;
+
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.extension.ParcelCaptureResult;
+import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.impl.PhysicalCaptureResultInfo;
+
+/** @hide */
+parcelable ParcelTotalCaptureResult
+{
+    String logicalCameraId;
+    CameraMetadataNative results;
+    CaptureRequest parent;
+    int sequenceId;
+    long frameNumber;
+    List<ParcelCaptureResult> partials;
+    int sessionId;
+    List<PhysicalCaptureResultInfo> physicalResult;
+}
diff --git a/core/java/android/hardware/camera2/extension/Request.aidl b/core/java/android/hardware/camera2/extension/Request.aidl
new file mode 100644
index 0000000..d9934dc
--- /dev/null
+++ b/core/java/android/hardware/camera2/extension/Request.aidl
@@ -0,0 +1,28 @@
+/**
+ * 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.hardware.camera2.extension;
+
+import android.hardware.camera2.extension.OutputConfigId;
+import android.hardware.camera2.impl.CameraMetadataNative;
+
+/** @hide */
+parcelable Request
+{
+    List<OutputConfigId> targetOutputConfigIds;
+    CameraMetadataNative parameters;
+    int templateId;
+    int requestId;
+}
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
new file mode 100644
index 0000000..abc487d
--- /dev/null
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -0,0 +1,917 @@
+/*
+ * 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.hardware.camera2.impl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.graphics.ImageFormat;
+import android.graphics.SurfaceTexture;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraExtensionCharacteristics;
+import android.hardware.camera2.CameraExtensionSession;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CaptureFailure;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.extension.CameraOutputConfig;
+import android.hardware.camera2.extension.CameraSessionConfig;
+import android.hardware.camera2.extension.IAdvancedExtenderImpl;
+import android.hardware.camera2.extension.ICaptureCallback;
+import android.hardware.camera2.extension.IImageProcessorImpl;
+import android.hardware.camera2.extension.IRequestCallback;
+import android.hardware.camera2.extension.IRequestProcessorImpl;
+import android.hardware.camera2.extension.ISessionProcessorImpl;
+import android.hardware.camera2.extension.OutputConfigId;
+import android.hardware.camera2.extension.OutputSurface;
+import android.hardware.camera2.extension.ParcelCaptureResult;
+import android.hardware.camera2.extension.ParcelImage;
+import android.hardware.camera2.extension.ParcelTotalCaptureResult;
+import android.hardware.camera2.extension.Request;
+import android.hardware.camera2.params.ExtensionSessionConfiguration;
+import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.SessionConfiguration;
+import android.hardware.camera2.utils.SurfaceUtils;
+import android.media.Image;
+import android.media.ImageReader;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Size;
+import android.view.Surface;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSession {
+    private static final String TAG = "CameraAdvancedExtensionSessionImpl";
+
+    private final Executor mExecutor;
+    private final CameraDevice mCameraDevice;
+    private final long mExtensionClientId;
+    private final Handler mHandler;
+    private final HandlerThread mHandlerThread;
+    private final CameraExtensionSession.StateCallback mCallbacks;
+    private final IAdvancedExtenderImpl mAdvancedExtender;
+    // maps camera outputs to extension output ids
+    private final HashMap<Surface, Integer> mSurfaceIdMap = new HashMap<>();
+    // maps camera extension output ids to camera registered image readers
+    private final HashMap<Integer, ImageReader> mReaderMap = new HashMap<>();
+    private final RequestProcessor mRequestProcessor = new RequestProcessor();
+
+    private Surface mClientRepeatingRequestSurface;
+    private Surface mClientCaptureSurface;
+    private CameraCaptureSession mCaptureSession = null;
+    private ISessionProcessorImpl mSessionProcessor = null;
+
+    private boolean mInitialized;
+
+
+    // Lock to synchronize cross-thread access to device public interface
+    final Object mInterfaceLock = new Object(); // access from this class and Session only!
+
+    /**
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.CAMERA)
+    public static CameraAdvancedExtensionSessionImpl createCameraAdvancedExtensionSession(
+            @NonNull CameraDevice cameraDevice, @NonNull Context ctx,
+            @NonNull ExtensionSessionConfiguration config)
+            throws CameraAccessException, RemoteException {
+        long clientId = CameraExtensionCharacteristics.registerClient(ctx);
+        if (clientId < 0) {
+            throw new UnsupportedOperationException("Unsupported extension!");
+        }
+
+        String cameraId = cameraDevice.getId();
+        CameraManager manager = ctx.getSystemService(CameraManager.class);
+        CameraCharacteristics chars = manager.getCameraCharacteristics(cameraId);
+        CameraExtensionCharacteristics extensionChars = new CameraExtensionCharacteristics(ctx,
+                cameraId, chars);
+
+        if (!CameraExtensionCharacteristics.isExtensionSupported(cameraDevice.getId(),
+                config.getExtension(), chars)) {
+            throw new UnsupportedOperationException("Unsupported extension type: " +
+                    config.getExtension());
+        }
+
+        if (config.getOutputConfigurations().isEmpty() ||
+                config.getOutputConfigurations().size() > 2) {
+            throw new IllegalArgumentException("Unexpected amount of output surfaces, received: " +
+                    config.getOutputConfigurations().size() + " expected <= 2");
+        }
+
+        int suitableSurfaceCount = 0;
+        List<Size> supportedPreviewSizes = extensionChars.getExtensionSupportedSizes(
+                config.getExtension(), SurfaceTexture.class);
+        Surface repeatingRequestSurface = CameraExtensionUtils.getRepeatingRequestSurface(
+                config.getOutputConfigurations(), supportedPreviewSizes);
+        if (repeatingRequestSurface != null) {
+            suitableSurfaceCount++;
+        }
+
+        HashMap<Integer, List<Size>> supportedCaptureSizes = new HashMap<>();
+        for (int format : CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS) {
+            List<Size> supportedSizes = extensionChars.getExtensionSupportedSizes(
+                    config.getExtension(), format);
+            if (supportedSizes != null) {
+                supportedCaptureSizes.put(format, supportedSizes);
+            }
+        }
+        Surface burstCaptureSurface = CameraExtensionUtils.getBurstCaptureSurface(
+                config.getOutputConfigurations(), supportedCaptureSizes);
+        if (burstCaptureSurface != null) {
+            suitableSurfaceCount++;
+        }
+
+        if (suitableSurfaceCount != config.getOutputConfigurations().size()) {
+            throw new IllegalArgumentException("One or more unsupported output surfaces found!");
+        }
+
+        IAdvancedExtenderImpl extender = CameraExtensionCharacteristics.initializeAdvancedExtension(
+                config.getExtension());
+        extender.init(cameraId);
+
+        CameraAdvancedExtensionSessionImpl ret = new CameraAdvancedExtensionSessionImpl(clientId,
+                extender, cameraDevice, repeatingRequestSurface, burstCaptureSurface,
+                config.getStateCallback(), config.getExecutor());
+        ret.initialize();
+
+        return ret;
+    }
+
+    private CameraAdvancedExtensionSessionImpl(long extensionClientId,
+            @NonNull IAdvancedExtenderImpl extender, @NonNull CameraDevice cameraDevice,
+            @Nullable Surface repeatingRequestSurface, @Nullable Surface burstCaptureSurface,
+            @NonNull CameraExtensionSession.StateCallback callback, @NonNull Executor executor) {
+        mExtensionClientId = extensionClientId;
+        mAdvancedExtender = extender;
+        mCameraDevice = cameraDevice;
+        mCallbacks = callback;
+        mExecutor = executor;
+        mClientRepeatingRequestSurface = repeatingRequestSurface;
+        mClientCaptureSurface = burstCaptureSurface;
+        mHandlerThread = new HandlerThread(TAG);
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+        mInitialized = false;
+    }
+
+    /**
+     * @hide
+     */
+    public synchronized void initialize() throws CameraAccessException, RemoteException {
+        if (mInitialized) {
+            Log.d(TAG, "Session already initialized");
+            return;
+        }
+
+        OutputSurface previewSurface = initializeParcelable(mClientRepeatingRequestSurface);
+        OutputSurface captureSurface = initializeParcelable(mClientCaptureSurface);
+        mSessionProcessor = mAdvancedExtender.getSessionProcessor();
+        CameraSessionConfig sessionConfig = mSessionProcessor.initSession(mCameraDevice.getId(),
+                previewSurface, captureSurface);
+        List<CameraOutputConfig> outputConfigs = sessionConfig.outputConfigs;
+        // map camera output ids to output configurations
+        HashMap<Integer, OutputConfiguration> cameraOutputs = new HashMap<>();
+        for (CameraOutputConfig output : outputConfigs) {
+            OutputConfiguration cameraOutput = null;
+            switch(output.type) {
+                case CameraOutputConfig.TYPE_SURFACE:
+                    if (output.surface == null) {
+                        Log.w(TAG, "Unsupported client output id: " + output.outputId.id +
+                                ", skipping!");
+                        continue;
+                    }
+                    cameraOutput = new OutputConfiguration(output.surfaceGroupId,
+                            output.surface);
+                    break;
+                case CameraOutputConfig.TYPE_IMAGEREADER:
+                    if ((output.imageFormat == ImageFormat.UNKNOWN) || (output.size.width <= 0) ||
+                            (output.size.height <= 0)) {
+                        Log.w(TAG, "Unsupported client output id: " + output.outputId.id +
+                                ", skipping!");
+                        continue;
+                    }
+                    ImageReader reader = ImageReader.newInstance(output.size.width,
+                            output.size.height, output.imageFormat, output.capacity);
+                    mReaderMap.put(output.outputId.id, reader);
+                    cameraOutput = new OutputConfiguration(output.surfaceGroupId,
+                            reader.getSurface());
+                    break;
+                case CameraOutputConfig.TYPE_MULTIRES_IMAGEREADER:
+                    // TBD
+                default:
+                    throw new IllegalArgumentException("Unsupported output config type: " +
+                            output.type);
+            }
+            cameraOutput.setPhysicalCameraId(output.physicalCameraId);
+            cameraOutputs.put(output.outputId.id, cameraOutput);
+        }
+
+        ArrayList<OutputConfiguration> outputList = new ArrayList<>();
+        for (CameraOutputConfig output : outputConfigs) {
+            if (!cameraOutputs.containsKey(output.outputId.id)) {
+                // Shared surface already removed by a previous iteration
+                continue;
+            }
+            OutputConfiguration outConfig = cameraOutputs.get(output.outputId.id);
+            if ((output.surfaceSharingOutputConfigs != null) &&
+                    !output.surfaceSharingOutputConfigs.isEmpty()) {
+                outConfig.enableSurfaceSharing();
+                for (OutputConfigId outputId : output.surfaceSharingOutputConfigs) {
+                    outConfig.addSurface(cameraOutputs.get(outputId.id).getSurface());
+                    cameraOutputs.remove(outputId.id);
+                }
+            }
+            outputList.add(outConfig);
+            mSurfaceIdMap.put(outConfig.getSurface(), output.outputId.id);
+        }
+
+        SessionConfiguration sessionConfiguration = new SessionConfiguration(
+                SessionConfiguration.SESSION_REGULAR, outputList,
+                new CameraExtensionUtils.HandlerExecutor(mHandler), new SessionStateHandler());
+
+        if ((sessionConfig.sessionParameter != null) &&
+                (!sessionConfig.sessionParameter.isEmpty())) {
+            CaptureRequest.Builder requestBuilder = mCameraDevice.createCaptureRequest(
+                    sessionConfig.sessionTemplateId);
+            CaptureRequest sessionRequest = requestBuilder.build();
+            CameraMetadataNative.update(sessionRequest.getNativeMetadata(),
+                    sessionConfig.sessionParameter);
+            sessionConfiguration.setSessionParameters(sessionRequest);
+        }
+
+        mCameraDevice.createCaptureSession(sessionConfiguration);
+    }
+
+    private static ParcelCaptureResult initializeParcelable(CaptureResult result) {
+        ParcelCaptureResult ret = new ParcelCaptureResult();
+        ret.cameraId = result.getCameraId();
+        ret.results = result.getNativeMetadata();
+        ret.parent = result.getRequest();
+        ret.sequenceId = result.getSequenceId();
+        ret.frameNumber = result.getFrameNumber();
+
+        return ret;
+    }
+
+    private static ParcelTotalCaptureResult initializeParcelable(TotalCaptureResult totalResult) {
+        ParcelTotalCaptureResult ret = new ParcelTotalCaptureResult();
+        ret.logicalCameraId = totalResult.getCameraId();
+        ret.results = totalResult.getNativeMetadata();
+        ret.parent = totalResult.getRequest();
+        ret.sequenceId = totalResult.getSequenceId();
+        ret.frameNumber = totalResult.getFrameNumber();
+        ret.sessionId = totalResult.getSessionId();
+        ret.partials = new ArrayList<>(totalResult.getPartialResults().size());
+        for (CaptureResult partial : totalResult.getPartialResults()) {
+            ret.partials.add(initializeParcelable(partial));
+        }
+        Map<String, TotalCaptureResult> physicalResults =
+                totalResult.getPhysicalCameraTotalResults();
+        ret.physicalResult = new ArrayList<>(physicalResults.size());
+        for (TotalCaptureResult physicalResult : physicalResults.values()) {
+            ret.physicalResult.add(new PhysicalCaptureResultInfo(physicalResult.getCameraId(),
+                    physicalResult.getNativeMetadata()));
+        }
+
+        return ret;
+    }
+
+    private static OutputSurface initializeParcelable(Surface s) {
+        OutputSurface ret = new OutputSurface();
+        if (s != null) {
+            ret.surface = s;
+            ret.size = new android.hardware.camera2.extension.Size();
+            Size surfaceSize = SurfaceUtils.getSurfaceSize(s);
+            ret.size.width = surfaceSize.getWidth();
+            ret.size.height = surfaceSize.getHeight();
+            ret.imageFormat = SurfaceUtils.getSurfaceFormat(s);
+        } else {
+            ret.surface = null;
+            ret.size = new android.hardware.camera2.extension.Size();
+            ret.size.width = -1;
+            ret.size.height = -1;
+            ret.imageFormat = ImageFormat.UNKNOWN;
+        }
+
+        return ret;
+    }
+
+    @Override
+    public @NonNull CameraDevice getDevice() {
+        synchronized (mInterfaceLock) {
+            return mCameraDevice;
+        }
+    }
+
+    @Override
+    public int setRepeatingRequest(@NonNull CaptureRequest request, @NonNull Executor executor,
+            @NonNull ExtensionCaptureCallback listener) throws CameraAccessException {
+        int seqId = -1;
+        synchronized (mInterfaceLock) {
+            if (!mInitialized) {
+                throw new IllegalStateException("Uninitialized component");
+            }
+
+            if (mClientRepeatingRequestSurface == null) {
+                throw new IllegalArgumentException("No registered preview surface");
+            }
+
+            if (!request.containsTarget(mClientRepeatingRequestSurface) ||
+                    (request.getTargets().size() != 1)) {
+                throw new IllegalArgumentException("Invalid repeating request output target!");
+            }
+
+            try {
+                seqId = mSessionProcessor.startRepeating(new RequestCallbackHandler(request,
+                        executor, listener));
+            } catch (RemoteException e) {
+                throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
+                        "Failed to enable repeating request, extension service failed to respond!");
+            }
+        }
+
+        return seqId;
+    }
+
+    @Override
+    public int capture(@NonNull CaptureRequest request,
+            @NonNull Executor executor,
+            @NonNull ExtensionCaptureCallback listener) throws CameraAccessException {
+        int seqId = -1;
+        synchronized (mInterfaceLock) {
+            if (!mInitialized) {
+                throw new IllegalStateException("Uninitialized component");
+            }
+
+            if (mClientCaptureSurface == null) {
+                throw new IllegalArgumentException("No output surface registered for single"
+                        + " requests!");
+            }
+
+            if (!request.containsTarget(mClientCaptureSurface) ||
+                    (request.getTargets().size() != 1)) {
+                throw new IllegalArgumentException("Invalid single capture output target!");
+            }
+
+            try {
+                // This will override the extension capture stage jpeg parameters with the user set
+                // jpeg quality and rotation. This will guarantee that client configured jpeg
+                // parameters always have highest priority.
+                Integer jpegRotation = request.get(CaptureRequest.JPEG_ORIENTATION);
+                if (jpegRotation == null) {
+                    jpegRotation = CameraExtensionUtils.JPEG_DEFAULT_ROTATION;
+                }
+                Byte jpegQuality = request.get(CaptureRequest.JPEG_QUALITY);
+                if (jpegQuality == null) {
+                    jpegQuality = CameraExtensionUtils.JPEG_DEFAULT_QUALITY;
+                }
+
+                seqId = mSessionProcessor.startCapture(new RequestCallbackHandler(request,
+                        executor, listener), jpegRotation, jpegQuality);
+            } catch (RemoteException e) {
+                throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
+                        "Failed to submit capture request, extension service failed to respond!");
+            }
+        }
+
+        return seqId;
+    }
+
+    @Override
+    public void stopRepeating() throws CameraAccessException {
+        synchronized (mInterfaceLock) {
+            if (!mInitialized) {
+                throw new IllegalStateException("Uninitialized component");
+            }
+
+            mCaptureSession.stopRepeating();
+
+            try {
+                mSessionProcessor.stopRepeating();
+            } catch (RemoteException e) {
+               throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
+                       "Failed to notify about the end of repeating request, extension service"
+                               + " failed to respond!");
+            }
+        }
+    }
+
+    @Override
+    public void close() throws CameraAccessException {
+        synchronized (mInterfaceLock) {
+            if (mInitialized) {
+                try {
+                    mCaptureSession.stopRepeating();
+                    mSessionProcessor.stopRepeating();
+                    mSessionProcessor.onCaptureSessionEnd();
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failed to stop the repeating request or end the session,"
+                            + " , extension service does not respond!") ;
+                }
+                mCaptureSession.close();
+            }
+        }
+    }
+
+    public void release() {
+        synchronized (mInterfaceLock) {
+            mInitialized = false;
+            mHandlerThread.quitSafely();
+
+            if (mSessionProcessor != null) {
+                try {
+                    mSessionProcessor.deInitSession();
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failed to de-initialize session processor, extension service"
+                            + " does not respond!") ;
+                }
+                mSessionProcessor = null;
+            }
+
+            if (mExtensionClientId >= 0) {
+                CameraExtensionCharacteristics.unregisterClient(mExtensionClientId);
+            }
+
+            for (ImageReader reader : mReaderMap.values()) {
+                reader.close();
+            }
+            mReaderMap.clear();
+
+            mClientRepeatingRequestSurface = null;
+            mClientCaptureSurface = null;
+        }
+    }
+
+    private void notifyConfigurationFailure() {
+        synchronized (mInterfaceLock) {
+            if (mInitialized) {
+                return;
+            }
+        }
+
+        release();
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(
+                    () -> mCallbacks.onConfigureFailed(
+                            CameraAdvancedExtensionSessionImpl.this));
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    private class SessionStateHandler extends
+            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);
+            }
+        }
+
+        @Override
+        public void onConfigureFailed(@NonNull CameraCaptureSession session) {
+            notifyConfigurationFailure();
+        }
+
+        @Override
+        public void onConfigured(@NonNull CameraCaptureSession session) {
+            boolean status = true;
+            synchronized (mInterfaceLock) {
+                mCaptureSession = session;
+                try {
+                    mSessionProcessor.onCaptureSessionStart(mRequestProcessor);
+                    mInitialized = true;
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failed to start capture session,"
+                            + " extension service does not respond!");
+                    status = false;
+                    session.close();
+                }
+            }
+
+            if (status) {
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(
+                            () -> mCallbacks.onConfigured(CameraAdvancedExtensionSessionImpl.this));
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+            } else {
+                notifyConfigurationFailure();
+            }
+        }
+    }
+
+    private final class RequestCallbackHandler extends ICaptureCallback.Stub {
+        private final CaptureRequest mClientRequest;
+        private final Executor mClientExecutor;
+        private final ExtensionCaptureCallback mClientCallbacks;
+
+        private RequestCallbackHandler(@NonNull CaptureRequest clientRequest,
+                @NonNull Executor clientExecutor,
+                @NonNull ExtensionCaptureCallback clientCallbacks) {
+            mClientRequest = clientRequest;
+            mClientExecutor = clientExecutor;
+            mClientCallbacks = clientCallbacks;
+        }
+
+        @Override
+        public void onCaptureStarted(int captureSequenceId, long timestamp) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mClientExecutor.execute(
+                        () -> mClientCallbacks.onCaptureStarted(
+                                CameraAdvancedExtensionSessionImpl.this, mClientRequest,
+                                timestamp));
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        @Override
+        public void onCaptureProcessStarted(int captureSequenceId) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mClientExecutor.execute(
+                        () -> mClientCallbacks.onCaptureProcessStarted(
+                                CameraAdvancedExtensionSessionImpl.this, mClientRequest));
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        @Override
+        public void onCaptureFailed(int captureSequenceId) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mClientExecutor.execute(
+                        () -> mClientCallbacks.onCaptureFailed(
+                                CameraAdvancedExtensionSessionImpl.this, mClientRequest));
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        @Override
+        public void onCaptureSequenceCompleted(int captureSequenceId) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mClientExecutor.execute(
+                        () -> mClientCallbacks.onCaptureSequenceCompleted(
+                                CameraAdvancedExtensionSessionImpl.this, captureSequenceId));
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        @Override
+        public void onCaptureSequenceAborted(int captureSequenceId) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mClientExecutor.execute(
+                        () -> mClientCallbacks.onCaptureSequenceAborted(
+                                CameraAdvancedExtensionSessionImpl.this, captureSequenceId));
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    private final class CaptureCallbackHandler extends CameraCaptureSession.CaptureCallback {
+        private final IRequestCallback mCallback;
+
+        public CaptureCallbackHandler(IRequestCallback callback) {
+            mCallback = callback;
+        }
+
+        @Override
+        public void onCaptureBufferLost(CameraCaptureSession session, CaptureRequest request,
+                Surface target, long frameNumber) {
+            try {
+                if (request.getTag() instanceof Integer) {
+                    Integer requestId = (Integer) request.getTag();
+                    mCallback.onCaptureBufferLost(requestId, frameNumber,
+                            mSurfaceIdMap.get(target));
+                } else {
+                    Log.e(TAG, "Invalid capture request tag!");
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to notify lost capture buffer, extension service doesn't"
+                        + " respond!");
+            }
+        }
+
+        @Override
+        public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
+                TotalCaptureResult result) {
+            try {
+                if (request.getTag() instanceof Integer) {
+                    Integer requestId = (Integer) request.getTag();
+                    mCallback.onCaptureCompleted(requestId, initializeParcelable(result));
+                } else {
+                    Log.e(TAG, "Invalid capture request tag!");
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to notify capture result, extension service doesn't"
+                        + " respond!");
+            }
+        }
+
+        @Override
+        public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request,
+                CaptureFailure failure) {
+            try {
+                if (request.getTag() instanceof Integer) {
+                    Integer requestId = (Integer) request.getTag();
+                    android.hardware.camera2.extension.CaptureFailure captureFailure =
+                            new android.hardware.camera2.extension.CaptureFailure();
+                    captureFailure.request = request;
+                    captureFailure.reason = failure.getReason();
+                    captureFailure.errorPhysicalCameraId = failure.getPhysicalCameraId();
+                    captureFailure.frameNumber = failure.getFrameNumber();
+                    captureFailure.sequenceId = failure.getSequenceId();
+                    captureFailure.dropped = !failure.wasImageCaptured();
+                    mCallback.onCaptureFailed(requestId, captureFailure);
+                } else {
+                    Log.e(TAG, "Invalid capture request tag!");
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to notify capture failure, extension service doesn't"
+                        + " respond!");
+            }
+        }
+
+        @Override
+        public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request,
+                CaptureResult partialResult) {
+            try {
+                if (request.getTag() instanceof Integer) {
+                    Integer requestId = (Integer) request.getTag();
+                    mCallback.onCaptureProgressed(requestId, initializeParcelable(partialResult));
+                } else {
+                    Log.e(TAG, "Invalid capture request tag!");
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to notify capture partial result, extension service doesn't"
+                        + " respond!");
+            }
+        }
+
+        @Override
+        public void onCaptureSequenceAborted(CameraCaptureSession session, int sequenceId) {
+            try {
+                mCallback.onCaptureSequenceAborted(sequenceId);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to notify aborted sequence, extension service doesn't"
+                        + " respond!");
+            }
+        }
+
+        @Override
+        public void onCaptureSequenceCompleted(CameraCaptureSession session, int sequenceId,
+                long frameNumber) {
+            try {
+                mCallback.onCaptureSequenceCompleted(sequenceId, frameNumber);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to notify sequence complete, extension service doesn't"
+                        + " respond!");
+            }
+        }
+
+        @Override
+        public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request,
+                long timestamp, long frameNumber) {
+            try {
+                if (request.getTag() instanceof Integer) {
+                    Integer requestId = (Integer) request.getTag();
+                    mCallback.onCaptureStarted(requestId, frameNumber, timestamp);
+                } else {
+                    Log.e(TAG, "Invalid capture request tag!");
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to notify capture started, extension service doesn't"
+                        + " respond!");
+            }
+        }
+    }
+
+    private static final class ImageReaderHandler implements ImageReader.OnImageAvailableListener {
+        private final OutputConfigId mOutputConfigId;
+        private final IImageProcessorImpl mIImageProcessor;
+
+        private ImageReaderHandler(int outputConfigId,
+                IImageProcessorImpl iImageProcessor) {
+            mOutputConfigId = new OutputConfigId();
+            mOutputConfigId.id = outputConfigId;
+            mIImageProcessor = iImageProcessor;
+        }
+
+        @Override
+        public void onImageAvailable(ImageReader reader) {
+            if (mIImageProcessor == null) {
+                return;
+            }
+
+            Image img;
+            try {
+                img = reader.acquireNextImage();
+            } catch (IllegalStateException e) {
+                Log.e(TAG, "Failed to acquire image, too many images pending!");
+                return;
+            }
+            if (img == null) {
+                Log.e(TAG, "Invalid image!");
+                return;
+            }
+
+            try {
+                reader.detachImage(img);
+            } catch(Exception e) {
+                Log.e(TAG, "Failed to detach image");
+                img.close();
+                return;
+            }
+
+            ParcelImage parcelImage = new ParcelImage();
+            parcelImage.buffer = img.getHardwareBuffer();
+            if (img.getFenceFd() >= 0) {
+                try {
+                    parcelImage.fence = ParcelFileDescriptor.fromFd(img.getFenceFd());
+                } catch (IOException e) {
+                    Log.e(TAG,"Failed to parcel buffer fence!");
+                }
+            }
+            parcelImage.format = img.getFormat();
+            parcelImage.timestamp = img.getTimestamp();
+            parcelImage.transform = img.getTransform();
+            parcelImage.scalingMode = img.getScalingMode();
+            parcelImage.planeCount = img.getPlaneCount();
+            parcelImage.crop = img.getCropRect();
+
+            try {
+                mIImageProcessor.onNextImageAvailable(mOutputConfigId, parcelImage);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to propagate image buffer on output surface id: " +
+                        mOutputConfigId + " extension service does not respond!");
+            } finally {
+                parcelImage.buffer.close();
+                img.close();
+            }
+        }
+    }
+
+    private final class RequestProcessor extends IRequestProcessorImpl.Stub {
+        @Override
+        public void setImageProcessor(OutputConfigId outputConfigId,
+                IImageProcessorImpl imageProcessor) {
+            synchronized (mInterfaceLock) {
+                if (mReaderMap.containsKey(outputConfigId.id)) {
+                    mReaderMap.get(outputConfigId.id).setOnImageAvailableListener(
+                            new ImageReaderHandler(outputConfigId.id, imageProcessor), mHandler);
+                } else {
+                    Log.e(TAG, "ImageReader with output config id: " + outputConfigId.id +
+                            " not found!");
+                }
+            }
+        }
+
+        @Override
+        public boolean 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) {
+            synchronized (mInterfaceLock) {
+                try {
+                    CaptureCallbackHandler captureCallback = new CaptureCallbackHandler(callback);
+                    ArrayList<CaptureRequest> captureRequests = new ArrayList<>();
+                    for (Request request : requests) {
+                        captureRequests.add(initializeCaptureRequest(mCameraDevice, request,
+                                mSurfaceIdMap));
+                    }
+                    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;
+        }
+
+        @Override
+        public boolean setRepeating(Request request, IRequestCallback callback) {
+            synchronized (mInterfaceLock) {
+                try {
+                    CaptureRequest repeatingRequest = initializeCaptureRequest(mCameraDevice,
+                                request, mSurfaceIdMap);
+                    CaptureCallbackHandler captureCallback = new CaptureCallbackHandler(callback);
+                    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;
+        }
+
+        @Override
+        public void abortCaptures() {
+            synchronized (mInterfaceLock) {
+                try {
+                    mCaptureSession.abortCaptures();
+                } catch (CameraAccessException e) {
+                    Log.e(TAG, "Failed during capture abort!");
+                } catch (IllegalStateException e) {
+                    Log.e(TAG, "Capture session closed!");
+                }
+            }
+        }
+
+        @Override
+        public void stopRepeating() {
+            synchronized (mInterfaceLock) {
+                try {
+                    mCaptureSession.stopRepeating();
+                } catch (CameraAccessException e) {
+                    Log.e(TAG, "Failed during repeating capture stop!");
+                } catch (IllegalStateException e) {
+                    Log.e(TAG, "Capture session closed!");
+                }
+            }
+        }
+    }
+
+    private static CaptureRequest initializeCaptureRequest(CameraDevice cameraDevice,
+            Request request, HashMap<Surface, Integer> surfaceIdMap) throws CameraAccessException {
+        CaptureRequest.Builder builder = cameraDevice.createCaptureRequest(request.templateId);
+        for (OutputConfigId configId : request.targetOutputConfigIds) {
+            boolean found = false;
+            for (Map.Entry<Surface, Integer> entry : surfaceIdMap.entrySet()) {
+                if (entry.getValue() == configId.id) {
+                    builder.addTarget(entry.getKey());
+                    found = true;
+                    break;
+                }
+            }
+
+            if (!found) {
+                Log.e(TAG, "Surface with output id: " + configId.id +
+                        " not found among registered camera outputs!");
+            }
+        }
+
+        builder.setTag(request.requestId);
+        CaptureRequest ret = builder.build();
+        CameraMetadataNative.update(ret.getNativeMetadata(), request.parameters);
+        return ret;
+    }
+}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index b578bf8..11b137ca 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -25,6 +25,7 @@
 import android.hardware.camera2.CameraAccessException;
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraExtensionCharacteristics;
 import android.hardware.camera2.CameraExtensionSession;
 import android.hardware.camera2.CameraOfflineSession;
 import android.hardware.camera2.CameraDevice;
@@ -138,6 +139,7 @@
 
     private CameraCaptureSessionCore mCurrentSession;
     private CameraExtensionSessionImpl mCurrentExtensionSession;
+    private CameraAdvancedExtensionSessionImpl mCurrentAdvancedExtensionSession;
     private int mNextSessionId = 0;
 
     private final int mAppTargetSdkVersion;
@@ -1343,6 +1345,12 @@
                 mCurrentExtensionSession.release();
                 mCurrentExtensionSession = null;
             }
+
+            if (mCurrentAdvancedExtensionSession != null) {
+                mCurrentAdvancedExtensionSession.release();
+                mCurrentAdvancedExtensionSession = null;
+            }
+
             // Only want to fire the onClosed callback once;
             // either a normal close where the remote device is valid
             // or a close after a startup error (no remote device but in error state)
@@ -2395,9 +2403,14 @@
     public void createExtensionSession(ExtensionSessionConfiguration extensionConfiguration)
             throws CameraAccessException {
         try {
-            mCurrentExtensionSession = CameraExtensionSessionImpl.createCameraExtensionSession(this,
-                    mContext,
-                    extensionConfiguration);
+            if (CameraExtensionCharacteristics.areAdvancedExtensionsSupported()) {
+                mCurrentAdvancedExtensionSession =
+                        CameraAdvancedExtensionSessionImpl.createCameraAdvancedExtensionSession(
+                                this, mContext, extensionConfiguration);
+            } else {
+                mCurrentExtensionSession = CameraExtensionSessionImpl.createCameraExtensionSession(
+                        this, mContext, extensionConfiguration);
+            }
         } catch (RemoteException e) {
             throw new CameraAccessException(CameraAccessException.CAMERA_ERROR);
         }
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
index 936734b..3b1cb94 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
@@ -16,6 +16,9 @@
 
 package android.hardware.camera2.impl;
 
+import static android.hardware.camera2.impl.CameraExtensionUtils.JPEG_DEFAULT_QUALITY;
+import static android.hardware.camera2.impl.CameraExtensionUtils.JPEG_DEFAULT_ROTATION;
+
 import android.annotation.NonNull;
 import android.graphics.ImageFormat;
 import android.hardware.camera2.CaptureResult;
@@ -42,8 +45,6 @@
 public class CameraExtensionJpegProcessor implements ICaptureProcessorImpl {
     public final static String TAG = "CameraExtensionJpeg";
     private final static int JPEG_QUEUE_SIZE = 1;
-    private final static int JPEG_DEFAULT_QUALITY = 100;
-    private final static int JPEG_DEFAULT_ROTATION = 0;
 
     private final Handler mHandler;
     private final HandlerThread mHandlerThread;
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
index 3d771c01..5339f41 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
@@ -110,79 +110,10 @@
     // Lock to synchronize cross-thread access to device public interface
     final Object mInterfaceLock = new Object(); // access from this class and Session only!
 
-    private static class SurfaceInfo {
-        public int mWidth = 0;
-        public int mHeight = 0;
-        public int mFormat = PixelFormat.RGBA_8888;
-        public long mUsage = 0;
-    }
-
-    private static final int SUPPORTED_CAPTURE_OUTPUT_FORMATS[] = {
-        CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT,
-        ImageFormat.JPEG
-    };
-
-    private static int nativeGetSurfaceWidth(Surface surface) {
-        return SurfaceUtils.getSurfaceSize(surface).getWidth();
-    }
-
-    private static int nativeGetSurfaceHeight(Surface surface) {
-        return SurfaceUtils.getSurfaceSize(surface).getHeight();
-    }
-
     private static int nativeGetSurfaceFormat(Surface surface) {
         return SurfaceUtils.getSurfaceFormat(surface);
     }
 
-    private static Surface getBurstCaptureSurface(
-            @NonNull List<OutputConfiguration> outputConfigs,
-            @NonNull HashMap<Integer, List<Size>> supportedCaptureSizes) {
-        for (OutputConfiguration config : outputConfigs) {
-            SurfaceInfo surfaceInfo = querySurface(config.getSurface());
-            for (int supportedFormat : SUPPORTED_CAPTURE_OUTPUT_FORMATS) {
-                if (surfaceInfo.mFormat == supportedFormat) {
-                    Size captureSize = new Size(surfaceInfo.mWidth, surfaceInfo.mHeight);
-                    if (supportedCaptureSizes.containsKey(supportedFormat)) {
-                        if (supportedCaptureSizes.get(surfaceInfo.mFormat).contains(captureSize)) {
-                            return config.getSurface();
-                        } else {
-                            throw new IllegalArgumentException("Capture size not supported!");
-                        }
-                    }
-                    return config.getSurface();
-                }
-            }
-        }
-
-        return null;
-    }
-
-    private static @Nullable Surface getRepeatingRequestSurface(
-            @NonNull List<OutputConfiguration> outputConfigs,
-            @Nullable List<Size> supportedPreviewSizes) {
-        for (OutputConfiguration config : outputConfigs) {
-            SurfaceInfo surfaceInfo = querySurface(config.getSurface());
-            if ((surfaceInfo.mFormat ==
-                    CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT) ||
-                    // The default RGBA_8888 is also implicitly supported because camera will
-                    // internally override it to
-                    // 'CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT'
-                    (surfaceInfo.mFormat == PixelFormat.RGBA_8888)) {
-                Size repeatingRequestSurfaceSize = new Size(surfaceInfo.mWidth,
-                        surfaceInfo.mHeight);
-                if ((supportedPreviewSizes == null) ||
-                        (!supportedPreviewSizes.contains(repeatingRequestSurfaceSize))) {
-                    throw new IllegalArgumentException("Repeating request surface size " +
-                            repeatingRequestSurfaceSize + " not supported!");
-                }
-
-                return config.getSurface();
-            }
-        }
-
-        return null;
-    }
-
     /**
      * @hide
      */
@@ -221,22 +152,22 @@
         int suitableSurfaceCount = 0;
         List<Size> supportedPreviewSizes = extensionChars.getExtensionSupportedSizes(
                 config.getExtension(), SurfaceTexture.class);
-        Surface repeatingRequestSurface = getRepeatingRequestSurface(
+        Surface repeatingRequestSurface = CameraExtensionUtils.getRepeatingRequestSurface(
                 config.getOutputConfigurations(), supportedPreviewSizes);
         if (repeatingRequestSurface != null) {
             suitableSurfaceCount++;
         }
 
         HashMap<Integer, List<Size>> supportedCaptureSizes = new HashMap<>();
-        for (int format : SUPPORTED_CAPTURE_OUTPUT_FORMATS) {
+        for (int format : CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS) {
             List<Size> supportedSizes = extensionChars.getExtensionSupportedSizes(
                     config.getExtension(), format);
             if (supportedSizes != null) {
                 supportedCaptureSizes.put(format, supportedSizes);
             }
         }
-        Surface burstCaptureSurface = getBurstCaptureSurface(config.getOutputConfigurations(),
-                supportedCaptureSizes);
+        Surface burstCaptureSurface = CameraExtensionUtils.getBurstCaptureSurface(
+                config.getOutputConfigurations(), supportedCaptureSizes);
         if (burstCaptureSurface != null) {
             suitableSurfaceCount++;
         }
@@ -266,15 +197,15 @@
         return session;
     }
 
-    private CameraExtensionSessionImpl(@NonNull IImageCaptureExtenderImpl imageExtender,
-                                       @NonNull IPreviewExtenderImpl previewExtender,
-                                       @NonNull List<Size> previewSizes,
-                                       long extensionClientId,
-                                       @NonNull CameraDevice cameraDevice,
-                                       @Nullable Surface repeatingRequestSurface,
-                                       @Nullable Surface burstCaptureSurface,
-                                       @NonNull StateCallback callback,
-                                       @NonNull Executor executor) {
+    public CameraExtensionSessionImpl(@NonNull IImageCaptureExtenderImpl imageExtender,
+            @NonNull IPreviewExtenderImpl previewExtender,
+            @NonNull List<Size> previewSizes,
+            long extensionClientId,
+            @NonNull CameraDevice cameraDevice,
+            @Nullable Surface repeatingRequestSurface,
+            @Nullable Surface burstCaptureSurface,
+            @NonNull StateCallback callback,
+            @NonNull Executor executor) {
         mExtensionClientId = extensionClientId;
         mImageExtender = imageExtender;
         mPreviewExtender = previewExtender;
@@ -290,57 +221,17 @@
         mInitialized = false;
     }
 
-    private static @NonNull SurfaceInfo querySurface(@NonNull Surface s) {
-        ImageWriter writer = null;
-        Image img = null;
-        SurfaceInfo surfaceInfo = new SurfaceInfo();
-        int nativeFormat = nativeGetSurfaceFormat(s);
-        int dataspace = SurfaceUtils.getSurfaceDataspace(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;
-            surfaceInfo.mWidth = nativeGetSurfaceWidth(s);
-            surfaceInfo.mHeight = nativeGetSurfaceHeight(s);
-            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;
-    }
-
     private void initializeRepeatingRequestPipeline() throws RemoteException {
-        SurfaceInfo repeatingSurfaceInfo = new SurfaceInfo();
+        CameraExtensionUtils.SurfaceInfo repeatingSurfaceInfo =
+                new CameraExtensionUtils.SurfaceInfo();
         mPreviewProcessorType = mPreviewExtender.getProcessorType();
         if (mClientRepeatingRequestSurface != null) {
-            repeatingSurfaceInfo = querySurface(mClientRepeatingRequestSurface);
+            repeatingSurfaceInfo = CameraExtensionUtils.querySurface(
+                    mClientRepeatingRequestSurface);
         } else {
             // Make the intermediate surface behave as any regular 'SurfaceTexture'
-            SurfaceInfo captureSurfaceInfo = querySurface(mClientCaptureSurface);
+            CameraExtensionUtils.SurfaceInfo captureSurfaceInfo = CameraExtensionUtils.querySurface(
+                    mClientCaptureSurface);
             Size captureSize = new Size(captureSurfaceInfo.mWidth, captureSurfaceInfo.mHeight);
             Size previewSize = findSmallestAspectMatchedSize(mSupportedPreviewSizes, captureSize);
             repeatingSurfaceInfo.mWidth = previewSize.getWidth();
@@ -418,7 +309,8 @@
 
         if (mImageProcessor != null) {
             if (mClientCaptureSurface != null) {
-                SurfaceInfo surfaceInfo = querySurface(mClientCaptureSurface);
+                CameraExtensionUtils.SurfaceInfo surfaceInfo = CameraExtensionUtils.querySurface(
+                        mClientCaptureSurface);
                 if (surfaceInfo.mFormat == ImageFormat.JPEG) {
                     mImageJpegProcessor = new CameraExtensionJpegProcessor(mImageProcessor);
                     mImageProcessor = mImageJpegProcessor;
@@ -501,7 +393,7 @@
         SessionConfiguration sessionConfig = new SessionConfiguration(
                 SessionConfiguration.SESSION_REGULAR,
                 outputList,
-                new HandlerExecutor(mHandler),
+                new CameraExtensionUtils.HandlerExecutor(mHandler),
                 new SessionStateHandler());
 
         if (!sessionParamsList.isEmpty()) {
@@ -656,7 +548,8 @@
             throw new UnsupportedOperationException("Failed to create still capture burst request");
         }
 
-        return mCaptureSession.captureBurstRequests(burstRequest, new HandlerExecutor(mHandler),
+        return mCaptureSession.captureBurstRequests(burstRequest,
+                new CameraExtensionUtils.HandlerExecutor(mHandler),
                 new BurstRequestHandler(request, executor, listener, requestMap,
                         mBurstCaptureImageCallback));
     }
@@ -724,7 +617,7 @@
         CaptureRequest repeatingRequest = createRequest(mCameraDevice,
                 captureStageList, mCameraRepeatingSurface, CameraDevice.TEMPLATE_PREVIEW);
         return mCaptureSession.setSingleRepeatingRequest(repeatingRequest,
-                new HandlerExecutor(mHandler), requestHandler);
+                new CameraExtensionUtils.HandlerExecutor(mHandler), requestHandler);
     }
 
     /** @hide */
@@ -1705,23 +1598,6 @@
         return ret;
     }
 
-    private final class HandlerExecutor implements Executor {
-        private final Handler mHandler;
-
-        public HandlerExecutor(Handler handler) {
-            mHandler = handler;
-        }
-
-        @Override
-        public void execute(Runnable runCmd) {
-            try {
-                mHandler.post(runCmd);
-            } catch (RejectedExecutionException e) {
-                Log.w(TAG, "Handler thread unavailable, skipping message!");
-            }
-        }
-    }
-
     private static ParcelImage initializeParcelImage(Image img) {
         ParcelImage parcelImage = new ParcelImage();
         parcelImage.buffer = img.getHardwareBuffer();
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java b/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java
new file mode 100644
index 0000000..950d716b
--- /dev/null
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java
@@ -0,0 +1,167 @@
+/*
+ * 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.hardware.camera2.impl;
+
+import android.annotation.NonNull;
+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;
+import android.hardware.camera2.utils.SurfaceUtils;
+import android.media.Image;
+import android.media.ImageWriter;
+import android.os.Handler;
+import android.util.Log;
+import android.util.Size;
+import android.view.Surface;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+
+public final class CameraExtensionUtils {
+    private static final String TAG = "CameraExtensionUtils";
+
+    public final static int JPEG_DEFAULT_QUALITY = 100;
+    public final static int JPEG_DEFAULT_ROTATION = 0;
+
+    public static final int[] SUPPORTED_CAPTURE_OUTPUT_FORMATS = {
+            CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT,
+            ImageFormat.JPEG
+    };
+
+    public static class SurfaceInfo {
+        public int mWidth = 0;
+        public int mHeight = 0;
+        public int mFormat = PixelFormat.RGBA_8888;
+        public long mUsage = 0;
+    }
+
+    public static final class HandlerExecutor implements Executor {
+        private final Handler mHandler;
+
+        public HandlerExecutor(Handler handler) {
+            mHandler = handler;
+        }
+
+        @Override
+        public void execute(Runnable runCmd) {
+            try {
+                mHandler.post(runCmd);
+            } catch (RejectedExecutionException e) {
+                Log.w(TAG, "Handler thread unavailable, skipping message!");
+            }
+        }
+    }
+
+    public static @NonNull SurfaceInfo querySurface(@NonNull Surface s) {
+        ImageWriter writer = null;
+        Image img = null;
+        SurfaceInfo surfaceInfo = new SurfaceInfo();
+        int nativeFormat = SurfaceUtils.getSurfaceFormat(s);
+        int dataspace = SurfaceUtils.getSurfaceDataspace(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;
+    }
+
+    public static Surface getBurstCaptureSurface(
+            @NonNull List<OutputConfiguration> outputConfigs,
+            @NonNull HashMap<Integer, List<Size>> supportedCaptureSizes) {
+        for (OutputConfiguration config : outputConfigs) {
+            SurfaceInfo surfaceInfo = querySurface(config.getSurface());
+            for (int supportedFormat : SUPPORTED_CAPTURE_OUTPUT_FORMATS) {
+                if (surfaceInfo.mFormat == supportedFormat) {
+                    Size captureSize = new Size(surfaceInfo.mWidth, surfaceInfo.mHeight);
+                    if (supportedCaptureSizes.containsKey(supportedFormat)) {
+                        if (supportedCaptureSizes.get(surfaceInfo.mFormat).contains(captureSize)) {
+                            return config.getSurface();
+                        } else {
+                            throw new IllegalArgumentException("Capture size not supported!");
+                        }
+                    }
+                    return config.getSurface();
+                }
+            }
+        }
+
+        return null;
+    }
+
+    public static @Nullable Surface getRepeatingRequestSurface(
+            @NonNull List<OutputConfiguration> outputConfigs,
+            @Nullable List<Size> supportedPreviewSizes) {
+        for (OutputConfiguration config : outputConfigs) {
+            SurfaceInfo surfaceInfo = querySurface(config.getSurface());
+            if ((surfaceInfo.mFormat ==
+                    CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT) ||
+                    // The default RGBA_8888 is also implicitly supported because camera will
+                    // internally override it to
+                    // 'CameraExtensionCharacteristics.NON_PROCESSING_INPUT_FORMAT'
+                    (surfaceInfo.mFormat == PixelFormat.RGBA_8888)) {
+                Size repeatingRequestSurfaceSize = new Size(surfaceInfo.mWidth,
+                        surfaceInfo.mHeight);
+                if ((supportedPreviewSizes == null) ||
+                        (!supportedPreviewSizes.contains(repeatingRequestSurfaceSize))) {
+                    throw new IllegalArgumentException("Repeating request surface size " +
+                            repeatingRequestSurfaceSize + " not supported!");
+                }
+
+                return config.getSurface();
+            }
+        }
+
+        return null;
+    }
+}
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 9a27a99..55c90ce 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -69,8 +69,6 @@
     private static final int MSG_SET_FEATURE_COMPLETED = 107;
     private static final int MSG_CHALLENGE_GENERATED = 108;
     private static final int MSG_FACE_DETECTED = 109;
-    private static final int MSG_CHALLENGE_INTERRUPTED = 110;
-    private static final int MSG_CHALLENGE_INTERRUPT_FINISHED = 111;
     private static final int MSG_AUTHENTICATION_FRAME = 112;
     private static final int MSG_ENROLLMENT_FRAME = 113;
 
@@ -102,8 +100,8 @@
 
         @Override // binder call
         public void onAuthenticationSucceeded(Face face, int userId, boolean isStrongBiometric) {
-            mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, isStrongBiometric ? 1 : 0,
-                    face).sendToTarget();
+            mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId,
+                    isStrongBiometric ? 1 : 0, face).sendToTarget();
         }
 
         @Override // binder call
@@ -142,22 +140,12 @@
         }
 
         @Override
-        public void onChallengeGenerated(int sensorId, long challenge) {
-            mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, sensorId, 0, challenge)
+        public void onChallengeGenerated(int sensorId, int userId, long challenge) {
+            mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, sensorId, userId, challenge)
                     .sendToTarget();
         }
 
         @Override
-        public void onChallengeInterrupted(int sensorId) {
-            mHandler.obtainMessage(MSG_CHALLENGE_INTERRUPTED, sensorId).sendToTarget();
-        }
-
-        @Override
-        public void onChallengeInterruptFinished(int sensorId) {
-            mHandler.obtainMessage(MSG_CHALLENGE_INTERRUPT_FINISHED, sensorId).sendToTarget();
-        }
-
-        @Override
         public void onAuthenticationFrame(FaceAuthenticationFrame frame) {
             mHandler.obtainMessage(MSG_AUTHENTICATION_FRAME, frame).sendToTarget();
         }
@@ -434,16 +422,14 @@
      *
      * @see com.android.server.locksettings.LockSettingsService
      *
-     * TODO(b/171335732): should take userId
-     *
      * @hide
      */
     @RequiresPermission(MANAGE_BIOMETRIC)
-    public void generateChallenge(int sensorId, GenerateChallengeCallback callback) {
+    public void generateChallenge(int sensorId, int userId, GenerateChallengeCallback callback) {
         if (mService != null) {
             try {
                 mGenerateChallengeCallback = callback;
-                mService.generateChallenge(mToken, sensorId, 0 /* userId */, mServiceReceiver,
+                mService.generateChallenge(mToken, sensorId, userId, mServiceReceiver,
                         mContext.getOpPackageName());
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
@@ -452,12 +438,13 @@
     }
 
     /**
-     * Same as {@link #generateChallenge(int, GenerateChallengeCallback)}, but assumes the first
-     * enumerated sensor.
+     * Same as {@link #generateChallenge(int, int, GenerateChallengeCallback)}, but assumes the
+     * first enumerated sensor.
+     *
      * @hide
      */
     @RequiresPermission(MANAGE_BIOMETRIC)
-    public void generateChallenge(GenerateChallengeCallback callback) {
+    public void generateChallenge(int userId, GenerateChallengeCallback callback) {
         final List<FaceSensorPropertiesInternal> faceSensorProperties =
                 getSensorPropertiesInternal();
         if (faceSensorProperties.isEmpty()) {
@@ -466,7 +453,7 @@
         }
 
         final int sensorId = faceSensorProperties.get(0).sensorId;
-        generateChallenge(sensorId, callback);
+        generateChallenge(sensorId, userId, callback);
     }
 
     /**
@@ -1120,25 +1107,16 @@
     }
 
     /**
-     * Callback structure provided to {@link #generateChallenge(int, GenerateChallengeCallback)}.
+     * Callback structure provided to {@link #generateChallenge(int, int,
+     * GenerateChallengeCallback)}.
+     *
      * @hide
      */
     public interface GenerateChallengeCallback {
         /**
          * Invoked when a challenge has been generated.
          */
-        void onGenerateChallengeResult(int sensorId, long challenge);
-
-        /**
-         * Invoked if the challenge has not been revoked and a subsequent caller/owner invokes
-         * {@link #generateChallenge(int, GenerateChallengeCallback)}, but
-         */
-        default void onChallengeInterrupted(int sensorId) {}
-
-        /**
-         * Invoked when the interrupting client has finished (e.g. revoked its challenge).
-         */
-        default void onChallengeInterruptFinished(int sensorId) {}
+        void onGenerateChallengeResult(int sensorId, int userId, long challenge);
     }
 
     private class OnEnrollCancelListener implements OnCancelListener {
@@ -1212,18 +1190,13 @@
                     args.recycle();
                     break;
                 case MSG_CHALLENGE_GENERATED:
-                    sendChallengeGenerated(msg.arg1 /* sensorId */, (long) msg.obj /* challenge */);
+                    sendChallengeGenerated(msg.arg1 /* sensorId */, msg.arg2 /* userId */,
+                            (long) msg.obj /* challenge */);
                     break;
                 case MSG_FACE_DETECTED:
                     sendFaceDetected(msg.arg1 /* sensorId */, msg.arg2 /* userId */,
                             (boolean) msg.obj /* isStrongBiometric */);
                     break;
-                case MSG_CHALLENGE_INTERRUPTED:
-                    sendChallengeInterrupted((int) msg.obj /* sensorId */);
-                    break;
-                case MSG_CHALLENGE_INTERRUPT_FINISHED:
-                    sendChallengeInterruptFinished((int) msg.obj /* sensorId */);
-                    break;
                 case MSG_AUTHENTICATION_FRAME:
                     sendAuthenticationFrame((FaceAuthenticationFrame) msg.obj /* frame */);
                     break;
@@ -1251,11 +1224,11 @@
         mGetFeatureCallback.onCompleted(success, features, featureState);
     }
 
-    private void sendChallengeGenerated(int sensorId, long challenge) {
+    private void sendChallengeGenerated(int sensorId, int userId, long challenge) {
         if (mGenerateChallengeCallback == null) {
             return;
         }
-        mGenerateChallengeCallback.onGenerateChallengeResult(sensorId, challenge);
+        mGenerateChallengeCallback.onGenerateChallengeResult(sensorId, userId, challenge);
     }
 
     private void sendFaceDetected(int sensorId, int userId, boolean isStrongBiometric) {
@@ -1266,22 +1239,6 @@
         mFaceDetectionCallback.onFaceDetected(sensorId, userId, isStrongBiometric);
     }
 
-    private void sendChallengeInterrupted(int sensorId) {
-        if (mGenerateChallengeCallback == null) {
-            Slog.e(TAG, "sendChallengeInterrupted, callback null");
-            return;
-        }
-        mGenerateChallengeCallback.onChallengeInterrupted(sensorId);
-    }
-
-    private void sendChallengeInterruptFinished(int sensorId) {
-        if (mGenerateChallengeCallback == null) {
-            Slog.e(TAG, "sendChallengeInterruptFinished, callback null");
-            return;
-        }
-        mGenerateChallengeCallback.onChallengeInterruptFinished(sensorId);
-    }
-
     private void sendRemovedResult(Face face, int remaining) {
         if (mRemovalCallback == null) {
             return;
diff --git a/core/java/android/hardware/face/FaceServiceReceiver.java b/core/java/android/hardware/face/FaceServiceReceiver.java
index 9e62ca5..9e78592 100644
--- a/core/java/android/hardware/face/FaceServiceReceiver.java
+++ b/core/java/android/hardware/face/FaceServiceReceiver.java
@@ -72,17 +72,8 @@
     }
 
     @Override
-    public void onChallengeGenerated(int sensorId, long challenge) throws RemoteException {
-
-    }
-
-    @Override
-    public void onChallengeInterrupted(int sensorId) throws RemoteException {
-
-    }
-
-    @Override
-    public void onChallengeInterruptFinished(int sensorId) throws RemoteException {
+    public void onChallengeGenerated(int sensorId, int userId, long challenge)
+            throws RemoteException {
 
     }
 
diff --git a/core/java/android/hardware/face/IFaceServiceReceiver.aidl b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
index 0ccb395..c4d9bf2 100644
--- a/core/java/android/hardware/face/IFaceServiceReceiver.aidl
+++ b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
@@ -33,9 +33,7 @@
     void onRemoved(in Face face, int remaining);
     void onFeatureSet(boolean success, int feature);
     void onFeatureGet(boolean success, in int[] features, in boolean[] featureState);
-    void onChallengeGenerated(int sensorId, long challenge);
-    void onChallengeInterrupted(int sensorId);
-    void onChallengeInterruptFinished(int sensorId);
+    void onChallengeGenerated(int sensorId, int userId, long challenge);
     void onAuthenticationFrame(in FaceAuthenticationFrame frame);
     void onEnrollmentFrame(in FaceEnrollFrame frame);
 }
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index b52955d..8aeb5cd 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -475,10 +475,13 @@
     }
 
     /**
+     * Callbacks for generate challenge operations.
+     *
      * @hide
      */
     public interface GenerateChallengeCallback {
-        void onChallengeGenerated(int sensorId, long challenge);
+        /** Called when a challenged has been generated. */
+        void onChallengeGenerated(int sensorId, int userId, long challenge);
     }
 
     /**
@@ -1124,7 +1127,8 @@
                     sendRemovedResult((Fingerprint) msg.obj, msg.arg1 /* remaining */);
                     break;
                 case MSG_CHALLENGE_GENERATED:
-                    sendChallengeGenerated(msg.arg1 /* sensorId */, (long) msg.obj /* challenge */);
+                    sendChallengeGenerated(msg.arg1 /* sensorId */, msg.arg2 /* userId */,
+                            (long) msg.obj /* challenge */);
                     break;
                 case MSG_FINGERPRINT_DETECTED:
                     sendFingerprintDetected(msg.arg1 /* sensorId */, msg.arg2 /* userId */,
@@ -1233,12 +1237,12 @@
         }
     }
 
-    private void sendChallengeGenerated(int sensorId, long challenge) {
+    private void sendChallengeGenerated(int sensorId, int userId, long challenge) {
         if (mGenerateChallengeCallback == null) {
             Slog.e(TAG, "sendChallengeGenerated, callback null");
             return;
         }
-        mGenerateChallengeCallback.onChallengeGenerated(sensorId, challenge);
+        mGenerateChallengeCallback.onChallengeGenerated(sensorId, userId, challenge);
     }
 
     private void sendFingerprintDetected(int sensorId, int userId, boolean isStrongBiometric) {
@@ -1454,8 +1458,8 @@
         }
 
         @Override // binder call
-        public void onChallengeGenerated(int sensorId, long challenge) {
-            mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, sensorId, 0, challenge)
+        public void onChallengeGenerated(int sensorId, int userId, long challenge) {
+            mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, sensorId, userId, challenge)
                     .sendToTarget();
         }
 
diff --git a/core/java/android/hardware/fingerprint/FingerprintServiceReceiver.java b/core/java/android/hardware/fingerprint/FingerprintServiceReceiver.java
index 798e87b..a9779b5 100644
--- a/core/java/android/hardware/fingerprint/FingerprintServiceReceiver.java
+++ b/core/java/android/hardware/fingerprint/FingerprintServiceReceiver.java
@@ -61,7 +61,8 @@
     }
 
     @Override
-    public void onChallengeGenerated(int sensorId, long challenge) throws RemoteException {
+    public void onChallengeGenerated(int sensorId, int userId, long challenge)
+            throws RemoteException {
 
     }
 
diff --git a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
index 1bd284d..9cea1fe 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
@@ -29,7 +29,7 @@
     void onAuthenticationFailed();
     void onError(int error, int vendorCode);
     void onRemoved(in Fingerprint fp, int remaining);
-    void onChallengeGenerated(int sensorId, long challenge);
+    void onChallengeGenerated(int sensorId, int userId, long challenge);
     void onUdfpsPointerDown(int sensorId);
     void onUdfpsPointerUp(int sensorId);
 }
diff --git a/core/java/android/hardware/fingerprint/IUdfpsHbmListener.aidl b/core/java/android/hardware/fingerprint/IUdfpsHbmListener.aidl
index 1551e07..f4d22da 100644
--- a/core/java/android/hardware/fingerprint/IUdfpsHbmListener.aidl
+++ b/core/java/android/hardware/fingerprint/IUdfpsHbmListener.aidl
@@ -35,7 +35,7 @@
      * UdfpsController will call this method when the HBM is enabled.
      *
      * @param hbmType The type of HBM that was enabled. See
-     *        {@link com.android.systemui.biometrics.HbmTypes}.
+     *        {@link com.android.systemui.biometrics.UdfpsHbmTypes}.
      * @param displayId The displayId for which the HBM is enabled. See
      *        {@link android.view.Display#getDisplayId()}.
      */
@@ -45,7 +45,7 @@
      * UdfpsController will call this method when the HBM is disabled.
      *
      * @param hbmType The type of HBM that was disabled. See
-     *        {@link com.android.systemui.biometrics.HbmTypes}.
+     *        {@link com.android.systemui.biometrics.UdfpsHbmTypes}.
      * @param displayId The displayId for which the HBM is disabled. See
      *        {@link android.view.Display#getDisplayId()}.
      */
diff --git a/core/java/android/hardware/hdmi/OWNERS b/core/java/android/hardware/hdmi/OWNERS
index 16c15e3..60d43fd 100644
--- a/core/java/android/hardware/hdmi/OWNERS
+++ b/core/java/android/hardware/hdmi/OWNERS
@@ -4,3 +4,4 @@
 
 marvinramin@google.com
 nchalko@google.com
+lcnathalie@google.com
diff --git a/core/java/android/net/NetworkStateSnapshot.java b/core/java/android/net/NetworkStateSnapshot.java
index 9df861a..3915634 100644
--- a/core/java/android/net/NetworkStateSnapshot.java
+++ b/core/java/android/net/NetworkStateSnapshot.java
@@ -104,7 +104,10 @@
         return mSubscriberId;
     }
 
-    /** Get the legacy type of the network associated with this snapshot. */
+    /**
+     * Get the legacy type of the network associated with this snapshot.
+     * @return the legacy network type. See {@code ConnectivityManager#TYPE_*}.
+     */
     public int getLegacyType() {
         return mLegacyType;
     }
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index c97d1f8..4c8297a 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -3931,7 +3931,6 @@
                 getStartClockTime(),
                 whichBatteryScreenOffRealtime / 1000, whichBatteryScreenOffUptime / 1000,
                 getEstimatedBatteryCapacity(),
-                getLearnedBatteryCapacity(),
                 getMinLearnedBatteryCapacity(),
                 getMaxLearnedBatteryCapacity(),
                 screenDozeTime / 1000);
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 38ed3fb..900659b 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -309,7 +309,7 @@
          * booted, but it may increase when the hardware manufacturer provides an OTA update.
          * <p>
          * Possible non-zero values are defined in {@link Build.VERSION_CODES} starting with
-         * {@link Build.VERSION_CODES#S}.
+         * {@link Build.VERSION_CODES#R}.
          */
         public static final int MEDIA_PERFORMANCE_CLASS =
                 DeviceProperties.media_performance_class().orElse(0);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 3bdc546..b879082 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9921,6 +9921,18 @@
                 "accessibility_floating_menu_opacity";
 
         /**
+         * Prompts the user to the Accessibility button is replaced with the floating menu.
+         * <ul>
+         *    <li> 0 = disabled </li>
+         *    <li> 1 = enabled </li>
+         * </ul>
+         *
+         * @hide
+         */
+        public static final String ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT =
+                "accessibility_floating_menu_migration_tooltip_prompt";
+
+        /**
          * Whether the Adaptive connectivity option is enabled.
          *
          * @hide
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 53bde36..324d1ab 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -21,6 +21,7 @@
 import static android.graphics.Matrix.MSKEW_X;
 import static android.graphics.Matrix.MSKEW_Y;
 import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
 import android.annotation.FloatRange;
 import android.annotation.NonNull;
@@ -1218,7 +1219,9 @@
             mIWallpaperEngine.mDisplayManager.registerDisplayListener(mDisplayListener,
                     mCaller.getHandler());
             mDisplay = mIWallpaperEngine.mDisplay;
-            mDisplayContext = createDisplayContext(mDisplay);
+            // Use window context of TYPE_WALLPAPER so client can access UI resources correctly.
+            mDisplayContext = createDisplayContext(mDisplay)
+                    .createWindowContext(TYPE_WALLPAPER, null /* options */);
             mDisplayState = mDisplay.getState();
 
             if (DEBUG) Log.v(TAG, "onCreate(): " + this);
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/AttachedSurfaceControl.java
similarity index 70%
rename from core/java/android/view/ViewRoot.java
rename to core/java/android/view/AttachedSurfaceControl.java
index 3c75598..bcc5b56 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/AttachedSurfaceControl.java
@@ -22,16 +22,19 @@
 /**
  * Provides an interface to the root-Surface of a View Hierarchy or Window. This
  * is used in combination with the {@link android.view.SurfaceControl} API to enable
- * attaching app created SurfaceControl to the ViewRoot's surface hierarchy, and enable
- * SurfaceTransactions to be performed in sync with the ViewRoot drawing. This object
- * is obtained from {@link android.view.View#getViewRoot} and
- * {@link android.view.Window#getViewRoot}. It must be used from the UI thread of
+ * attaching app created SurfaceControl to the SurfaceControl hierarchy used
+ * by the app, and enable SurfaceTransactions to be performed in sync with the
+ * View hierarchy drawing.
+ *
+ * This object is obtained from {@link android.view.View#getRootSurfaceControl} and
+ * {@link android.view.Window#getRootSurfaceControl}. It must be used from the UI thread of
  * the object it was obtained from.
  */
 @UiThread
-public interface ViewRoot {
+public interface AttachedSurfaceControl {
     /**
-     * Create a transaction which will reparent {@param child} to the ViewRoot. See
+     * Create a transaction which will reparent {@param child} to the View hierarchy
+     * root SurfaceControl. See
      * {@link SurfaceControl.Transaction#reparent}. This transacton must be applied
      * or merged in to another transaction by the caller, otherwise it will have
      * no effect.
@@ -42,9 +45,9 @@
     @Nullable SurfaceControl.Transaction buildReparentTransaction(@NonNull SurfaceControl child);
 
     /**
-     * Consume the passed in transaction, and request the ViewRoot to apply it with the
-     * next draw. This transaction will be merged with the buffer transaction from the ViewRoot
-     * and they will show up on-screen atomically synced.
+     * Consume the passed in transaction, and request the View hierarchy to apply it atomically
+     * with the next draw. This transaction will be merged with the buffer transaction from the
+     * ViewRoot and they will show up on-screen atomically synced.
      *
      * This will not cause a draw to be scheduled, and if there are no other changes
      * to the View hierarchy you may need to call {@link android.view.View#invalidate}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c1c892c..acc0fc1 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -29599,7 +29599,7 @@
             return mScrollCaptureInternal;
         }
 
-        ViewRoot getViewRoot() {
+        AttachedSurfaceControl getRootSurfaceControl() {
             return mViewRootImpl;
         }
 
@@ -30876,7 +30876,9 @@
 
     /**
      * Called when the content from {@link View#onCreateViewTranslationRequest} had been translated
-     * by the TranslationService.
+     * by the TranslationService. The {@link ViewTranslationResponse} should be saved here so that
+     * the {@link ViewTranslationResponse} can be used to display the translation when the system
+     * calls {@link ViewTranslationCallback#onShowTranslation}.
      *
      * <p> The default implementation will set the ViewTranslationResponse that can be get from
      * {@link View#getViewTranslationResponse}. </p>
@@ -30929,7 +30931,7 @@
      * information, e.g. source spec, target spec.
      * @param requests fill in with {@link ViewTranslationRequest}s for translation purpose.
      */
-    public void dispatchRequestTranslation(@NonNull Map<AutofillId, long[]> viewIds,
+    public void dispatchCreateViewTranslationRequest(@NonNull Map<AutofillId, long[]> viewIds,
             @NonNull @DataFormat int[] supportedFormats,
             @NonNull TranslationCapability capability,
             @NonNull List<ViewTranslationRequest> requests) {
@@ -31043,17 +31045,17 @@
     }
 
     /**
-     * @return The {@link android.view.ViewRoot} interface for this View. This will only
-     * return a non-null value when called between {@link #onAttachedToWindow} and
-     * {@link #onDetachedFromWindow}.
-     *
-     * The ViewRoot itself is not a View, it is just the interface to the windowing-system
-     * object that contains the entire view hierarchy. For the root View of a given hierarchy
-     * see {@link #getRootView}.
+     * The AttachedSurfaceControl itself is not a View, it is just the interface to the
+     * windowing-system object that contains the entire view hierarchy.
+     * For the root View of a given hierarchy see {@link #getRootView}.
+
+     * @return The {@link android.view.AttachedSurfaceControl} interface for this View.
+     * This will only return a non-null value when called between {@link #onAttachedToWindow}
+     * and {@link #onDetachedFromWindow}.
      */
-    public @Nullable ViewRoot getViewRoot() {
+    public @Nullable AttachedSurfaceControl getRootSurfaceControl() {
         if (mAttachInfo != null) {
-            return mAttachInfo.getViewRoot();
+          return mAttachInfo.getRootSurfaceControl();
         }
         return null;
     }
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index a02281b..4647f47 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -9275,21 +9275,23 @@
     /**
      * {@inheritDoc}
      *
-     * The implementation calls {@link #dispatchRequestTranslation} for all the child views.
+     * The implementation calls {@link #dispatchCreateViewTranslationRequest} for all the child
+     * views.
      */
     @Override
-    public void dispatchRequestTranslation(@NonNull Map<AutofillId, long[]> viewIds,
+    public void dispatchCreateViewTranslationRequest(@NonNull Map<AutofillId, long[]> viewIds,
             @NonNull @DataFormat int[] supportedFormats,
             @Nullable TranslationCapability capability,
             @NonNull List<ViewTranslationRequest> requests) {
-        super.dispatchRequestTranslation(viewIds, supportedFormats, capability, requests);
+        super.dispatchCreateViewTranslationRequest(viewIds, supportedFormats, capability, requests);
         final int childCount = getChildCount();
         if (childCount == 0) {
             return;
         }
         for (int i = 0; i < childCount; ++i) {
             final View child = getChildAt(i);
-            child.dispatchRequestTranslation(viewIds, supportedFormats, capability, requests);
+            child.dispatchCreateViewTranslationRequest(viewIds, supportedFormats, capability,
+                    requests);
         }
     }
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a06f193..4d1c666 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -224,7 +224,8 @@
  */
 @SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
 public final class ViewRootImpl implements ViewParent,
-        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks, ViewRoot {
+        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,
+        AttachedSurfaceControl {
     private static final String TAG = "ViewRootImpl";
     private static final boolean DBG = false;
     private static final boolean LOCAL_LOGV = false;
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 52a09701..5fb4e70 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -2722,11 +2722,12 @@
 
     /**
      * This will be null before a content view is added, e.g. via
-     * {@link #setContentView} or {@link #addContentView}.
+     * {@link #setContentView} or {@link #addContentView}. See
+     * {@link android.view.View#getRootSurfaceControl}.
      *
-     * @return The {@link android.view.ViewRoot} interface for this Window
+     * @return The {@link android.view.AttachedSurfaceControl} interface for this Window
      */
-    public @Nullable ViewRoot getViewRoot() {
+    public @Nullable AttachedSurfaceControl getRootSurfaceControl() {
         return null;
     }
 }
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index 8a2b629..e0526f8 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -466,8 +466,8 @@
                 // traverse the hierarchy to collect ViewTranslationRequests
                 for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
                     View rootView = roots.get(rootNum).getView();
-                    rootView.dispatchRequestTranslation(viewIds, supportedFormats, capability,
-                            requests);
+                    rootView.dispatchCreateViewTranslationRequest(viewIds, supportedFormats,
+                            capability, requests);
                 }
                 mWorkerHandler.sendMessage(PooledLambda.obtainMessage(
                         UiTranslationController::sendTranslationRequest,
diff --git a/core/java/android/view/translation/UiTranslationSpec.java b/core/java/android/view/translation/UiTranslationSpec.java
index b43dbce..3d1ebfd 100644
--- a/core/java/android/view/translation/UiTranslationSpec.java
+++ b/core/java/android/view/translation/UiTranslationSpec.java
@@ -20,6 +20,7 @@
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.widget.TextView;
 
 import com.android.internal.util.DataClass;
 
@@ -35,36 +36,38 @@
 public final class UiTranslationSpec implements Parcelable {
 
     /**
-     * Whether the original content of the view should be directly modified to include padding that
-     * makes it the same size as the translated content. Defaults to {@code false}.
+     * Whether the original content of the view should be made to appear as if it is the same size
+     * as the translated content. Defaults to {@code false}.
      * <p>
-     * For {@link android.widget.TextView}, the system does not directly modify the original text,
-     * rather changes the displayed content using a
-     * {@link android.text.method.TransformationMethod}.
-     * This can cause issues in apps that do not account for TransformationMethods. For example, an
-     * app using DynamicLayout may use the calculated text offsets to operate on the original text,
-     * but this can be problematic when the layout was calculated on translated text with a
-     * different length.
+     * For {@link TextView}, the system does not directly modify the original text, rather
+     * changes the displayed content using a {@link android.text.method.TransformationMethod}. This
+     * can cause issues in apps that do not account for length-changing TransformationMethods. For
+     * example, an app using DynamicLayout may use the calculated line-offsets to operate on the
+     * original text, but this can cause crashes when the layout was calculated on translated text
+     * with a different length.
      * <p>
-     * If this is {@code true}, for a TextView the default implementation will append spaces to the
-     * text to make the length the same as the translated text.
+     * If this is {@code true}, for a TextView the default implementation appends spaces to the
+     * result of {@link TextView#getText()} to make the length the same as the translated text.
+     * <p>
+     * This only affects apps with target SDK R or lower.
      */
     private boolean mShouldPadContentForCompat = false;
 
     /**
-     * Whether the original content of the view should be directly modified to include padding that
-     * makes it the same size as the translated content.
+     * Whether the original content of the view should be made to appear as if it is the same size
+     * as the translated content.
      * <p>
-     * For {@link android.widget.TextView}, the system does not directly modify the original text,
-     * rather changes the displayed content using a
-     * {@link android.text.method.TransformationMethod}.
-     * This can cause issues in apps that do not account for TransformationMethods. For example, an
-     * app using DynamicLayout may use the calculated text offsets to operate on the original text,
-     * but this can be problematic when the layout was calculated on translated text with a
-     * different length.
+     * For {@link TextView}, the system does not directly modify the original text, rather
+     * changes the displayed content using a {@link android.text.method.TransformationMethod}. This
+     * can cause issues in apps that do not account for length-changing TransformationMethods. For
+     * example, an app using DynamicLayout may use the calculated line-offsets to operate on the
+     * original text, but this can cause crashes when the layout was calculated on translated text
+     * with a different length.
      * <p>
-     * If this is {@code true}, for a TextView the default implementation will append spaces to the
-     * text to make the length the same as the translated text.
+     * If this is {@code true}, for a TextView the default implementation appends spaces to the
+     * result of {@link TextView#getText()} to make the length the same as the translated text.
+     * <p>
+     * This only affects apps with target SDK R or lower.
      */
     public boolean shouldPadContentForCompat() {
         return mShouldPadContentForCompat;
@@ -190,19 +193,20 @@
         }
 
         /**
-         * Whether the original content of the view should be directly modified to include padding that
-         * makes it the same size as the translated content. Defaults to {@code false}.
+         * Whether the original content of the view should be made to appear as if it is the same size
+         * as the translated content. Defaults to {@code false}.
          * <p>
-         * For {@link android.widget.TextView}, the system does not directly modify the original text,
-         * rather changes the displayed content using a
-         * {@link android.text.method.TransformationMethod}.
-         * This can cause issues in apps that do not account for TransformationMethods. For example, an
-         * app using DynamicLayout may use the calculated text offsets to operate on the original text,
-         * but this can be problematic when the layout was calculated on translated text with a
-         * different length.
+         * For {@link TextView}, the system does not directly modify the original text, rather
+         * changes the displayed content using a {@link android.text.method.TransformationMethod}. This
+         * can cause issues in apps that do not account for length-changing TransformationMethods. For
+         * example, an app using DynamicLayout may use the calculated line-offsets to operate on the
+         * original text, but this can cause crashes when the layout was calculated on translated text
+         * with a different length.
          * <p>
-         * If this is {@code true}, for a TextView the default implementation will append spaces to the
-         * text to make the length the same as the translated text.
+         * If this is {@code true}, for a TextView the default implementation appends spaces to the
+         * result of {@link TextView#getText()} to make the length the same as the translated text.
+         * <p>
+         * This only affects apps with target SDK R or lower.
          */
         @DataClass.Generated.Member
         public @NonNull Builder setShouldPadContentForCompat(boolean value) {
@@ -234,7 +238,7 @@
     }
 
     @DataClass.Generated(
-            time = 1619034161701L,
+            time = 1620790033058L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/view/translation/UiTranslationSpec.java",
             inputSignatures = "private  boolean mShouldPadContentForCompat\npublic  boolean shouldPadContentForCompat()\nclass UiTranslationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genToString=true)")
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index a1d4822..0bbaac0f 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -60,11 +60,13 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeProvider;
+import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
 import android.view.inspector.InspectableProperty;
 import android.view.textclassifier.TextClassifier;
+import android.view.translation.TranslationCapability;
 import android.view.translation.TranslationSpec.DataFormat;
 import android.view.translation.ViewTranslationRequest;
 import android.view.translation.ViewTranslationResponse;
@@ -2869,6 +2871,16 @@
     }
 
     @Override
+    public void dispatchCreateViewTranslationRequest(@NonNull Map<AutofillId, long[]> viewIds,
+            @NonNull @DataFormat int[] supportedFormats,
+            @Nullable TranslationCapability capability,
+            @NonNull List<ViewTranslationRequest> requests) {
+        super.dispatchCreateViewTranslationRequest(viewIds, supportedFormats, capability, requests);
+        mProvider.getViewDelegate().dispatchCreateViewTranslationRequest(viewIds, supportedFormats,
+                capability, requests);
+    }
+
+    @Override
     public void onVirtualViewTranslationResponses(
             @NonNull LongSparseArray<ViewTranslationResponse> response) {
         mProvider.getViewDelegate().onVirtualViewTranslationResponses(response);
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index 8d996ee..f9f823b 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -45,10 +45,12 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeProvider;
+import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
 import android.view.textclassifier.TextClassifier;
+import android.view.translation.TranslationCapability;
 import android.view.translation.TranslationSpec.DataFormat;
 import android.view.translation.ViewTranslationRequest;
 import android.view.translation.ViewTranslationResponse;
@@ -59,6 +61,7 @@
 
 import java.io.BufferedWriter;
 import java.io.File;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
@@ -377,6 +380,14 @@
                         LongSparseArray<ViewTranslationResponse> response) {
         }
 
+        default void dispatchCreateViewTranslationRequest(
+                @NonNull @SuppressWarnings("unused") Map<AutofillId, long[]> viewIds,
+                @NonNull @SuppressWarnings("unused") @DataFormat int[] supportedFormats,
+                @Nullable @SuppressWarnings("unused") TranslationCapability capability,
+                @NonNull @SuppressWarnings("unused") List<ViewTranslationRequest> requests) {
+
+        }
+
         public AccessibilityNodeProvider getAccessibilityNodeProvider();
 
         public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info);
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 49a3e29..14fd4c2 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -86,6 +86,11 @@
     public static final ComponentName REDUCE_BRIGHT_COLORS_COMPONENT_NAME =
             new ComponentName("com.android.server.accessibility", "ReduceBrightColors");
 
+    // The component name for the sub setting of Accessibility button in Accessibility settings
+    public static final ComponentName ACCESSIBILITY_BUTTON_COMPONENT_NAME =
+            new ComponentName("com.android.server.accessibility", "AccessibilityButton");
+
+
     private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
             .setUsage(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY)
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index a6dc4e0..0ea299d 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -70,12 +70,12 @@
 import android.transition.TransitionManager;
 import android.transition.TransitionSet;
 import android.util.AndroidRuntimeException;
+import android.view.AttachedSurfaceControl;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseArray;
 import android.util.TypedValue;
-import android.view.ViewRoot;
 import android.view.ContextThemeWrapper;
 import android.view.CrossWindowBlurListeners;
 import android.view.Gravity;
@@ -3984,7 +3984,7 @@
     }
 
     @Override
-    public ViewRoot getViewRoot() {
+    public AttachedSurfaceControl getRootSurfaceControl() {
         return getViewRootImplOrNull();
     }
 }
diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java
index 42fb3f4..72b57ab 100644
--- a/core/java/com/android/internal/power/MeasuredEnergyStats.java
+++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java
@@ -33,6 +33,7 @@
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
 
 /**
  * Tracks the measured charge consumption of various subsystems according to their
@@ -96,8 +97,10 @@
      * supportedStandardBuckets must be of size {@link #NUMBER_STANDARD_POWER_BUCKETS}.
      * numCustomBuckets >= 0 is the number of (non-standard) custom power buckets on the device.
      */
-    public MeasuredEnergyStats(boolean[] supportedStandardBuckets, String[] customBucketNames) {
-        final int numTotalBuckets = NUMBER_STANDARD_POWER_BUCKETS + customBucketNames.length;
+    public MeasuredEnergyStats(@NonNull boolean[] supportedStandardBuckets,
+            @Nullable String[] customBucketNames) {
+        mCustomBucketNames = customBucketNames == null ? new String[0] : customBucketNames;
+        final int numTotalBuckets = NUMBER_STANDARD_POWER_BUCKETS + mCustomBucketNames.length;
         mAccumulatedChargeMicroCoulomb = new long[numTotalBuckets];
         // Initialize to all zeros where supported, otherwise POWER_DATA_UNAVAILABLE.
         // All custom buckets are, by definition, supported, so their values stay at 0.
@@ -106,7 +109,6 @@
                 mAccumulatedChargeMicroCoulomb[stdBucket] = POWER_DATA_UNAVAILABLE;
             }
         }
-        mCustomBucketNames = customBucketNames;
     }
 
     /**
@@ -431,14 +433,22 @@
 
     /** Check if the supported power buckets are precisely those given. */
     public boolean isSupportEqualTo(
-            @NonNull boolean[] queriedStandardBuckets, String[] customBucketNames) {
+            @NonNull boolean[] queriedStandardBuckets, @Nullable String[] customBucketNames) {
+        if (customBucketNames == null) {
+            //In practice customBucketNames should never be null, but sanitize it just to be sure.
+            customBucketNames = new String[0];
+        }
 
         final int numBuckets = getNumberOfIndices();
-        // TODO(b/178504428): Detect whether custom buckets have changed qualitatively, not just
-        //                    quantitatively, and treat as mismatch if so.
-        if (numBuckets != NUMBER_STANDARD_POWER_BUCKETS + customBucketNames.length) {
+        final int numCustomBuckets = customBucketNames == null ? 0 : customBucketNames.length;
+        if (numBuckets != NUMBER_STANDARD_POWER_BUCKETS + numCustomBuckets) {
             return false;
         }
+
+        if (!Arrays.equals(mCustomBucketNames, customBucketNames)) {
+            return false;
+        }
+
         for (int stdBucket = 0; stdBucket < NUMBER_STANDARD_POWER_BUCKETS; stdBucket++) {
             if (isStandardBucketSupported(stdBucket) != queriedStandardBuckets[stdBucket]) {
                 return false;
diff --git a/core/java/com/android/internal/widget/EmphasizedNotificationButton.java b/core/java/com/android/internal/widget/EmphasizedNotificationButton.java
index 058a921..4460e4a 100644
--- a/core/java/com/android/internal/widget/EmphasizedNotificationButton.java
+++ b/core/java/com/android/internal/widget/EmphasizedNotificationButton.java
@@ -27,9 +27,7 @@
 import android.graphics.drawable.RippleDrawable;
 import android.util.AttributeSet;
 import android.view.RemotableViewMethod;
-import android.view.ViewGroup;
 import android.widget.Button;
-import android.widget.LinearLayout;
 import android.widget.RemoteViews;
 
 import com.android.internal.R;
@@ -42,8 +40,6 @@
 @RemoteViews.RemoteView
 public class EmphasizedNotificationButton extends Button {
     private final RippleDrawable mRipple;
-    private final int mStrokeWidth;
-    private final int mStrokeColor;
     private boolean mPriority;
 
     public EmphasizedNotificationButton(Context context) {
@@ -63,9 +59,6 @@
         super(context, attrs, defStyleAttr, defStyleRes);
         DrawableWrapper background = (DrawableWrapper) getBackground().mutate();
         mRipple = (RippleDrawable) background.getDrawable();
-        mStrokeWidth = getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.emphasized_button_stroke_width);
-        mStrokeColor = getContext().getColor(com.android.internal.R.color.material_grey_300);
         mRipple.mutate();
     }
 
@@ -82,13 +75,6 @@
         invalidate();
     }
 
-    @RemotableViewMethod
-    public void setHasStroke(boolean hasStroke) {
-        GradientDrawable inner = (GradientDrawable) mRipple.getDrawable(0);
-        inner.setStroke(hasStroke ? mStrokeWidth : 0, mStrokeColor);
-        invalidate();
-    }
-
     /**
      * Sets an image icon which will have its size constrained and will be set to the same color as
      * the text. Must be called after {@link #setTextColor(int)} for the latter to work.
@@ -121,18 +107,13 @@
     }
 
     /**
-     * Changes the LayoutParams.width to WRAP_CONTENT, with the argument representing if this view
-     * is a priority over its peers (which affects weight).
+     * Sets whether this view is a priority over its peers (which affects width).
+     * Specifically, this is used by {@link NotificationActionListLayout} to give this view width
+     * priority ahead of user-defined buttons when allocating horizontal space.
      */
     @RemotableViewMethod
-    public void setWrapModePriority(boolean priority) {
+    public void setIsPriority(boolean priority) {
         mPriority = priority;
-        ViewGroup.LayoutParams layoutParams = getLayoutParams();
-        layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
-        if (layoutParams instanceof LinearLayout.LayoutParams) {
-            ((LinearLayout.LayoutParams) layoutParams).weight = 0;
-        }
-        setLayoutParams(layoutParams);
     }
 
     /**
diff --git a/core/java/com/android/internal/widget/NotificationActionListLayout.java b/core/java/com/android/internal/widget/NotificationActionListLayout.java
index 8e6497b..a4d6a60 100644
--- a/core/java/com/android/internal/widget/NotificationActionListLayout.java
+++ b/core/java/com/android/internal/widget/NotificationActionListLayout.java
@@ -17,11 +17,11 @@
 package com.android.internal.widget;
 
 import android.annotation.DimenRes;
+import android.app.Notification;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.drawable.RippleDrawable;
 import android.util.AttributeSet;
-import android.util.Pair;
 import android.view.Gravity;
 import android.view.RemotableViewMethod;
 import android.view.View;
@@ -43,10 +43,9 @@
     private final int mGravity;
     private int mTotalWidth = 0;
     private int mExtraStartPadding = 0;
-    private ArrayList<Pair<Integer, TextView>> mMeasureOrderTextViews = new ArrayList<>();
+    private ArrayList<TextViewInfo> mMeasureOrderTextViews = new ArrayList<>();
     private ArrayList<View> mMeasureOrderOther = new ArrayList<>();
     private boolean mEmphasizedMode;
-    private boolean mPrioritizedWrapMode;
     private int mDefaultPaddingBottom;
     private int mDefaultPaddingTop;
     private int mEmphasizedHeight;
@@ -70,16 +69,18 @@
         ta.recycle();
     }
 
+    private static boolean isPriority(View actionView) {
+        return actionView instanceof EmphasizedNotificationButton
+                && ((EmphasizedNotificationButton) actionView).isPriority();
+    }
+
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        if (mEmphasizedMode && !mPrioritizedWrapMode) {
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-            return;
-        }
         final int N = getChildCount();
         int textViews = 0;
         int otherViews = 0;
         int notGoneChildren = 0;
+        int priorityChildren = 0;
 
         for (int i = 0; i < N; i++) {
             View c = getChildAt(i);
@@ -90,6 +91,9 @@
             }
             if (c.getVisibility() != GONE) {
                 notGoneChildren++;
+                if (isPriority(c)) {
+                    priorityChildren++;
+                }
             }
         }
 
@@ -103,9 +107,9 @@
         if (!needRebuild) {
             final int size = mMeasureOrderTextViews.size();
             for (int i = 0; i < size; i++) {
-                Pair<Integer, TextView> pair = mMeasureOrderTextViews.get(i);
-                if (pair.first != pair.second.getText().length()) {
+                if (mMeasureOrderTextViews.get(i).needsRebuild()) {
                     needRebuild = true;
+                    break;
                 }
             }
         }
@@ -122,14 +126,19 @@
         int usedWidth = 0;
 
         int measuredChildren = 0;
+        int measuredPriorityChildren = 0;
         for (int i = 0; i < N; i++) {
             // Measure shortest children first. To avoid measuring twice, we approximate by looking
             // at the text length.
-            View c;
+            final boolean isPriority;
+            final View c;
             if (i < otherSize) {
                 c = mMeasureOrderOther.get(i);
+                isPriority = false;
             } else {
-                c = mMeasureOrderTextViews.get(i - otherSize).second;
+                TextViewInfo info = mMeasureOrderTextViews.get(i - otherSize);
+                c = info.mTextView;
+                isPriority = info.mIsPriority;
             }
             if (c.getVisibility() == GONE) {
                 continue;
@@ -143,7 +152,18 @@
                 // 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 maxWidthForChild = availableWidth / (notGoneChildren - measuredChildren);
+                int unmeasuredChildren = notGoneChildren - measuredChildren;
+                int maxWidthForChild = availableWidth / unmeasuredChildren;
+                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 unmeasuredOtherChildren = unmeasuredChildren - unmeasuredPriorityChildren;
+                    int widthReservedForOtherChildren = innerWidth * unmeasuredOtherChildren
+                            / (Notification.MAX_ACTION_BUTTONS + 1);
+                    int widthAvailableForPriority = availableWidth - widthReservedForOtherChildren;
+                    maxWidthForChild = widthAvailableForPriority / unmeasuredPriorityChildren;
+                }
 
                 usedWidthForChild = innerWidth - maxWidthForChild;
             }
@@ -153,6 +173,9 @@
 
             usedWidth += c.getMeasuredWidth() + lp.rightMargin + lp.leftMargin;
             measuredChildren++;
+            if (isPriority) {
+                measuredPriorityChildren++;
+            }
         }
 
         int collapsibleIndent = mCollapsibleIndentDimen == 0 ? 0
@@ -175,13 +198,8 @@
         final int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
             View c = getChildAt(i);
-            if (c instanceof EmphasizedNotificationButton
-                    && ((EmphasizedNotificationButton) c).isPriority()) {
-                // add with 0 length to ensure that this view is measured before others.
-                mMeasureOrderTextViews.add(Pair.create(0, (TextView) c));
-            } else if (c instanceof TextView && ((TextView) c).getText().length() > 0) {
-                mMeasureOrderTextViews.add(Pair.create(((TextView) c).getText().length(),
-                        (TextView)c));
+            if (c instanceof TextView && ((TextView) c).getText().length() > 0) {
+                mMeasureOrderTextViews.add(new TextViewInfo((TextView) c));
             } else {
                 mMeasureOrderOther.add(c);
             }
@@ -213,10 +231,6 @@
 
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        if (mEmphasizedMode && !mPrioritizedWrapMode) {
-            super.onLayout(changed, left, top, right, bottom);
-            return;
-        }
         final boolean isLayoutRtl = isLayoutRtl();
         final int paddingTop = mPaddingTop;
         final boolean centerAligned = (mGravity & Gravity.CENTER_HORIZONTAL) != 0;
@@ -293,16 +307,6 @@
     }
 
     /**
-     * When used with emphasizedMode, changes the button sizing behavior to prioritize certain
-     * buttons (which are system generated) to not scrunch, and leave the remaining space for
-     * custom actions.
-     */
-    @RemotableViewMethod
-    public void setPrioritizedWrapMode(boolean prioritizedWrapMode) {
-        mPrioritizedWrapMode = prioritizedWrapMode;
-    }
-
-    /**
      * When buttons are in wrap mode, this is a padding that will be applied at the start of the
      * layout of the actions, but only when those actions would fit with the entire padding
      * visible.  Otherwise, this padding will be omitted entirely.
@@ -353,6 +357,28 @@
         return 0;
     }
 
-    public static final Comparator<Pair<Integer, TextView>> MEASURE_ORDER_COMPARATOR
-            = (a, b) -> a.first.compareTo(b.first);
+    public static final Comparator<TextViewInfo> MEASURE_ORDER_COMPARATOR = (a, b) -> {
+        int priorityComparison = -Boolean.compare(a.mIsPriority, b.mIsPriority);
+        return priorityComparison != 0
+                ? priorityComparison
+                : Integer.compare(a.mTextLength, b.mTextLength);
+    };
+
+    private static final class TextViewInfo {
+        final boolean mIsPriority;
+        final int mTextLength;
+        final TextView mTextView;
+
+        TextViewInfo(TextView textView) {
+            this.mIsPriority = isPriority(textView);
+            this.mTextLength = textView.getText().length();
+            this.mTextView = textView;
+        }
+
+        boolean needsRebuild() {
+            return mTextView.getText().length() != mTextLength
+                    || isPriority(mTextView) != mIsPriority;
+        }
+    }
+
 }
diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp
index 315ca2e..5c9999d 100644
--- a/core/jni/android_hardware_camera2_CameraMetadata.cpp
+++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp
@@ -270,7 +270,28 @@
             metadataSrc->unlock(metaBuffer);
             return;
         }
-        metadataDst->update(entry.tag, entry.data.u8, entry.count);
+        switch (entry.type) {
+            case TYPE_BYTE:
+                metadataDst->update(entry.tag, entry.data.u8, entry.count);
+                break;
+            case TYPE_INT32:
+                metadataDst->update(entry.tag, entry.data.i32, entry.count);
+                break;
+            case TYPE_FLOAT:
+                metadataDst->update(entry.tag, entry.data.f, entry.count);
+                break;
+            case TYPE_INT64:
+                metadataDst->update(entry.tag, entry.data.i64, entry.count);
+                break;
+            case TYPE_DOUBLE:
+                metadataDst->update(entry.tag, entry.data.d, entry.count);
+                break;
+            case TYPE_RATIONAL:
+                metadataDst->update(entry.tag, entry.data.r, entry.count);
+                break;
+            default:
+                ALOGE("%s: Unsupported tag type: %d!", __func__, entry.type);
+        }
     }
     metadataSrc->unlock(metaBuffer);
 }
diff --git a/core/res/Android.bp b/core/res/Android.bp
index b988b5a..6063062 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -100,8 +100,7 @@
         + "RES_DIR=$$(dirname $$(dirname $${INPUTS[0]})) && "
         + "mkdir -p $$RES_DIR/values && "
         + "cp $${INPUTS[1]} $$RES_DIR/values && "
-        + "$(location soong_zip) -o $(out) -C $$RES_DIR -D $$RES_DIR && "
-        + "cp $(out) ."
+        + "$(location soong_zip) -o $(out) -C $$RES_DIR -D $$RES_DIR"
 }
 
 android_app {
diff --git a/core/res/res/drawable/btn_notification_emphasized.xml b/core/res/res/drawable/btn_notification_emphasized.xml
index ad68054..63707ab 100644
--- a/core/res/res/drawable/btn_notification_emphasized.xml
+++ b/core/res/res/drawable/btn_notification_emphasized.xml
@@ -24,13 +24,11 @@
         <item>
             <shape android:shape="rectangle">
                 <corners android:radius="@dimen/notification_action_button_radius" />
-                <padding android:left="@dimen/button_padding_horizontal_material"
+                <padding android:left="12dp"
                          android:top="@dimen/button_padding_vertical_material"
-                         android:right="@dimen/button_padding_horizontal_material"
+                         android:right="12dp"
                          android:bottom="@dimen/button_padding_vertical_material" />
                 <solid android:color="@color/white" />
-                <stroke android:width="@dimen/emphasized_button_stroke_width"
-                        android:color="@color/material_grey_300"/>
             </shape>
         </item>
     </ripple>
diff --git a/core/res/res/layout/notification_material_action_emphasized.xml b/core/res/res/layout/notification_material_action_emphasized.xml
index cd1f1ab..ea84185 100644
--- a/core/res/res/layout/notification_material_action_emphasized.xml
+++ b/core/res/res/layout/notification_material_action_emphasized.xml
@@ -18,10 +18,9 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     style="@style/NotificationEmphasizedAction"
     android:id="@+id/action0"
-    android:layout_width="match_parent"
+    android:layout_width="wrap_content"
     android:layout_height="match_parent"
     android:layout_marginStart="12dp"
-    android:layout_weight="1"
     android:drawablePadding="6dp"
     android:gravity="center"
     android:textColor="@color/notification_default_color"
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 4f90a17..d334306 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -224,14 +224,14 @@
     <!-- The margin on the end of the top-line content views (accommodates the expander) -->
     <dimen name="notification_heading_margin_end">56dp</dimen>
 
-    <!-- The height of the notification action list -->
+    <!-- The total height of the notification action list -->
     <dimen name="notification_action_list_height">60dp</dimen>
 
     <!-- The margin of the notification action list at the top -->
     <dimen name="notification_action_list_margin_top">0dp</dimen>
 
-    <!-- The height of the notification action list -->
-    <dimen name="notification_action_emphasized_height">48dp</dimen>
+    <!-- The visual height of the emphasized notification action -->
+    <dimen name="notification_action_emphasized_height">36dp</dimen>
 
     <!-- The padding of the actions in non-conversation layout. For conversations, the analogous
          value is calculated in ConversationLayout#updateActionListPadding() -->
@@ -252,7 +252,7 @@
     <dimen name="notification_actions_icon_drawable_size">20dp</dimen>
 
     <!-- The corner radius if the emphasized action buttons in a notification -->
-    <dimen name="notification_action_button_radius">8dp</dimen>
+    <dimen name="notification_action_button_radius">18dp</dimen>
 
     <!-- Size of the stroke with for the emphasized notification button style -->
     <dimen name="emphasized_button_stroke_width">1dp</dimen>
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
index 9e88373..a613e77 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
@@ -16,18 +16,12 @@
 
 package android.app.appsearch;
 
-
 import static com.google.common.truth.Truth.assertThat;
 
 import android.os.Bundle;
 
-import com.google.common.collect.ImmutableList;
-
 import org.junit.Test;
 
-import java.util.List;
-import java.util.Map;
-
 public class SearchSpecTest {
 
     @Test
@@ -63,32 +57,4 @@
         assertThat(bundle.getInt(SearchSpec.RANKING_STRATEGY_FIELD))
                 .isEqualTo(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE);
     }
-
-    @Test
-    public void testGetProjectionTypePropertyMasks() {
-        SearchSpec searchSpec =
-                new SearchSpec.Builder()
-                        .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
-                        .addProjection("TypeA", ImmutableList.of("field1", "field2.subfield2"))
-                        .addProjection("TypeB", ImmutableList.of("field7"))
-                        .addProjection("TypeC", ImmutableList.of())
-                        .build();
-
-        Map<String, List<String>> typePropertyPathMap = searchSpec.getProjections();
-        assertThat(typePropertyPathMap.keySet()).containsExactly("TypeA", "TypeB", "TypeC");
-        assertThat(typePropertyPathMap.get("TypeA")).containsExactly("field1", "field2.subfield2");
-        assertThat(typePropertyPathMap.get("TypeB")).containsExactly("field7");
-        assertThat(typePropertyPathMap.get("TypeC")).isEmpty();
-    }
-
-    @Test
-    public void testGetRankingStrategy() {
-        SearchSpec searchSpec =
-                new SearchSpec.Builder()
-                        .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
-                        .setRankingStrategy(SearchSpec.RANKING_STRATEGY_RELEVANCE_SCORE)
-                        .build();
-        assertThat(searchSpec.getRankingStrategy())
-                .isEqualTo(SearchSpec.RANKING_STRATEGY_RELEVANCE_SCORE);
-    }
 }
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
index 46dbe0f..e7ee9dc 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
@@ -166,7 +166,7 @@
 
         Configuration newConfig = new Configuration();
         newConfig.orientation = Configuration.ORIENTATION_LANDSCAPE;
-        mResourcesManager.applyConfigurationToResourcesLocked(newConfig, null);
+        mResourcesManager.applyConfigurationToResources(newConfig, null);
 
         final Configuration expectedConfig = new Configuration();
         expectedConfig.setToDefaults();
diff --git a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
index 2d894f5..a70033b 100644
--- a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
@@ -499,4 +499,84 @@
         assertEquals(exp, MeasuredEnergyStats.getDisplayPowerBucket(Display.STATE_DOZE));
         assertEquals(exp, MeasuredEnergyStats.getDisplayPowerBucket(Display.STATE_DOZE_SUSPEND));
     }
+
+    /** Test MeasuredEnergyStats#isSupportEqualTo */
+    @Test
+    public void testIsSupportEqualTo() {
+        final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS];
+        Arrays.fill(supportedStandardBuckets, true);
+        final String[] customBucketNames = {"A", "B"};
+
+        final MeasuredEnergyStats stats =
+                new MeasuredEnergyStats(supportedStandardBuckets.clone(),
+                        customBucketNames.clone());
+
+        assertTrue(
+                "All standard and custom bucket supports match",
+                stats.isSupportEqualTo(supportedStandardBuckets, customBucketNames));
+
+        boolean[] differentSupportedStandardBuckets = supportedStandardBuckets.clone();
+        differentSupportedStandardBuckets[0] = !differentSupportedStandardBuckets[0];
+        assertFalse(
+                "Standard bucket support mismatch",
+                stats.isSupportEqualTo(differentSupportedStandardBuckets, customBucketNames));
+
+        assertFalse(
+                "Custom bucket support mismatch",
+                stats.isSupportEqualTo(supportedStandardBuckets, new String[]{"C", "B"}));
+
+        assertFalse(
+                "Fewer custom buckets supported",
+                stats.isSupportEqualTo(supportedStandardBuckets, new String[]{"A"}));
+
+        assertFalse(
+                "More custom bucket supported",
+                stats.isSupportEqualTo(supportedStandardBuckets, new String[]{"A", "B", "C"}));
+
+        assertFalse(
+                "Custom bucket support order changed",
+                stats.isSupportEqualTo(supportedStandardBuckets, new String[]{"B", "A"}));
+    }
+
+    /** Test MeasuredEnergyStats#isSupportEqualTo when holding a null array of custom buckets */
+    @Test
+    public void testIsSupportEqualTo_nullCustomBuckets() {
+        final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS];
+
+        final MeasuredEnergyStats stats =
+                new MeasuredEnergyStats(supportedStandardBuckets.clone(), null);
+
+        assertTrue(
+                "Null custom bucket name lists should match",
+                stats.isSupportEqualTo(supportedStandardBuckets, null));
+
+        assertTrue(
+                "Null and empty custom buckets should match",
+                stats.isSupportEqualTo(supportedStandardBuckets, new String[0]));
+
+        assertFalse(
+                "Null custom buckets should not match populated list",
+                stats.isSupportEqualTo(supportedStandardBuckets, new String[]{"A", "B"}));
+    }
+
+    /** Test MeasuredEnergyStats#isSupportEqualTo when holding an empty array of custom buckets */
+    @Test
+    public void testIsSupportEqualTo_emptyCustomBuckets() {
+        final boolean[] supportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS];
+
+        final MeasuredEnergyStats stats =
+                new MeasuredEnergyStats(supportedStandardBuckets.clone(), new String[0]);
+
+        assertTrue(
+                "Empty custom buckets should match",
+                stats.isSupportEqualTo(supportedStandardBuckets, new String[0]));
+
+        assertTrue(
+                "Empty and null custom buckets should match",
+                stats.isSupportEqualTo(supportedStandardBuckets, null));
+
+        assertFalse(
+                "Empty custom buckets should not match populated list",
+                stats.isSupportEqualTo(supportedStandardBuckets, new String[]{"A", "B"}));
+    }
 }
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index e222570..51bf6d53 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -255,6 +255,12 @@
             | 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)
@@ -570,10 +576,13 @@
      * accelerated drawing always acts as if {@link #FILTER_BITMAP_FLAG} is set.
      * 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}.</p>
+     * 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>
      */
     public Paint() {
-        this(0);
+        this(DEFAULT_PAINT_FLAGS);
     }
 
     /**
@@ -618,7 +627,7 @@
     /** Restores the paint to its default settings. */
     public void reset() {
         nReset(mNativePaint);
-        setFlags(HIDDEN_DEFAULT_PAINT_FLAGS);
+        setFlags(HIDDEN_DEFAULT_PAINT_FLAGS | DEFAULT_PAINT_FLAGS);
 
         // TODO: Turning off hinting has undesirable side effects, we need to
         //       revisit hinting once we add support for subpixel positioning
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 fd6f0ad9..1f098c2 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
@@ -66,7 +66,7 @@
     // For example, an icon with the foreground 108*108 opaque pixels and it's background
     // also 108*108 pixels, then do not enlarge this icon if only need to show foreground icon.
     private static final float ENLARGE_FOREGROUND_ICON_THRESHOLD = (72f * 72f) / (108f * 108f);
-    private static final float NO_BACKGROUND_SCALE = 1.3f;
+    private static final float NO_BACKGROUND_SCALE = 192f / 160;
     private final Context mContext;
     private final IconProvider mIconProvider;
 
@@ -283,7 +283,8 @@
             } else {
                 final float iconScale = (float) mIconSize / (float) mDefaultIconSize;
                 final int densityDpi = mContext.getResources().getDisplayMetrics().densityDpi;
-                final int scaledIconDpi = (int) (0.5f + iconScale * densityDpi);
+                final int scaledIconDpi =
+                        (int) (0.5f + iconScale * densityDpi * NO_BACKGROUND_SCALE);
                 iconDrawable = mIconProvider.getIcon(mActivityInfo, scaledIconDpi);
                 if (iconDrawable == null) {
                     iconDrawable = mContext.getPackageManager().getDefaultActivityIcon();
@@ -356,7 +357,7 @@
                     Slog.d(TAG, "makeSplashScreenContentView: choose fg icon");
                 }
                 // Reference AdaptiveIcon description, outer is 108 and inner is 72, so we
-                // should enlarge the size 108/72 if we only draw adaptiveIcon's foreground.
+                // scale by 192/160 if we only draw adaptiveIcon's foreground.
                 final float noBgScale =
                         foreIconTester.nonTransparentRatio() < ENLARGE_FOREGROUND_ICON_THRESHOLD
                                 ? NO_BACKGROUND_SCALE : 1f;
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 7d061fb..320106a 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -226,9 +226,9 @@
       // staging aliases can only be defined by the framework package (which is not a shared
       // library), the compile-time package id of the framework is the same across all packages
       // that compile against the framework.
-      for (const auto& package : iter2->packages_) {
+      for (const auto& package : iter->packages_) {
         for (const auto& entry : package.loaded_package_->GetAliasResourceIdMap()) {
-          iter->dynamic_ref_table->addAlias(entry.first, entry.second);
+          iter2->dynamic_ref_table->addAlias(entry.first, entry.second);
         }
       }
     }
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index 100bfb6..4658035 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -137,9 +137,10 @@
     int mCaptureSequence = 0;
 
     // Multi frame serialization stream and writer used when serializing more than one frame.
+    std::unique_ptr<SkSharingSerialContext> mSerialContext;  // Must be declared before any other
+                                                             // serializing member
     std::unique_ptr<SkFILEWStream> mOpenMultiPicStream;
     sk_sp<SkDocument> mMultiPic;
-    std::unique_ptr<SkSharingSerialContext> mSerialContext;
 
     /**
      * mRecorder holds the current picture recorder when serializing in either SingleFrameSKP or
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index bba2207..bae1ab5 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -467,11 +467,11 @@
     mRenderThread.pushBackFrameCallback(this);
 }
 
-void CanvasContext::draw() {
+nsecs_t CanvasContext::draw() {
     if (auto grContext = getGrContext()) {
         if (grContext->abandoned()) {
             LOG_ALWAYS_FATAL("GrContext is abandoned/device lost at start of CanvasContext::draw");
-            return;
+            return 0;
         }
     }
     SkRect dirty;
@@ -486,7 +486,7 @@
             std::invoke(func, mFrameNumber);
         }
         mFrameCompleteCallbacks.clear();
-        return;
+        return 0;
     }
 
     ScopedActiveContext activeContext(this);
@@ -616,6 +616,7 @@
     }
 
     mRenderThread.cacheManager().onFrameCompleted();
+    return mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration);
 }
 
 void CanvasContext::reportMetricsWithPresentTime() {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index af1ebb2..4f8e4ca 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -127,7 +127,8 @@
     void setColorMode(ColorMode mode);
     bool makeCurrent();
     void prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued, RenderNode* target);
-    void draw();
+    // Returns the DequeueBufferDuration.
+    nsecs_t draw();
     void destroy();
 
     // IFrameCallback, Choreographer-driven frame callback entry point
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index cb92aa1..8448b87 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -18,6 +18,7 @@
 
 #include <utils/Log.h>
 #include <utils/TraceUtils.h>
+#include <algorithm>
 
 #include "../DeferredLayerUpdater.h"
 #include "../DisplayList.h"
@@ -91,6 +92,7 @@
 void DrawFrameTask::run() {
     const int64_t vsyncId = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameTimelineVsyncId)];
     ATRACE_FORMAT("DrawFrames %" PRId64, vsyncId);
+    nsecs_t syncDelayDuration = systemTime(SYSTEM_TIME_MONOTONIC) - mSyncQueued;
 
     bool canUnblockUiThread;
     bool canDrawThisFrame;
@@ -124,8 +126,9 @@
                 [callback, frameNr = context->getFrameNumber()]() { callback(frameNr); });
     }
 
+    nsecs_t dequeueBufferDuration = 0;
     if (CC_LIKELY(canDrawThisFrame)) {
-        context->draw();
+        dequeueBufferDuration = context->draw();
     } else {
         // wait on fences so tasks don't overlap next frame
         context->waitOnFences();
@@ -149,10 +152,14 @@
             mUpdateTargetWorkDuration(targetWorkDuration);
         }
         int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime;
-        if (frameDuration > kSanityCheckLowerBound && frameDuration < kSanityCheckUpperBound) {
-            mReportActualWorkDuration(frameDuration);
+        int64_t actualDuration = frameDuration -
+                                 (std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
+                                 dequeueBufferDuration;
+        if (actualDuration > kSanityCheckLowerBound && actualDuration < kSanityCheckUpperBound) {
+            mReportActualWorkDuration(actualDuration);
         }
     }
+    mLastDequeueBufferDuration = dequeueBufferDuration;
 }
 
 bool DrawFrameTask::syncFrameState(TreeInfo& info) {
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index 3bb574a..2455ea8 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -110,6 +110,7 @@
     std::function<void(int64_t)> mFrameCallback;
     std::function<void(int64_t)> mFrameCompleteCallback;
 
+    nsecs_t mLastDequeueBufferDuration = 0;
     nsecs_t mLastTargetWorkDuration = 0;
     std::function<void(int64_t)> mUpdateTargetWorkDuration;
     std::function<void(int64_t)> mReportActualWorkDuration;
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 39b7922..5f6fc17 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -131,6 +131,9 @@
     public static final int SAMPLE_RATE_HZ_MIN = native_getMinSampleRate();
     private static native int native_getMinSampleRate();
 
+    /** @hide */
+    public static final int FCC_24 = 24; // fixed channel count 24; do not change.
+
     // Expose only the getter method publicly so we can change it in the future
     private static final int NUM_STREAM_TYPES = 12;
 
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index b2b2f8e..23d9532 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1720,9 +1720,10 @@
                 mChannelCount = 0;
                 break; // channel index configuration only
             }
-            if (!isMultichannelConfigSupported(channelConfig)) {
-                // input channel configuration features unsupported channels
-                throw new IllegalArgumentException("Unsupported channel configuration.");
+            if (!isMultichannelConfigSupported(channelConfig, audioFormat)) {
+                throw new IllegalArgumentException(
+                        "Unsupported channel mask configuration " + channelConfig
+                        + " for encoding " + audioFormat);
             }
             mChannelMask = channelConfig;
             mChannelCount = AudioFormat.channelCountFromOutChannelMask(channelConfig);
@@ -1730,13 +1731,17 @@
         // check the channel index configuration (if present)
         mChannelIndexMask = channelIndexMask;
         if (mChannelIndexMask != 0) {
-            // restrictive: indexMask could allow up to AUDIO_CHANNEL_BITS_LOG2
-            final int indexMask = (1 << AudioSystem.OUT_CHANNEL_COUNT_MAX) - 1;
-            if ((channelIndexMask & ~indexMask) != 0) {
-                throw new IllegalArgumentException("Unsupported channel index configuration "
-                        + channelIndexMask);
+            // As of S, we accept up to 24 channel index mask.
+            final int fullIndexMask = (1 << AudioSystem.FCC_24) - 1;
+            final int channelIndexCount = Integer.bitCount(channelIndexMask);
+            final boolean accepted = (channelIndexMask & ~fullIndexMask) == 0
+                    && (!AudioFormat.isEncodingLinearFrames(audioFormat)  // compressed OK
+                            || channelIndexCount <= AudioSystem.OUT_CHANNEL_COUNT_MAX); // PCM
+            if (!accepted) {
+                throw new IllegalArgumentException(
+                        "Unsupported channel index mask configuration " + channelIndexMask
+                        + " for encoding " + audioFormat);
             }
-            int channelIndexCount = Integer.bitCount(channelIndexMask);
             if (mChannelCount == 0) {
                  mChannelCount = channelIndexCount;
             } else if (mChannelCount != channelIndexCount) {
@@ -1789,16 +1794,19 @@
      * @param channelConfig the mask to validate
      * @return false if the AudioTrack can't be used with such a mask
      */
-    private static boolean isMultichannelConfigSupported(int channelConfig) {
+    private static boolean isMultichannelConfigSupported(int channelConfig, int encoding) {
         // check for unsupported channels
         if ((channelConfig & SUPPORTED_OUT_CHANNELS) != channelConfig) {
             loge("Channel configuration features unsupported channels");
             return false;
         }
         final int channelCount = AudioFormat.channelCountFromOutChannelMask(channelConfig);
-        if (channelCount > AudioSystem.OUT_CHANNEL_COUNT_MAX) {
-            loge("Channel configuration contains too many channels " +
-                    channelCount + ">" + AudioSystem.OUT_CHANNEL_COUNT_MAX);
+        final int channelCountLimit = AudioFormat.isEncodingLinearFrames(encoding)
+                ? AudioSystem.OUT_CHANNEL_COUNT_MAX  // PCM limited to OUT_CHANNEL_COUNT_MAX
+                : AudioSystem.FCC_24;                // Compressed limited to 24 channels
+        if (channelCount > channelCountLimit) {
+            loge("Channel configuration contains too many channels for encoding "
+                    + encoding + "(" + channelCount + " > " + channelCountLimit + ")");
             return false;
         }
         // check for unsupported multichannel combinations:
@@ -2310,7 +2318,7 @@
             channelCount = 2;
             break;
         default:
-            if (!isMultichannelConfigSupported(channelConfig)) {
+            if (!isMultichannelConfigSupported(channelConfig, audioFormat)) {
                 loge("getMinBufferSize(): Invalid channel configuration.");
                 return ERROR_BAD_VALUE;
             } else {
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index a8c2ea5..93a5444 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -454,28 +454,7 @@
         sourceRect.makeInvalid();
     }
     transaction->setBufferCrop(surfaceControl, sourceRect);
-
-    int destW = destRect.width();
-    int destH = destRect.height();
-    if (destRect.left < 0) {
-        destRect.left = 0;
-        destRect.right = destW;
-    }
-    if (destRect.top < 0) {
-        destRect.top = 0;
-        destRect.bottom = destH;
-    }
-
-    if (!sourceRect.isEmpty()) {
-        float sx = destW / static_cast<float>(sourceRect.width());
-        float sy = destH / static_cast<float>(sourceRect.height());
-        transaction->setPosition(surfaceControl, destRect.left - (sourceRect.left * sx),
-                                 destRect.top - (sourceRect.top * sy));
-        transaction->setMatrix(surfaceControl, sx, 0, 0, sy);
-    } else {
-        transaction->setPosition(surfaceControl, destRect.left, destRect.top);
-    }
-
+    transaction->setDestinationFrame(surfaceControl, destRect);
     transaction->setTransform(surfaceControl, transform);
     bool transformToInverseDisplay = (NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY & transform) ==
             NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
diff --git a/native/android/system_fonts.cpp b/native/android/system_fonts.cpp
index 60b0f1e..9fe7929 100644
--- a/native/android/system_fonts.cpp
+++ b/native/android/system_fonts.cpp
@@ -329,7 +329,9 @@
                 static_cast<minikin::FamilyVariant>(matcher->mFamilyVariant),
                 1  /* maxRun */);
 
-    const std::shared_ptr<minikin::Font>& font = runs[0].fakedFont.font;
+    const std::shared_ptr<minikin::Font>& font =
+            fc->getBestFont(minikin::U16StringPiece(text, textLength), runs[0], matcher->mFontStyle)
+                    .font;
     std::unique_ptr<AFont> result = std::make_unique<AFont>();
     const android::MinikinFontSkia* minikinFontSkia =
             reinterpret_cast<android::MinikinFontSkia*>(font->typeface().get());
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index 3d6cc68..614dfc1 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -839,8 +839,17 @@
         final int[] originalAdministratorUids = getAdministratorUids();
         final TransportInfo originalTransportInfo = getTransportInfo();
         clearAll();
-        mTransportTypes = (originalTransportTypes & TEST_NETWORKS_ALLOWED_TRANSPORTS)
-                | (1 << TRANSPORT_TEST);
+        if (0 != (originalCapabilities & NET_CAPABILITY_NOT_RESTRICTED)) {
+            // If the test network is not restricted, then it is only allowed to declare some
+            // specific transports. This is to minimize impact on running apps in case an app
+            // run from the shell creates a test a network.
+            mTransportTypes =
+                    (originalTransportTypes & UNRESTRICTED_TEST_NETWORKS_ALLOWED_TRANSPORTS)
+                            | (1 << TRANSPORT_TEST);
+        } else {
+            // If the test transport is restricted, then it may declare any transport.
+            mTransportTypes = (originalTransportTypes | (1 << TRANSPORT_TEST));
+        }
         mNetworkCapabilities = originalCapabilities & TEST_NETWORKS_ALLOWED_CAPABILITIES;
         mNetworkSpecifier = originalSpecifier;
         mSignalStrength = originalSignalStrength;
@@ -951,9 +960,10 @@
     };
 
     /**
-     * Allowed transports on a test network, in addition to TRANSPORT_TEST.
+     * Allowed transports on an unrestricted test network (in addition to TRANSPORT_TEST).
      */
-    private static final int TEST_NETWORKS_ALLOWED_TRANSPORTS = 1 << TRANSPORT_TEST
+    private static final int UNRESTRICTED_TEST_NETWORKS_ALLOWED_TRANSPORTS =
+            1 << TRANSPORT_TEST
             // Test ethernet networks can be created with EthernetManager#setIncludeTestInterfaces
             | 1 << TRANSPORT_ETHERNET
             // Test VPN networks can be created but their UID ranges must be empty.
diff --git a/packages/CtsShim/apk/arm/CtsShim.apk b/packages/CtsShim/apk/arm/CtsShim.apk
index 784a747..ca69a28 100644
--- a/packages/CtsShim/apk/arm/CtsShim.apk
+++ b/packages/CtsShim/apk/arm/CtsShim.apk
Binary files differ
diff --git a/packages/CtsShim/apk/arm/CtsShimPriv.apk b/packages/CtsShim/apk/arm/CtsShimPriv.apk
index 5b7bda4..d7cfb96 100644
--- a/packages/CtsShim/apk/arm/CtsShimPriv.apk
+++ b/packages/CtsShim/apk/arm/CtsShimPriv.apk
Binary files differ
diff --git a/packages/CtsShim/apk/x86/CtsShim.apk b/packages/CtsShim/apk/x86/CtsShim.apk
index 784a747..ca69a28 100644
--- a/packages/CtsShim/apk/x86/CtsShim.apk
+++ b/packages/CtsShim/apk/x86/CtsShim.apk
Binary files differ
diff --git a/packages/CtsShim/apk/x86/CtsShimPriv.apk b/packages/CtsShim/apk/x86/CtsShimPriv.apk
index 780cb8a..84c3401 100644
--- a/packages/CtsShim/apk/x86/CtsShimPriv.apk
+++ b/packages/CtsShim/apk/x86/CtsShimPriv.apk
Binary files differ
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index 16a946d..f8cb5d3 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -561,7 +561,20 @@
                 break;
         }
 
-        Log.d(TAG, "status=" + statusString + ", cause=" + causeString + ", detail=" + detail);
+        StringBuilder msg = new StringBuilder();
+        msg.append("status: " + statusString + ", cause: " + causeString);
+        if (status == STATUS_IN_PROGRESS) {
+            msg.append(
+                    String.format(
+                            ", partition name: %s, progress: %d/%d",
+                            mCurrentPartitionName,
+                            mCurrentPartitionInstalledSize,
+                            mCurrentPartitionSize));
+        }
+        if (detail != null) {
+            msg.append(", detail: " + detail);
+        }
+        Log.d(TAG, msg.toString());
 
         if (notifyOnNotificationBar) {
             mNM.notify(NOTIFICATION_ID, buildNotification(status, cause, detail));
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index 59ea9f0..f18d426 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -320,20 +320,21 @@
         }
     }
 
-    private void installScratch() throws IOException {
-        final long scratchSize = mDynSystem.suggestScratchSize();
+    private void installWritablePartition(final String partitionName, final long partitionSize)
+            throws IOException {
+        Log.d(TAG, "Creating writable partition: " + partitionName + ", size: " + partitionSize);
+
         Thread thread = new Thread() {
             @Override
             public void run() {
                 mInstallationSession =
-                        mDynSystem.createPartition("scratch", scratchSize, /* readOnly= */ false);
+                        mDynSystem.createPartition(
+                                partitionName, partitionSize, /* readOnly= */ false);
             }
         };
 
-        Log.d(TAG, "Creating partition: scratch, size = " + scratchSize);
         thread.start();
-
-        Progress progress = new Progress("scratch", scratchSize, mNumInstalledPartitions++);
+        Progress progress = new Progress(partitionName, partitionSize, mNumInstalledPartitions++);
 
         while (thread.isAlive()) {
             if (isCancelled()) {
@@ -356,53 +357,22 @@
 
         if (mInstallationSession == null) {
             throw new IOException(
-                    "Failed to start installation with requested size: " + scratchSize);
+                    "Failed to start installation with requested size: " + partitionSize);
         }
+
         // Reset installation session and verify that installation completes successfully.
         mInstallationSession = null;
         if (!mDynSystem.closePartition()) {
-            throw new IOException("Failed to complete partition installation: scratch");
+            throw new IOException("Failed to complete partition installation: " + partitionName);
         }
     }
 
+    private void installScratch() throws IOException {
+        installWritablePartition("scratch", mDynSystem.suggestScratchSize());
+    }
+
     private void installUserdata() throws IOException {
-        Thread thread = new Thread(() -> {
-            mInstallationSession = mDynSystem.createPartition("userdata", mUserdataSize, false);
-        });
-
-        Log.d(TAG, "Creating partition: userdata, size = " + mUserdataSize);
-        thread.start();
-
-        Progress progress = new Progress("userdata", mUserdataSize, mNumInstalledPartitions++);
-
-        while (thread.isAlive()) {
-            if (isCancelled()) {
-                return;
-            }
-
-            final long installedSize = mDynSystem.getInstallationProgress().bytes_processed;
-
-            if (installedSize > progress.installedSize + MIN_PROGRESS_TO_PUBLISH) {
-                progress.installedSize = installedSize;
-                publishProgress(progress);
-            }
-
-            try {
-                Thread.sleep(100);
-            } catch (InterruptedException e) {
-                // Ignore the error.
-            }
-        }
-
-        if (mInstallationSession == null) {
-            throw new IOException(
-                    "Failed to start installation with requested size: " + mUserdataSize);
-        }
-        // Reset installation session and verify that installation completes successfully.
-        mInstallationSession = null;
-        if (!mDynSystem.closePartition()) {
-            throw new IOException("Failed to complete partition installation: userdata");
-        }
+        installWritablePartition("userdata", mUserdataSize);
     }
 
     private void installImages() throws IOException, ImageValidationException {
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 69cee00..a65bf41 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -54,6 +54,7 @@
         "SettingsLibAppPreference",
         "SettingsLibSearchWidget",
         "SettingsLibSettingsSpinner",
+        "SettingsLibIllustrationPreference",
         "SettingsLibLayoutPreference",
         "SettingsLibMainSwitchPreference",
         "SettingsLibActionButtonsPreference",
diff --git a/packages/SettingsLib/IllustrationPreference/Android.bp b/packages/SettingsLib/IllustrationPreference/Android.bp
new file mode 100644
index 0000000..f8dd384
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/Android.bp
@@ -0,0 +1,23 @@
+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_library {
+    name: "SettingsLibIllustrationPreference",
+
+    srcs: ["src/**/*.java"],
+    resource_dirs: ["res"],
+
+    static_libs: [
+        "androidx.preference_preference",
+        "lottie",
+    ],
+
+    sdk_version: "system_current",
+    min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/IllustrationPreference/AndroidManifest.xml b/packages/SettingsLib/IllustrationPreference/AndroidManifest.xml
new file mode 100644
index 0000000..120b085
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/AndroidManifest.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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.settingslib.widget">
+
+    <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/IllustrationPreference/res/drawable/ic_gesture_play_button.xml b/packages/SettingsLib/IllustrationPreference/res/drawable/ic_gesture_play_button.xml
new file mode 100644
index 0000000..55b3115
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/res/drawable/ic_gesture_play_button.xml
@@ -0,0 +1,24 @@
+<!--
+    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="48.0"
+        android:viewportHeight="48.0">
+    <path android:fillColor="#FFFFFF" android:pathData="M24,24m-19,0a19,19 0,1 1,38 0a19,19 0,1 1,-38 0"/>
+    <path android:fillColor="#1A73E8" android:pathData="M20,33l12,-9l-12,-9z"/>
+    <path android:fillColor="#1A73E8" android:pathData="M24,4C12.96,4 4,12.96 4,24s8.96,20 20,20s20,-8.96 20,-20S35.04,4 24,4zM24,40c-8.82,0 -16,-7.18 -16,-16S15.18,8 24,8s16,7.18 16,16S32.82,40 24,40z"/>
+</vector>
diff --git a/packages/SettingsLib/IllustrationPreference/res/drawable/protection_background.xml b/packages/SettingsLib/IllustrationPreference/res/drawable/protection_background.xml
new file mode 100644
index 0000000..dd2fa5e
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/res/drawable/protection_background.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.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:top="@dimen/settingslib_illustration_padding"
+        android:left="@dimen/settingslib_illustration_padding"
+        android:right="@dimen/settingslib_illustration_padding"
+        android:bottom="@dimen/settingslib_illustration_padding">
+        <shape android:shape="rectangle">
+            <solid android:color="@color/settingslib_protection_color"/>
+            <corners android:radius="28dp"/>
+        </shape>
+    </item>
+</layer-list>
diff --git a/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml b/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml
new file mode 100644
index 0000000..2cbb888
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:background="?android:attr/colorBackground"
+    android:gravity="center"
+    android:orientation="horizontal">
+
+    <View
+        android:id="@+id/protection_layer"
+        android:layout_width="412dp"
+        android:layout_height="300dp"
+        android:layout_gravity="center"
+        android:padding="@dimen/settingslib_illustration_padding"
+        android:background="@drawable/protection_background"/>
+
+    <com.airbnb.lottie.LottieAnimationView
+        android:id="@+id/lottie_view"
+        android:layout_width="412dp"
+        android:layout_height="300dp"
+        android:layout_gravity="center"
+        android:padding="@dimen/settingslib_illustration_padding"
+        android:importantForAccessibility="no"/>
+
+    <ImageView
+        android:id="@+id/video_play_button"
+        android:layout_width="36dp"
+        android:layout_height="36dp"
+        android:layout_gravity="center"
+        android:visibility="gone"
+        android:src="@drawable/ic_gesture_play_button"/>
+
+</FrameLayout>
+
diff --git a/packages/SettingsLib/IllustrationPreference/res/values-night/colors.xml b/packages/SettingsLib/IllustrationPreference/res/values-night/colors.xml
new file mode 100644
index 0000000..71b18a8
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/res/values-night/colors.xml
@@ -0,0 +1,20 @@
+<?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>
+    <color name="settingslib_protection_color">@android:color/black</color>
+</resources>
diff --git a/packages/SettingsLib/IllustrationPreference/res/values/colors.xml b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
new file mode 100644
index 0000000..e53a43e
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/res/values/colors.xml
@@ -0,0 +1,20 @@
+<?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>
+    <color name="settingslib_protection_color">@android:color/white</color>
+</resources>
diff --git a/packages/SettingsLib/IllustrationPreference/res/values/dimens.xml b/packages/SettingsLib/IllustrationPreference/res/values/dimens.xml
new file mode 100644
index 0000000..79562b9
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/res/values/dimens.xml
@@ -0,0 +1,21 @@
+<?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>
+    <!-- Padding of illustration -->
+    <dimen name="settingslib_illustration_padding">16dp</dimen>
+</resources>
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
new file mode 100644
index 0000000..90b8a32
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
@@ -0,0 +1,166 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.Preference.OnPreferenceClickListener;
+import androidx.preference.PreferenceViewHolder;
+
+import com.airbnb.lottie.LottieAnimationView;
+
+/**
+ * IllustrationPreference is a preference that can play lottie format animation
+ */
+public class IllustrationPreference extends Preference implements OnPreferenceClickListener {
+
+    static final String TAG = "IllustrationPreference";
+    private int mAnimationId;
+    private boolean mIsAnimating;
+    private ImageView mPlayButton;
+    private LottieAnimationView mIllustrationView;
+
+    public IllustrationPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init(context, attrs);
+    }
+
+    public IllustrationPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init(context, attrs);
+    }
+
+    public IllustrationPreference(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        init(context, attrs);
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+        if (mAnimationId == 0) {
+            Log.w(TAG, "Invalid illustration resource id.");
+            return;
+        }
+        mPlayButton = (ImageView) holder.findViewById(R.id.video_play_button);
+        mIllustrationView = (LottieAnimationView) holder.findViewById(R.id.lottie_view);
+        mIllustrationView.setAnimation(mAnimationId);
+        mIllustrationView.loop(true);
+        mIllustrationView.playAnimation();
+        updateAnimationStatus(mIsAnimating);
+        setOnPreferenceClickListener(this);
+    }
+
+    @Override
+    public boolean onPreferenceClick(Preference preference) {
+        mIsAnimating = !isAnimating();
+        updateAnimationStatus(mIsAnimating);
+        return true;
+    }
+
+    @Override
+    protected Parcelable onSaveInstanceState() {
+        Parcelable superState = super.onSaveInstanceState();
+        SavedState ss = new SavedState(superState);
+        ss.mIsAnimating = mIsAnimating;
+        return ss;
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Parcelable state) {
+        SavedState ss = (SavedState) state;
+        super.onRestoreInstanceState(ss.getSuperState());
+        mIsAnimating = ss.mIsAnimating;
+    }
+
+    @VisibleForTesting
+    boolean isAnimating() {
+        return mIllustrationView.isAnimating();
+    }
+
+    private void init(Context context, AttributeSet attrs) {
+        setLayoutResource(R.layout.illustration_preference);
+
+        mIsAnimating = true;
+        if (attrs != null) {
+            final TypedArray a = context.obtainStyledAttributes(attrs,
+                    R.styleable.LottieAnimationView, 0 /*defStyleAttr*/, 0 /*defStyleRes*/);
+            mAnimationId = a.getResourceId(R.styleable.LottieAnimationView_lottie_rawRes, 0);
+            a.recycle();
+        }
+    }
+
+    private void updateAnimationStatus(boolean playAnimation) {
+        if (playAnimation) {
+            mIllustrationView.resumeAnimation();
+            mPlayButton.setVisibility(View.INVISIBLE);
+        } else {
+            mIllustrationView.pauseAnimation();
+            mPlayButton.setVisibility(View.VISIBLE);
+        }
+    }
+
+    static class SavedState extends BaseSavedState {
+        boolean mIsAnimating;
+
+        SavedState(Parcelable superState) {
+            super(superState);
+        }
+
+        /**
+         * Constructor called from {@link #CREATOR}
+         */
+        private SavedState(Parcel in) {
+            super(in);
+            mIsAnimating = (Boolean) in.readValue(null);
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            super.writeToParcel(out, flags);
+            out.writeValue(mIsAnimating);
+        }
+
+        @Override
+        public String toString() {
+            return "IllustrationPreference.SavedState{"
+                    + Integer.toHexString(System.identityHashCode(this))
+                    + " mIsAnimating=" + mIsAnimating + "}";
+        }
+
+        public static final Parcelable.Creator<SavedState> CREATOR =
+                new Parcelable.Creator<SavedState>() {
+                    public SavedState createFromParcel(Parcel in) {
+                        return new SavedState(in);
+                    }
+
+                    public SavedState[] newArray(int size) {
+                        return new SavedState[size];
+                    }
+                };
+    }
+}
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
index 0748acd..123c477 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
@@ -72,9 +72,7 @@
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
 
-        LayoutInflater.from(context).inflate(
-                resourceId(context, "layout", "settingslib_main_switch_bar"),
-                this);
+        LayoutInflater.from(context).inflate(R.layout.settingslib_main_switch_bar, this);
 
         if (!BuildCompat.isAtLeastS()) {
             final TypedArray a = context.obtainStyledAttributes(
@@ -90,12 +88,10 @@
         mFrameView = findViewById(R.id.frame);
         mTextView = (TextView) findViewById(R.id.switch_text);
         mSwitch = (Switch) findViewById(android.R.id.switch_widget);
-        mBackgroundOn = getContext().getDrawable(
-                resourceId(context, "drawable", "settingslib_switch_bar_bg_on"));
-        mBackgroundOff = getContext().getDrawable(
-                resourceId(context, "drawable", "settingslib_switch_bar_bg_off"));
+        mBackgroundOn = getContext().getDrawable(R.drawable.settingslib_switch_bar_bg_on);
+        mBackgroundOff = getContext().getDrawable(R.drawable.settingslib_switch_bar_bg_off);
         mBackgroundDisabled = getContext().getDrawable(
-                resourceId(context, "drawable", "settingslib_switch_bar_bg_disabled"));
+                R.drawable.settingslib_switch_bar_bg_disabled);
 
         addOnSwitchChangeListener((switchView, isChecked) -> setChecked(isChecked));
 
@@ -302,8 +298,4 @@
 
         requestLayout();
     }
-
-    private int resourceId(Context context, String type, String name) {
-        return context.getResources().getIdentifier(name, type, context.getPackageName());
-    }
 }
diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
index 154a0f4..304c343 100644
--- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
+++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
@@ -36,6 +36,7 @@
     private SettingsSpinnerAdapter mAdapter;
     private AdapterView.OnItemSelectedListener mListener;
     private int mPosition; //Default 0 for internal shard storage.
+    private boolean mIsClickable = true;
 
     /**
      * Perform inflation from XML and apply a class-specific base style.
@@ -50,6 +51,7 @@
     public SettingsSpinnerPreference(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
         setLayoutResource(R.layout.settings_spinner_preference);
+        setSelectable(false);
     }
 
     /**
@@ -62,6 +64,7 @@
     public SettingsSpinnerPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
         setLayoutResource(R.layout.settings_spinner_preference);
+        setSelectable(false);
     }
 
     /**
@@ -98,10 +101,21 @@
         notifyChanged();
     }
 
+    /** Set clickable of the spinner. */
+    public void setClickable(boolean isClickable) {
+        if (mIsClickable == isClickable) {
+            return;
+        }
+        mIsClickable = isClickable;
+        notifyChanged();
+    }
+
     @Override
     public void onBindViewHolder(PreferenceViewHolder holder) {
         super.onBindViewHolder(holder);
         final SettingsSpinner spinner = (SettingsSpinner) holder.findViewById(R.id.spinner);
+        spinner.setEnabled(mIsClickable);
+        spinner.setClickable(mIsClickable);
         spinner.setAdapter(mAdapter);
         spinner.setSelection(mPosition);
         spinner.setOnItemSelectedListener(mOnSelectedListener);
diff --git a/packages/SettingsLib/TwoTargetPreference/src/com/android/settingslib/widget/TwoTargetPreference.java b/packages/SettingsLib/TwoTargetPreference/src/com/android/settingslib/widget/TwoTargetPreference.java
index 17f257d..9130662 100644
--- a/packages/SettingsLib/TwoTargetPreference/src/com/android/settingslib/widget/TwoTargetPreference.java
+++ b/packages/SettingsLib/TwoTargetPreference/src/com/android/settingslib/widget/TwoTargetPreference.java
@@ -72,9 +72,9 @@
     private void init(Context context) {
         setLayoutResource(R.layout.preference_two_target);
         mSmallIconSize = context.getResources().getDimensionPixelSize(
-                resourceId(context, "dimen", "two_target_pref_small_icon_size"));
+                R.dimen.two_target_pref_small_icon_size);
         mMediumIconSize = context.getResources().getDimensionPixelSize(
-                resourceId(context, "dimen", "two_target_pref_medium_icon_size"));
+                R.dimen.two_target_pref_medium_icon_size);
         final int secondTargetResId = getSecondTargetResId();
         if (secondTargetResId != 0) {
             setWidgetLayoutResource(secondTargetResId);
@@ -116,8 +116,4 @@
     protected int getSecondTargetResId() {
         return 0;
     }
-
-    private int resourceId(Context context, String type, String name) {
-        return context.getResources().getIdentifier(name, type, context.getPackageName());
-    }
 }
diff --git a/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_0.xml b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_0.xml
new file mode 100644
index 0000000..efae569
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_0.xml
@@ -0,0 +1,30 @@
+<!--
+    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">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M20.66,7C18.19,5.07 15.14,4 12,4C8.58,4 5.27,5.27 2.7,7.53L12,18.85l6,-7.3v3.15L12,22L0,7.39C2.97,4.08 7.25,2 12,2c4.56,0 8.69,1.92 11.64,5H20.66z"/>
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M22,10h-2v8h2V10z"/>
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M22,20h-2v2h2V20z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_1.xml b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_1.xml
new file mode 100644
index 0000000..d50e734
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_1.xml
@@ -0,0 +1,30 @@
+<!--
+    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">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M23.62,7C20.65,3.93 16.6,2 12,2C7.2,2 3,4.1 0,7.4L12,22l6,-7.3v-3.19l-2.13,2.59C14.74,13.4 13.39,13 12,13s-2.74,0.4 -3.87,1.1L2.7,7.5C5.3,5.3 8.6,4 12,4c3.13,0 6.18,1.1 8.68,3H23.62z"/>
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M22,10h-2v8h2V10z"/>
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M22,20h-2v2h2V20z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_2.xml b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_2.xml
new file mode 100644
index 0000000..1be297e
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_2.xml
@@ -0,0 +1,30 @@
+<!--
+    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">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M23.62,7C20.65,3.93 16.6,2 12,2C7.2,2 3,4.1 0,7.4L12,22l6,-7.3v-3.19l-0.27,0.33C16.12,10.67 14.09,10 12,10c-2.09,0 -4.12,0.67 -5.73,1.84L2.7,7.5C5.3,5.3 8.6,4 12,4c3.13,0 6.18,1.1 8.68,3H23.62z"/>
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M22,10h-2v8h2V10z"/>
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M22,20h-2v2h2V20z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_3.xml b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_3.xml
new file mode 100644
index 0000000..738bd5a
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_3.xml
@@ -0,0 +1,30 @@
+<!--
+    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">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M0,7.4L12,22l6,-7.3V8.57C16.21,7.56 14.14,7 12,7C9.2,7 6.53,7.96 4.45,9.62L2.7,7.5C5.3,5.3 8.6,4 12,4c3.13,0 6.18,1.1 8.68,3h2.95C20.65,3.93 16.6,2 12,2C7.2,2 3,4.1 0,7.4z"/>
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M22,10h-2v8h2V10z"/>
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M22,20h-2v2h2V20z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_4.xml b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_4.xml
new file mode 100644
index 0000000..14d020b
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_no_internet_wifi_signal_4.xml
@@ -0,0 +1,30 @@
+<!--
+    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">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M0,7.4C3,4.1 7.2,2 12,2c4.6,0 8.65,1.93 11.62,5H18v7.7L12,22L0,7.4z"/>
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M22,10h-2v8h2V10z"/>
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M22,20h-2v2h2V20z"/>
+</vector>
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
index 9889419..4dd3ff1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
@@ -187,8 +187,9 @@
     }
 
     protected int getIconColorAttr() {
-        return (mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED)
-                ? android.R.attr.colorAccent : android.R.attr.colorControlNormal;
+        final boolean accent = (mWifiEntry.hasInternetAccess()
+                && mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED);
+        return accent ? android.R.attr.colorAccent : android.R.attr.colorControlNormal;
     }
 
     private void updateIcon(boolean showX, int level) {
@@ -267,7 +268,7 @@
         }
 
         public Drawable getIcon(boolean showX, int level) {
-            return mContext.getDrawable(Utils.getWifiIconResource(showX, level));
+            return mContext.getDrawable(WifiUtils.getInternetIconResource(level, showX));
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
index 15b146d..6100615 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
@@ -36,6 +36,22 @@
 
     private static final int INVALID_RSSI = -127;
 
+    static final int[] WIFI_PIE = {
+            com.android.internal.R.drawable.ic_wifi_signal_0,
+            com.android.internal.R.drawable.ic_wifi_signal_1,
+            com.android.internal.R.drawable.ic_wifi_signal_2,
+            com.android.internal.R.drawable.ic_wifi_signal_3,
+            com.android.internal.R.drawable.ic_wifi_signal_4
+    };
+
+    static final int[] NO_INTERNET_WIFI_PIE = {
+            R.drawable.ic_no_internet_wifi_signal_0,
+            R.drawable.ic_no_internet_wifi_signal_1,
+            R.drawable.ic_no_internet_wifi_signal_2,
+            R.drawable.ic_no_internet_wifi_signal_3,
+            R.drawable.ic_no_internet_wifi_signal_4
+    };
+
     public static String buildLoggingSummary(AccessPoint accessPoint, WifiConfiguration config) {
         final StringBuilder summary = new StringBuilder();
         final WifiInfo info = accessPoint.getInfo();
@@ -245,6 +261,20 @@
         return context.getString(R.string.wifi_unmetered_label);
     }
 
+    /**
+     * Returns the Internet icon resource for a given RSSI level.
+     *
+     * @param level The number of bars to show (0-4)
+     * @param noInternet True if a connected Wi-Fi network cannot access the Internet
+     * @throws IllegalArgumentException if an invalid RSSI level is given.
+     */
+    public static int getInternetIconResource(int level, boolean noInternet) {
+        if (level < 0 || level >= WIFI_PIE.length) {
+            throw new IllegalArgumentException("No Wifi icon found for level: " + level);
+        }
+        return noInternet ? NO_INTERNET_WIFI_PIE[level] : WIFI_PIE[level];
+    }
+
     public static boolean isMeteredOverridden(WifiConfiguration config) {
         return config.meteredOverride != WifiConfiguration.METERED_OVERRIDE_NONE;
     }
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java
index 53a382a..b0c5314 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java
@@ -89,4 +89,24 @@
         assertThat(mSpinnerPreference.getSelectedItem())
                 .isEqualTo(mSpinner.getAdapter().getItem(1));
     }
+
+    @Test
+    public void onBindViewHolder_setClickableTrue_isClickableTrue() {
+        mSpinnerPreference.setClickable(true);
+
+        mSpinnerPreference.onBindViewHolder(mViewHolder);
+
+        assertThat(mSpinner.isClickable()).isTrue();
+        assertThat(mSpinner.isEnabled()).isTrue();
+    }
+
+    @Test
+    public void onBindViewHolder_setClickableFalse_isClickableFalse() {
+        mSpinnerPreference.setClickable(false);
+
+        mSpinnerPreference.onBindViewHolder(mViewHolder);
+
+        assertThat(mSpinner.isClickable()).isFalse();
+        assertThat(mSpinner.isEnabled()).isFalse();
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
new file mode 100644
index 0000000..4a14403
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import com.airbnb.lottie.LottieAnimationView;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(RobolectricTestRunner.class)
+public class IllustrationPreferenceTest {
+
+    @Mock
+    LottieAnimationView mAnimationView;
+
+    private IllustrationPreference mPreference;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        final Context context = RuntimeEnvironment.application;
+        final AttributeSet attributeSet = Robolectric.buildAttributeSet().build();
+        mPreference = new IllustrationPreference(context, attributeSet);
+        ReflectionHelpers.setField(mPreference, "mIllustrationView", mAnimationView);
+    }
+
+    @Test
+    public void isAnimating_lottieAnimationViewIsNotAnimating_shouldReturnFalse() {
+        when(mAnimationView.isAnimating()).thenReturn(false);
+
+        assertThat(mPreference.isAnimating()).isFalse();
+    }
+
+    @Test
+    public void isAnimating_lottieAnimationViewIsAnimating_shouldReturnTrue() {
+        when(mAnimationView.isAnimating()).thenReturn(true);
+
+        assertThat(mPreference.isAnimating()).isTrue();
+    }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 01d66e6..3219b2b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -4944,6 +4944,15 @@
                                     String.valueOf(defAccessibilityButtonMode), /* tag= */
                                     null, /* makeDefault= */ true,
                                     SettingsState.SYSTEM_PACKAGE_NAME);
+
+                            if (hasValueInA11yButtonTargets(secureSettings)) {
+                                secureSettings.insertSettingLocked(
+                                        Secure.ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT,
+                                        /* enabled */ "1",
+                                        /* tag= */ null,
+                                        /* makeDefault= */ false,
+                                        SettingsState.SYSTEM_PACKAGE_NAME);
+                            }
                         }
                     }
 
@@ -5170,13 +5179,21 @@
         }
 
         private boolean isAccessibilityButtonInNavigationBarOn(SettingsState secureSettings) {
-            final boolean hasValueInA11yBtnTargets = !TextUtils.isEmpty(
-                    secureSettings.getSettingLocked(
-                            Secure.ACCESSIBILITY_BUTTON_TARGETS).getValue());
+            return hasValueInA11yButtonTargets(secureSettings) && !isGestureNavigateEnabled();
+        }
+
+        private boolean isGestureNavigateEnabled() {
             final int navigationMode = getContext().getResources().getInteger(
                     com.android.internal.R.integer.config_navBarInteractionMode);
+            return navigationMode == NAV_BAR_MODE_GESTURAL;
+        }
 
-            return hasValueInA11yBtnTargets && (navigationMode != NAV_BAR_MODE_GESTURAL);
+        private boolean hasValueInA11yButtonTargets(SettingsState secureSettings) {
+            final Setting a11yButtonTargetsSettings =
+                    secureSettings.getSettingLocked(Secure.ACCESSIBILITY_BUTTON_TARGETS);
+
+            return !a11yButtonTargetsSettings.isNull()
+                    && !TextUtils.isEmpty(a11yButtonTargetsSettings.getValue());
         }
     }
 }
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 22e38f4..3a82434 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -752,6 +752,7 @@
                  Settings.Secure.SUPPRESS_DOZE,
                  Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
                  Settings.Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT,
+                 Settings.Secure.ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT,
                  Settings.Secure.UI_TRANSLATION_ENABLED);
 
     @Test
diff --git a/packages/SystemUI/res/drawable/accessibility_floating_tooltip_background.xml b/packages/SystemUI/res/drawable/accessibility_floating_tooltip_background.xml
new file mode 100644
index 0000000..46e7dcc
--- /dev/null
+++ b/packages/SystemUI/res/drawable/accessibility_floating_tooltip_background.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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+       android:shape="rectangle">
+    <solid android:color="?androidprv:attr/colorAccentPrimary" />
+    <corners android:radius="@dimen/accessibility_floating_tooltip_text_corner_radius" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/ic_conversation_icon.xml b/packages/SystemUI/res/drawable/ic_conversation_icon.xml
new file mode 100644
index 0000000..0e3533b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_conversation_icon.xml
@@ -0,0 +1,47 @@
+<?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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="48dp"
+    android:height="48dp"
+    android:viewportWidth="48"
+    android:viewportHeight="48">
+    <path
+        android:pathData="M24,24m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"
+        android:fillColor="#81C995"/>
+    <path
+        android:pathData="M27,34C23.134,34 20,30.866 20,27C20,23.134 23.134,20 27,20C30.866,20 34,23.134 34,27C34,28.4872 33.5362,29.8662 32.7453,31"
+        android:strokeWidth="2"
+        android:fillColor="#00000000"
+        android:strokeColor="#3C4043"/>
+    <path
+        android:pathData="M35,33l-8,0l-0,2l8,0z"
+        android:fillColor="#3C4043"/>
+    <path
+        android:pathData="M21,21m-6,0a6,6 0,1 1,12 0a6,6 0,1 1,-12 0"
+        android:fillColor="#81C995"/>
+    <path
+        android:pathData="M16,25h5v2h-5z"
+        android:fillColor="#81C995"/>
+    <path
+        android:pathData="M21,28C24.866,28 28,24.866 28,21C28,17.134 24.866,14 21,14C17.134,14 14,17.134 14,21C14,22.4872 14.4638,23.8662 15.2547,25"
+        android:strokeWidth="2"
+        android:fillColor="#00000000"
+        android:strokeColor="#ffffff"/>
+    <path
+        android:pathData="M13,27h8v2h-8z"
+        android:fillColor="#ffffff"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_corp_badge_off.xml b/packages/SystemUI/res/drawable/ic_corp_badge_off.xml
new file mode 100644
index 0000000..a441fb2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_corp_badge_off.xml
@@ -0,0 +1,28 @@
+<!--
+  ~ 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="20dp"
+    android:height="20dp"
+    android:viewportWidth="20.0"
+    android:viewportHeight="20.0">
+    <path
+        android:pathData="M10,10m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
+        android:fillColor="#607D8B"/>
+    <path
+        android:pathData="M16.42,15.68l-0.85,-0.85L7.21,6.47L4.9,4.16L4.16,4.9l1.57,1.57H5.36c-0.65,0 -1.16,0.52 -1.16,1.17L4.2,14.05c0,0.65 0.52,1.17 1.17,1.17h9.12l1.2,1.2L16.42,15.68zM15.83,7.64c0.03,-0.65 -0.49,-1.17 -1.14,-1.14h-2.33V5.3c0,-0.65 -0.52,-1.17 -1.17,-1.14H8.86C8.22,4.14 7.7,4.66 7.7,5.3v0.19l8.14,8.17V7.64zM11.2,6.5H8.83V5.3h2.36V6.5z"
+        android:fillColor="#FFFFFF"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/people_tile_empty_background.xml b/packages/SystemUI/res/drawable/people_tile_empty_background.xml
new file mode 100644
index 0000000..2dac740
--- /dev/null
+++ b/packages/SystemUI/res/drawable/people_tile_empty_background.xml
@@ -0,0 +1,21 @@
+<?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">
+    <solid android:color="?androidprv:attr/colorSurface" />
+    <corners android:radius="@dimen/people_space_widget_radius" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/people_tile_status_scrim.xml b/packages/SystemUI/res/drawable/people_tile_status_scrim.xml
new file mode 100644
index 0000000..cf16f1c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/people_tile_status_scrim.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.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <gradient
+        android:type="linear"
+        android:angle="90"
+        android:endColor="@android:color/transparent"
+        android:startColor="?androidprv:attr/colorSurfaceHeader" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/people_tile_suppressed_background.xml b/packages/SystemUI/res/drawable/people_tile_suppressed_background.xml
new file mode 100644
index 0000000..e0c111d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/people_tile_suppressed_background.xml
@@ -0,0 +1,21 @@
+<?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">
+    <solid android:color="?androidprv:attr/colorSurfaceVariant" />
+    <corners android:radius="@dimen/people_space_widget_radius" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/wallet_action_button_bg.xml b/packages/SystemUI/res/drawable/wallet_action_button_bg.xml
index bf7625f..925ab67 100644
--- a/packages/SystemUI/res/drawable/wallet_action_button_bg.xml
+++ b/packages/SystemUI/res/drawable/wallet_action_button_bg.xml
@@ -14,13 +14,14 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License
 -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
     <item>
         <shape android:shape="rectangle">
         <stroke
             android:width="1dp"
-            android:color="@color/GM2_blue_600"/>
-        <solid android:color="@color/GM2_blue_600"/>
+            android:color="?androidprv:attr/colorAccentPrimary"/>
+        <solid android:color="?androidprv:attr/colorAccentPrimary"/>
         <corners android:radius="24dp"/>
         </shape>
     </item>
diff --git a/packages/SystemUI/res/drawable/wallet_empty_state_bg.xml b/packages/SystemUI/res/drawable/wallet_empty_state_bg.xml
index f52889a..b203228 100644
--- a/packages/SystemUI/res/drawable/wallet_empty_state_bg.xml
+++ b/packages/SystemUI/res/drawable/wallet_empty_state_bg.xml
@@ -15,7 +15,7 @@
   ~ limitations under the License.
   -->
 <ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="?android:attr/colorControlHighlight">
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
     <item android:id="@android:id/mask">
     <shape android:shape="rectangle">
         <solid android:color="#DADCE0" />
@@ -26,7 +26,7 @@
         <shape>
         <stroke
             android:width="1dp"
-            android:color="@color/GM2_grey_900" />
+            android:color="?androidprv:attr/colorAccentPrimary" />
         <corners android:radius="@dimen/wallet_empty_state_corner_radius" />
         </shape>
     </item>
diff --git a/packages/SystemUI/res/layout/accessibility_floating_menu_tooltip.xml b/packages/SystemUI/res/layout/accessibility_floating_menu_tooltip.xml
new file mode 100644
index 0000000..5e7b7e1
--- /dev/null
+++ b/packages/SystemUI/res/layout/accessibility_floating_menu_tooltip.xml
@@ -0,0 +1,52 @@
+<?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/tooltip"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal">
+
+    <View
+        android:id="@+id/arrow_left"
+        android:layout_width="@dimen/accessibility_floating_tooltip_arrow_width"
+        android:layout_height="@dimen/accessibility_floating_tooltip_arrow_height"
+        android:layout_marginRight="@dimen/accessibility_floating_tooltip_arrow_margin"
+        android:visibility="gone"
+        android:layout_gravity="center_vertical"/>
+
+    <TextView
+        android:id="@+id/text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:padding="@dimen/accessibility_floating_tooltip_padding"
+        android:background="@drawable/accessibility_floating_tooltip_background"
+        android:textColor="@android:color/black"
+        android:textColorLink="@android:color/black"
+        android:text="@string/accessibility_floating_button_migration_tooltip"
+        android:textSize="@dimen/accessibility_floating_tooltip_font_size"/>
+
+    <View
+        android:id="@+id/arrow_right"
+        android:layout_width="@dimen/accessibility_floating_tooltip_arrow_width"
+        android:layout_height="@dimen/accessibility_floating_tooltip_arrow_height"
+        android:layout_marginLeft="@dimen/accessibility_floating_tooltip_arrow_margin"
+        android:visibility="gone"
+        android:layout_gravity="center_vertical"/>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml
index ceba4e3..b99c86f 100644
--- a/packages/SystemUI/res/layout/long_screenshot.xml
+++ b/packages/SystemUI/res/layout/long_screenshot.xml
@@ -126,8 +126,8 @@
         android:layout_height="wrap_content"
         app:layout_constraintTop_toBottomOf="@id/save"
         app:layout_constraintStart_toStartOf="parent"
-        android:scaleType="matrix"
-        android:visibility="gone"
+        android:scaleType="centerCrop"
+        android:visibility="invisible"
         />
 
 </androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
index 8c54e2c..9e67258 100644
--- a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
+++ b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
@@ -75,7 +75,7 @@
         android:id="@+id/media_logo1"
         android:layout_width="@dimen/qs_media_icon_size"
         android:layout_height="@dimen/qs_media_icon_size"
-        style="@style/MediaPlayer.AppIcon" />
+        style="@style/MediaPlayer.AppIcon.Recommendation" />
 
     <ImageView
         android:id="@+id/media_cover2"
@@ -91,7 +91,7 @@
         android:id="@+id/media_logo2"
         android:layout_width="@dimen/qs_media_icon_size"
         android:layout_height="@dimen/qs_media_icon_size"
-        style="@style/MediaPlayer.AppIcon" />
+        style="@style/MediaPlayer.AppIcon.Recommendation" />
 
     <ImageView
         android:id="@+id/media_cover3"
@@ -107,7 +107,7 @@
         android:id="@+id/media_logo3"
         android:layout_width="@dimen/qs_media_icon_size"
         android:layout_height="@dimen/qs_media_icon_size"
-        style="@style/MediaPlayer.AppIcon" />
+        style="@style/MediaPlayer.AppIcon.Recommendation" />
 
     <ImageView
         android:id="@+id/media_cover4"
@@ -123,7 +123,7 @@
         android:id="@+id/media_logo4"
         android:layout_width="@dimen/qs_media_icon_size"
         android:layout_height="@dimen/qs_media_icon_size"
-        style="@style/MediaPlayer.AppIcon" />
+        style="@style/MediaPlayer.AppIcon.Recommendation" />
 
     <ImageView
         android:id="@+id/media_cover5"
@@ -139,7 +139,7 @@
         android:id="@+id/media_logo5"
         android:layout_width="@dimen/qs_media_icon_size"
         android:layout_height="@dimen/qs_media_icon_size"
-        style="@style/MediaPlayer.AppIcon" />
+        style="@style/MediaPlayer.AppIcon.Recommendation" />
 
     <ImageView
         android:id="@+id/media_cover6"
@@ -155,7 +155,7 @@
         android:id="@+id/media_logo6"
         android:layout_width="@dimen/qs_media_icon_size"
         android:layout_height="@dimen/qs_media_icon_size"
-        style="@style/MediaPlayer.AppIcon" />
+        style="@style/MediaPlayer.AppIcon.Recommendation" />
 
     <!-- Long press menu -->
     <TextView
diff --git a/packages/SystemUI/res/layout/people_status_scrim_layout.xml b/packages/SystemUI/res/layout/people_status_scrim_layout.xml
new file mode 100644
index 0000000..9808f74
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_status_scrim_layout.xml
@@ -0,0 +1,58 @@
+<?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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/scrim_layout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <ImageView
+        android:id="@+id/status_icon"
+        android:scaleType="centerCrop"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:weightSum="1"
+        android:orientation="vertical">
+        <ImageView
+            android:layout_weight=".2"
+            android:layout_width="match_parent"
+            android:layout_height="0dp" />
+        <ImageView
+            android:background="@drawable/people_tile_status_scrim"
+            android:layout_weight=".8"
+            android:scaleType="centerCrop"
+            android:layout_width="match_parent"
+            android:layout_height="0dp" />
+    </LinearLayout>
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:weightSum="1"
+        android:orientation="vertical">
+        <ImageView
+            android:layout_weight=".66"
+            android:layout_width="match_parent"
+            android:layout_height="0dp" />
+        <ImageView
+            android:background="@drawable/people_tile_status_scrim"
+            android:layout_weight=".33"
+            android:layout_width="match_parent"
+            android:layout_height="0dp" />
+    </LinearLayout>
+</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/people_tile_empty_layout.xml b/packages/SystemUI/res/layout/people_tile_empty_layout.xml
index 7d3b919..8e9ebc6 100644
--- a/packages/SystemUI/res/layout/people_tile_empty_layout.xml
+++ b/packages/SystemUI/res/layout/people_tile_empty_layout.xml
@@ -14,16 +14,17 @@
   ~ limitations under the License.
   -->
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/item"
     android:theme="@android:style/Theme.DeviceDefault.DayNight"
-    android:background="@drawable/people_space_tile_view_card"
+    android:background="@drawable/people_tile_empty_background"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
     <ImageView
-        android:id="@+id/item"
+        android:id="@+id/icon"
+        android:src="@drawable/ic_conversation_icon"
         android:gravity="center"
         android:layout_gravity="center"
-        android:padding="8dp"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"/>
+        android:layout_width="48dp"
+        android:layout_height="48dp"/>
 </FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_tile_large_with_content.xml b/packages/SystemUI/res/layout/people_tile_large_with_content.xml
index 6f8de3b..b77670e 100644
--- a/packages/SystemUI/res/layout/people_tile_large_with_content.xml
+++ b/packages/SystemUI/res/layout/people_tile_large_with_content.xml
@@ -1,145 +1,159 @@
 <?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:theme="@android:style/Theme.DeviceDefault.DayNight"
-    android:id="@+id/item"
+~ 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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:background="@drawable/people_space_tile_view_card"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
+    android:id="@+id/item"
+    android:clipToOutline="true"
+    android:theme="@android:style/Theme.DeviceDefault.DayNight"
     android:layout_gravity="center"
-    android:padding="16dp"
-    android:orientation="vertical">
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
 
-    <RelativeLayout
+    <include layout="@layout/people_status_scrim_layout" />
+
+    <LinearLayout
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="start|top">
+        android:layout_height="match_parent"
+        android:layout_gravity="center"
+        android:padding="16dp"
+        android:orientation="vertical">
 
-        <LinearLayout
-            android:layout_width="wrap_content"
+        <RelativeLayout
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_alignParentStart="true"
-            android:gravity="start|top"
-            android:orientation="horizontal">
+            android:gravity="start|top">
 
-            <ImageView
-                android:id="@+id/person_icon"
-                android:layout_marginStart="-2dp"
-                android:layout_marginTop="-2dp"
+            <LinearLayout
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_weight="1" />
+                android:layout_alignParentStart="true"
+                android:gravity="start|top"
+                android:orientation="horizontal">
 
-            <ImageView
-                android:id="@+id/availability"
-                android:layout_marginStart="-2dp"
-                android:layout_width="10dp"
-                android:layout_height="10dp"
-                android:background="@drawable/circle_green_10dp" />
-        </LinearLayout>
+                <ImageView
+                    android:id="@+id/person_icon"
+                    android:layout_marginStart="-2dp"
+                    android:layout_marginTop="-2dp"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_weight="1" />
 
-        <TextView
-            android:id="@+id/messages_count"
-            android:layout_alignParentEnd="true"
-            android:paddingStart="8dp"
-            android:paddingEnd="8dp"
-            android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
-            android:textColor="?android:attr/textColorPrimary"
-            android:background="@drawable/people_space_messages_count_background"
-            android:textSize="14sp"
-            android:maxLines="1"
-            android:ellipsize="end"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:visibility="gone"
-            />
-    </RelativeLayout>
-    <RelativeLayout
-        android:layout_width="match_parent"
-        android:layout_height="match_parent">
-        <include layout="@layout/people_tile_punctuation_background_large" />
-        <include layout="@layout/people_tile_emoji_background_large" />
-
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:orientation="vertical">
-        <TextView
-            android:layout_gravity="center"
-            android:id="@+id/name"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:paddingBottom="12dp"
-            android:gravity="start"
-            android:singleLine="true"
-            android:ellipsize="end"
-            android:text="@string/empty_user_name"
-            android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
-            android:textColor="?android:attr/textColorPrimary"
-            android:textSize="14sp" />
-
-        <LinearLayout
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:paddingBottom="4dp"
-            android:gravity="center_vertical"
-            android:orientation="horizontal">
-
-            <ImageView
-                android:id="@+id/predefined_icon"
-                android:tint="?android:attr/colorAccent"
-                android:gravity="start|center_vertical"
-                android:paddingEnd="6dp"
-                android:layout_width="24dp"
-                android:layout_height="18dp" />
+                <ImageView
+                    android:id="@+id/availability"
+                    android:layout_marginStart="-2dp"
+                    android:layout_width="10dp"
+                    android:layout_height="10dp"
+                    android:background="@drawable/circle_green_10dp" />
+            </LinearLayout>
 
             <TextView
-                android:layout_gravity="center"
-                android:id="@+id/subtext"
-                android:gravity="center_vertical"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:ellipsize="end"
-                android:singleLine="true"
-                android:text="@string/empty_user_name"
+                android:id="@+id/messages_count"
+                android:layout_alignParentEnd="true"
+                android:paddingStart="8dp"
+                android:paddingEnd="8dp"
                 android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
-                android:textColor="?android:attr/textColorSecondary"
-                android:textSize="12sp" />
-        </LinearLayout>
+                android:textColor="?android:attr/textColorPrimary"
+                android:background="@drawable/people_space_messages_count_background"
+                android:textSize="14sp"
+                android:maxLines="1"
+                android:ellipsize="end"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:visibility="gone" />
+        </RelativeLayout>
 
-        <ImageView
-            android:id="@+id/image"
+        <RelativeLayout
             android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:background="@drawable/people_space_content_background"
-            android:gravity="center"
-            android:scaleType="centerCrop" />
+            android:layout_height="match_parent">
 
-        <TextView
-            android:layout_gravity="center"
-            android:id="@+id/text_content"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:ellipsize="end"
-            android:maxLines="2"
-            android:singleLine="false"
-            android:text="@string/empty_status"
-            android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
-            android:textColor="?android:attr/textColorPrimary"
-            android:textSize="12sp" />
-        </LinearLayout>
-    </RelativeLayout>
-</LinearLayout>
+            <include layout="@layout/people_tile_punctuation_background_large" />
+
+            <include layout="@layout/people_tile_emoji_background_large" />
+
+            <LinearLayout
+                android:id="@+id/content"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:orientation="vertical">
+
+                <TextView
+                    android:layout_gravity="center"
+                    android:id="@+id/name"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:paddingBottom="12dp"
+                    android:gravity="start"
+                    android:singleLine="true"
+                    android:ellipsize="end"
+                    android:text="@string/empty_user_name"
+                    android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+                    android:textColor="?android:attr/textColorPrimary"
+                    android:textSize="14sp" />
+
+                <LinearLayout
+                    android:id="@+id/status_icon_and_label"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:paddingBottom="4dp"
+                    android:gravity="center_vertical"
+                    android:orientation="horizontal">
+
+                    <ImageView
+                        android:id="@+id/predefined_icon"
+                        android:tint="?android:attr/colorAccent"
+                        android:gravity="start|center_vertical"
+                        android:paddingEnd="6dp"
+                        android:layout_width="24dp"
+                        android:layout_height="18dp" />
+
+                    <TextView
+                        android:layout_gravity="center"
+                        android:id="@+id/subtext"
+                        android:gravity="center_vertical"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:ellipsize="end"
+                        android:singleLine="true"
+                        android:text="@string/empty_user_name"
+                        android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+                        android:textColor="?android:attr/textColorSecondary"
+                        android:textSize="12sp" />
+                </LinearLayout>
+
+                <ImageView
+                    android:id="@+id/image"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:background="@drawable/people_space_content_background"
+                    android:gravity="center"
+                    android:scaleType="centerCrop" />
+
+                <TextView
+                    android:layout_gravity="center"
+                    android:id="@+id/text_content"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:ellipsize="end"
+                    android:maxLines="2"
+                    android:singleLine="false"
+                    android:text="@string/empty_status"
+                    android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
+                    android:textColor="?android:attr/textColorPrimary"
+                    android:textSize="@dimen/content_text_size_for_large" />
+            </LinearLayout>
+        </RelativeLayout>
+    </LinearLayout>
+</RelativeLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_tile_medium_with_content.xml b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
index a8c15ab..8df0bf8 100644
--- a/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
+++ b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
@@ -17,18 +17,20 @@
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:theme="@android:style/Theme.DeviceDefault.DayNight"
+    android:id="@+id/item"
+    android:background="@drawable/people_space_tile_view_card"
     android:layout_gravity="center"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical">
     <RelativeLayout
-        android:background="@drawable/people_space_tile_view_card"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
         <include layout="@layout/people_tile_punctuation_background_medium" />
-        <include layout="@layout/people_tile_emoji_background_medium" />
+        <include layout="@layout/people_tile_punctuation_background_medium" />
+        <include layout="@layout/people_status_scrim_layout" />
         <LinearLayout
-            android:id="@+id/item"
+            android:id="@+id/content"
             android:orientation="vertical"
             android:layout_gravity="center"
             android:padding="8dp"
@@ -89,7 +91,7 @@
                         android:text="@string/empty_status"
                         android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
                         android:textColor="?android:attr/textColorPrimary"
-                        android:textSize="12sp"
+                        android:textSize="@dimen/content_text_size_for_medium"
                         android:layout_width="match_parent"
                         android:layout_height="wrap_content"
                         android:maxLines="2"
diff --git a/packages/SystemUI/res/layout/people_tile_suppressed_layout.xml b/packages/SystemUI/res/layout/people_tile_suppressed_layout.xml
new file mode 100644
index 0000000..b151c60
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_tile_suppressed_layout.xml
@@ -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.
+  -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/item"
+    android:theme="@android:style/Theme.DeviceDefault.DayNight"
+    android:background="@drawable/people_tile_suppressed_background"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <ImageView
+        android:id="@+id/icon"
+        android:gravity="center"
+        android:layout_gravity="center"
+        android:layout_width="48dp"
+        android:layout_height="48dp"/>
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_tile_work_profile_quiet_layout.xml b/packages/SystemUI/res/layout/people_tile_work_profile_quiet_layout.xml
new file mode 100644
index 0000000..25ab5a6
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_tile_work_profile_quiet_layout.xml
@@ -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.
+  -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:id="@+id/item"
+    android:theme="@android:style/Theme.DeviceDefault.DayNight"
+    android:background="@drawable/people_tile_suppressed_background"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <ImageView
+        android:id="@+id/icon"
+        android:gravity="center"
+        android:layout_gravity="center"
+        android:layout_width="48dp"
+        android:layout_height="48dp"/>
+
+    <ImageView android:id="@+id/work_widget_badge_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom|right"
+        android:layout_marginBottom="12dp"
+        android:layout_marginRight="12dp"
+        android:src="@drawable/ic_corp_badge_off"
+        android:clickable="false" />
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index f0229a6..a62310b 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -28,7 +28,7 @@
 
     <com.android.systemui.statusbar.policy.Clock
         android:id="@+id/clock"
-        android:layout_width="wrap_content"
+        android:layout_width="0dp"
         android:layout_height="match_parent"
         android:minWidth="48dp"
         android:minHeight="48dp"
@@ -49,17 +49,35 @@
         android:layout_gravity="end|center_vertical"
         android:focusable="false"/>
 
-    <com.android.systemui.statusbar.phone.StatusIconContainer
-        android:id="@+id/statusIcons"
-        android:layout_width="wrap_content"
+    <View
+        android:id="@+id/separator"
+        android:layout_width="0dp"
         android:layout_height="match_parent"
-        android:paddingEnd="@dimen/signal_cluster_battery_padding" />
+        android:layout_gravity="center"
+        android:layout_marginStart="8dp"
+        android:layout_marginEnd="8dp"
+        android:visibility="gone"
+        />
 
-    <com.android.systemui.BatteryMeterView
-        android:id="@+id/batteryRemainingIcon"
+    <LinearLayout
+        android:id="@+id/rightLayout"
+        android:layout_width="0dp"
         android:layout_height="match_parent"
-        android:layout_width="wrap_content"
-        systemui:textAppearance="@style/TextAppearance.QS.Status"
-        android:paddingEnd="2dp" />
+        android:layout_weight="1"
+        >
+        <com.android.systemui.statusbar.phone.StatusIconContainer
+            android:id="@+id/statusIcons"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:paddingEnd="@dimen/signal_cluster_battery_padding" />
 
+        <com.android.systemui.BatteryMeterView
+            android:id="@+id/batteryRemainingIcon"
+            android:layout_height="match_parent"
+            android:layout_width="0dp"
+            android:layout_weight="1"
+            systemui:textAppearance="@style/TextAppearance.QS.Status"
+            android:paddingEnd="2dp" />
+
+    </LinearLayout>
 </LinearLayout>
diff --git a/packages/SystemUI/res/layout/smart_action_button.xml b/packages/SystemUI/res/layout/smart_action_button.xml
index 2716034..488be3a 100644
--- a/packages/SystemUI/res/layout/smart_action_button.xml
+++ b/packages/SystemUI/res/layout/smart_action_button.xml
@@ -29,6 +29,8 @@
         android:textSize="@dimen/smart_reply_button_font_size"
         android:lineSpacingExtra="@dimen/smart_reply_button_line_spacing_extra"
         android:textColor="@color/smart_reply_button_text"
+        android:paddingLeft="@dimen/smart_reply_button_action_padding_left"
+        android:paddingRight="@dimen/smart_reply_button_padding_horizontal"
         android:drawablePadding="@dimen/smart_action_button_icon_padding"
         android:textStyle="normal"
         android:ellipsize="none"/>
diff --git a/packages/SystemUI/res/layout/smart_reply_button.xml b/packages/SystemUI/res/layout/smart_reply_button.xml
index 9faed18..ddf16e0 100644
--- a/packages/SystemUI/res/layout/smart_reply_button.xml
+++ b/packages/SystemUI/res/layout/smart_reply_button.xml
@@ -31,5 +31,7 @@
         android:textSize="@dimen/smart_reply_button_font_size"
         android:lineSpacingExtra="@dimen/smart_reply_button_line_spacing_extra"
         android:textColor="@color/smart_reply_button_text"
+        android:paddingLeft="@dimen/smart_reply_button_padding_horizontal"
+        android:paddingRight="@dimen/smart_reply_button_padding_horizontal"
         android:textStyle="normal"
         android:ellipsize="none"/>
diff --git a/packages/SystemUI/res/layout/smart_reply_view.xml b/packages/SystemUI/res/layout/smart_reply_view.xml
index 9fffc72..9d4d1db 100644
--- a/packages/SystemUI/res/layout/smart_reply_view.xml
+++ b/packages/SystemUI/res/layout/smart_reply_view.xml
@@ -24,8 +24,6 @@
     android:layout_height="wrap_content"
     android:layout_width="wrap_content"
     systemui:spacing="@dimen/smart_reply_button_spacing"
-    systemui:singleLineButtonPaddingHorizontal="@dimen/smart_reply_button_padding_horizontal_single_line"
-    systemui:doubleLineButtonPaddingHorizontal="@dimen/smart_reply_button_padding_horizontal_double_line"
     systemui:buttonStrokeWidth="@dimen/smart_reply_button_stroke_width">
     <!-- smart_reply_button(s) will be added here. -->
 </com.android.systemui.statusbar.policy.SmartReplyView>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer.xml b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
index c3c291b..412276d 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_footer.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
@@ -19,8 +19,8 @@
         xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:paddingStart="4dp"
-        android:paddingEnd="4dp"
+        android:paddingStart="16dp"
+        android:paddingEnd="16dp"
         android:visibility="gone">
     <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
         android:id="@+id/content"
diff --git a/packages/SystemUI/res/layout/wallet_empty_state.xml b/packages/SystemUI/res/layout/wallet_empty_state.xml
index 3ca0f73..cc2e781 100644
--- a/packages/SystemUI/res/layout/wallet_empty_state.xml
+++ b/packages/SystemUI/res/layout/wallet_empty_state.xml
@@ -16,7 +16,8 @@
 ** limitations under the License.
 */
 -->
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
     <LinearLayout
         android:id="@+id/wallet_empty_state"
         android:layout_width="match_parent"
@@ -35,6 +36,7 @@
             android:layout_height="28dp"
             android:layout_gravity="center"
             android:layout_marginBottom="8dp"
+            android:tint="?androidprv:attr/colorAccentPrimary"
             android:contentDescription="@null"
             android:scaleType="fitCenter"/>
         <TextView
diff --git a/packages/SystemUI/res/layout/wallet_fullscreen.xml b/packages/SystemUI/res/layout/wallet_fullscreen.xml
index bbb180f..d365aa3 100644
--- a/packages/SystemUI/res/layout/wallet_fullscreen.xml
+++ b/packages/SystemUI/res/layout/wallet_fullscreen.xml
@@ -16,6 +16,8 @@
 -->
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:clipChildren="false">
@@ -34,8 +36,8 @@
         android:orientation="vertical">
         <ImageView
             android:id="@+id/icon"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_width="@dimen/wallet_screen_header_view_size"
+            android:layout_height="@dimen/wallet_screen_header_view_size"
             android:layout_gravity="center_horizontal"
             android:layout_marginVertical="10dp"
             android:scaleType="center"
@@ -65,7 +67,7 @@
             android:paddingVertical="@dimen/wallet_button_vertical_padding"
             android:paddingHorizontal="@dimen/wallet_button_horizontal_padding"
             android:background="@drawable/wallet_action_button_bg"
-            android:textColor="@color/wallet_white"
+            android:textColor="?androidprv:attr/textColorPrimaryInverse"
             android:textAlignment="center"
             android:visibility="gone"/>
 
@@ -83,7 +85,7 @@
             android:paddingHorizontal="@dimen/wallet_button_horizontal_padding"
             android:background="@drawable/wallet_app_button_bg"
             android:text="@string/wallet_app_button_label"
-            android:textColor="@color/GM2_blue_600"
+            android:textColor="?androidprv:attr/colorAccentPrimary"
             android:textAlignment="center"
             android:layout_marginVertical="24dp"/>
 
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index d3ab5b3..e000ff4 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -289,11 +289,9 @@
     <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobiele warmkol afgeskakel."</string>
     <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobiele warmkol aangeskakel."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Uitsaai van skerm gestaak."</string>
-    <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
-    <skip />
+    <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Werkmodus is onderbreek."</string>
     <string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Werkmodus is aan."</string>
-    <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
-    <skip />
+    <string name="accessibility_quick_settings_work_mode_changed_off" msgid="2653550342355027441">"Werkmodus is verander na onderbreek."</string>
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Werkmodus is aangeskakel."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Databespaarder is afgeskakel."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Databespaarder is aangeskakel."</string>
@@ -412,8 +410,7 @@
     <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g>-limiet"</string>
     <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> waarskuwing"</string>
     <string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Werkprofiel"</string>
-    <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
-    <skip />
+    <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Onderbreek"</string>
     <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Aandbeligting"</string>
     <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Aan by sonsondergang"</string>
     <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Tot sonsopkoms"</string>
@@ -473,14 +470,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Volkome\nstilte"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Net\nprioriteit"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Net\nwekkers"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laai tans draadloos • Vol oor <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laai tans • Vol oor <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laai tans vinnig • Vol oor <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laai tans stadig • Vol oor <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Wissel gebruiker"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Wissel gebruiker, huidige gebruiker <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Huidige gebruiker <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +669,10 @@
     <string name="wallet_title" msgid="5369767670735827105">"Beursie"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"Wys alles"</string>
     <string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Ontsluit om te betaal"</string>
-    <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
-    <skip />
+    <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Nie opgestel nie"</string>
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Ontsluit om te gebruik"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Kon nie jou kaarte kry nie; probeer later weer"</string>
-    <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
-    <skip />
+    <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Sluitskerminstellings"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Werkprofiel"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Vliegtuigmodus"</string>
     <string name="add_tile" msgid="6239678623873086686">"Voeg teël by"</string>
@@ -871,8 +862,7 @@
     <string name="switch_bar_on" msgid="1770868129120096114">"Aan"</string>
     <string name="switch_bar_off" msgid="5669805115416379556">"Af"</string>
     <string name="tile_unavailable" msgid="3095879009136616920">"Onbeskikbaar"</string>
-    <!-- no translation found for tile_disabled (373212051546573069) -->
-    <skip />
+    <string name="tile_disabled" msgid="373212051546573069">"Gedeaktiveer"</string>
     <string name="nav_bar" msgid="4642708685386136807">"Navigasiebalk"</string>
     <string name="nav_bar_layout" msgid="4716392484772899544">"Uitleg"</string>
     <string name="left_nav_bar_button_type" msgid="2634852842345192790">"Ekstra linksknoppie-tipe"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 4dee085..e69ab3f 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -473,14 +473,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"ሙሉ ለሙሉ\nጸጥታ"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"ቅድሚያ ተሰጪ\nብቻ"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"ማንቂያዎች\nብቻ"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • በገመድ-አልባ ኃይል በመሙላት ላይ • በ<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ውስጥ ይሞላል"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ኃይል በመሙላት ላይ • በ<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ውስጥ ይሞላል"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • በፍጥነት ኃይልን በመሙላት ላይ • በ<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ውስጥ ይሞላል"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • በዝግታ ኃይልን በመሙላት ላይ • በ<xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> ውስጥ ይሞላል"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ተጠቃሚ ቀይር"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"ተጠቃሚ ይለውጡ፣ የአሁን ተጠቃሚ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"የአሁን ተጠቃሚ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index dd342ff..7c8d105 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -475,14 +475,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Potpuna\ntišina"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Samo\npriorit. prekidi"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Samo\nalarmi"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Bežično se puni • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do kraja punjenja"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Puni se • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do kraja punjenja"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Brzo se puni • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do kraja punjenja"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Sporo se puni • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do kraja punjenja"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Zameni korisnika"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Promenite korisnika, aktuelni korisnik je <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Aktuelni korisnik <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index adbef20..73efaed 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -473,14 +473,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"একদম\nনিরব"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"শুধুমাত্র\nঅগ্রাধিকার"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"শুধুমাত্র\nঅ্যালার্মগুলি"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ওয়্যারলেস পদ্ধতিতে চার্জ হচ্ছে • পুরো চার্জ হতে <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> লাগবে"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • চার্জিং • পুরো চার্জ হতে <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> লাগবে"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • দ্রুত চার্জ হচ্ছে • পুরো চার্জ হতে <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> লাগবে"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ধীরে চার্জ হচ্ছে • পুরো চার্জ হতে <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> লাগবে"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ব্যবহারকারী পাল্টে দিন"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"ব্যবহারকারী পাল্টান, বর্তমান ব্যবহারকারী <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"<xliff:g id="CURRENT_USER_NAME">%s</xliff:g> হল বর্তমান ব্যবহারকারী"</string>
@@ -750,14 +746,10 @@
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;স্ট্যাটাস:&lt;/b&gt; লেভেল কমিয়ে সাইলেন্ করা হয়েছে"</string>
     <string name="notification_channel_summary_automatic_promoted" msgid="1301710305149590426">"&lt;b&gt;স্ট্যাটাস:&lt;/b&gt; র‍্যাঙ্ক বেড়ে গেছে"</string>
     <string name="notification_channel_summary_automatic_demoted" msgid="1831303964660807700">"&lt;b&gt;স্ট্যাটাস:&lt;/b&gt; র‍্যাঙ্ক কমে গেছে"</string>
-    <!-- no translation found for notification_channel_summary_priority_baseline (46674690072551234) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_priority_bubble (1275413109619074576) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_priority_dnd (6665395023264154361) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_priority_all (7151752959650048285) -->
-    <skip />
+    <string name="notification_channel_summary_priority_baseline" msgid="46674690072551234">"কথোপকথনের বিজ্ঞপ্তির উপরের দিকে এবং প্রোফাইল ছবি হিসেবে লক স্ক্রিনে দেখানো হয়"</string>
+    <string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"কথোপকথনের বিজ্ঞপ্তির উপরের দিকে এবং প্রোফাইল ছবি হিসেবে লক স্ক্রিনে দেখানো হয়, বাবল হিসেবেও এটি দেখা যায়"</string>
+    <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"কথোপকথনের বিজ্ঞপ্তির উপরের দিকে এবং প্রোফাইল ছবি হিসেবে লক স্ক্রিনে দেখানো হয় এবং এর ফলে \'বিরক্ত করবে না\' মোডে কাজ করতে অসুবিধা হয়"</string>
+    <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"কথোপকথনের বিজ্ঞপ্তির উপরের দিকে এবং প্রোফাইল ছবি হিসেবে লক স্ক্রিনে দেখানো হয়, বাবল হিসেবেও এটি দেখা যায় এবং এর ফলে \'বিরক্ত করবে না\' মোডে কাজ করতে অসুবিধা হয়"</string>
     <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"সেটিংস"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"অগ্রাধিকার"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g>-এ কথোপকথন ফিচার কাজ করে না"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index a54dffe..5c9ec89 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -475,14 +475,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Potpuna\ntišina"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Samo\nprioritetni prekidi"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Samo\nalarmi"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Bežično punjenje • Potpuna napunjenost za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Punjenje • Potpuna napunjenost za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Brzo punjenje • Potpuna napunjenost za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Sporo punjenje • Potpuna napunjenost za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Zamijeni korisnika"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Zamijeni korisnika. Trenutni korisnik je <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Trenutni korisnik <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 803bbd1..fc3b2f5 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -473,14 +473,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Silenci\ntotal"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Només\ninterr. prior."</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Només\nalarmes"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregant sense fil • Es completarà d\'aquí a <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • S\'està carregant • Es completarà d\'aquí a <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregant ràpidament • Es completarà d\'aquí a <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregant lentament • Es completarà d\'aquí a <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Canvia d\'usuari"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Canvia d\'usuari. Usuari actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Usuari actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 8ed760a..4560cad 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -477,14 +477,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Úplné\nticho"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Pouze\nprioritní"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Pouze\nbudíky"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Bezdrátové nabíjení • Plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíjení • Plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Rychlé nabíjení • Plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Pomalé nabíjení • Plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Přepnout uživatele"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Přepnout uživatele, aktuální uživatel: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Aktuální uživatel <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index c76ebc3..a30e0636 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -289,11 +289,9 @@
     <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobilhotspot er slået fra."</string>
     <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobilhotspot er slået til."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Casting af din skærm er stoppet."</string>
-    <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
-    <skip />
+    <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Arbejdstilstand er på pause."</string>
     <string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Arbejdstilstand er slået til."</string>
-    <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
-    <skip />
+    <string name="accessibility_quick_settings_work_mode_changed_off" msgid="2653550342355027441">"Arbejdstilstand er sat på pause."</string>
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Arbejdstilstand er slået til."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Datasparefunktionen er slået fra."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Datasparefunktionen er aktiveret."</string>
@@ -412,8 +410,7 @@
     <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Grænse: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Advarsel ved <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Arbejdsprofil"</string>
-    <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
-    <skip />
+    <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Sat på pause"</string>
     <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Nattelys"</string>
     <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Tænd ved solnedgang"</string>
     <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Indtil solopgang"</string>
@@ -473,14 +470,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Total\nstilhed"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Kun\nprioritet"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Kun\nalarmer"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Trådløs opladning • Fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Oplader • Fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Oplader hurtigt • Fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Oplader langsomt • Fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Skift bruger"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Skift bruger. Nuværende bruger er <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Nuværende bruger: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +669,10 @@
     <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"Vis alle"</string>
     <string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Lås op for at betale"</string>
-    <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
-    <skip />
+    <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Ikke konfigureret"</string>
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Lås op for at bruge"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Dine kort kunne ikke hentes. Prøv igen senere."</string>
-    <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
-    <skip />
+    <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Lås skærmindstillinger"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Arbejdsprofil"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Flytilstand"</string>
     <string name="add_tile" msgid="6239678623873086686">"Tilføj et felt"</string>
@@ -871,8 +862,7 @@
     <string name="switch_bar_on" msgid="1770868129120096114">"Til"</string>
     <string name="switch_bar_off" msgid="5669805115416379556">"Fra"</string>
     <string name="tile_unavailable" msgid="3095879009136616920">"Ikke tilgængelig"</string>
-    <!-- no translation found for tile_disabled (373212051546573069) -->
-    <skip />
+    <string name="tile_disabled" msgid="373212051546573069">"Deaktiveret"</string>
     <string name="nav_bar" msgid="4642708685386136807">"Navigationslinje"</string>
     <string name="nav_bar_layout" msgid="4716392484772899544">"Layout"</string>
     <string name="left_nav_bar_button_type" msgid="2634852842345192790">"Ekstra venstre knaptype"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 3da19fc..fd8af9d 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -473,14 +473,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Πλήρης\nσίγαση"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Μόνο\nπροτεραιότητας"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Μόνο\nειδοποιήσεις"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ασύρματη φόρτιση • Πλήρης φόρτιση σε <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Φόρτιση • Πλήρης φόρτιση σε <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Γρήγορη φόρτιση • Πλήρης φόρτιση σε <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Αργή φόρτιση • Πλήρης φόρτιση σε <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Εναλλαγή χρήστη"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Εναλλαγή χρήστη, τρέχων χρήστης <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Τρέχων χρήστης <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 15347dd..58a31df 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -289,11 +289,9 @@
     <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Zona móvil desactivada"</string>
     <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Zona móvil activada"</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Transmisión de pantalla detenida"</string>
-    <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
-    <skip />
+    <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Modo de trabajo pausado."</string>
     <string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Modo de trabajo activado"</string>
-    <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
-    <skip />
+    <string name="accessibility_quick_settings_work_mode_changed_off" msgid="2653550342355027441">"Se pausó el modo de trabajo."</string>
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Se activó el modo de trabajo."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Se desactivó el Ahorro de datos."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Se activó el Ahorro de datos."</string>
@@ -412,8 +410,7 @@
     <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Límite de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Advertencia de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Perfil de trabajo"</string>
-    <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
-    <skip />
+    <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Pausado"</string>
     <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Luz nocturna"</string>
     <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Al atardecer"</string>
     <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Hasta el amanecer"</string>
@@ -473,14 +470,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Silencio\ntotal"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Solo\nprioridad"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Solo\nalarmas"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando de manera inalámbrica • Se completará en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando • Se completará en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando rápido • Se completará en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando lento • Se completará en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar usuario"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Cambiar de usuario (usuario actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"El usuario actual es <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +669,10 @@
     <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"Mostrar todo"</string>
     <string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Desbloquear para pagar"</string>
-    <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
-    <skip />
+    <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Sin configurar"</string>
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Ocurrió un problema al obtener las tarjetas; vuelve a intentarlo más tarde"</string>
-    <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
-    <skip />
+    <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configuración de pantalla de bloqueo"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabajo"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Modo de avión"</string>
     <string name="add_tile" msgid="6239678623873086686">"Agregar mosaico"</string>
@@ -871,8 +862,7 @@
     <string name="switch_bar_on" msgid="1770868129120096114">"Activado"</string>
     <string name="switch_bar_off" msgid="5669805115416379556">"Desactivado"</string>
     <string name="tile_unavailable" msgid="3095879009136616920">"No disponible"</string>
-    <!-- no translation found for tile_disabled (373212051546573069) -->
-    <skip />
+    <string name="tile_disabled" msgid="373212051546573069">"Inhabilitada"</string>
     <string name="nav_bar" msgid="4642708685386136807">"Barra de navegación"</string>
     <string name="nav_bar_layout" msgid="4716392484772899544">"Diseño"</string>
     <string name="left_nav_bar_button_type" msgid="2634852842345192790">"Tipo de botón izquierdo adicional"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 57ce1c5..a863111 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -473,14 +473,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Silencio\ntotal"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Solo\ncon prioridad"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Solo\nalarmas"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga sin cables • En <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> terminará de cargarse"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando • En <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> terminará de cargarse"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga rápida • En <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> terminará de cargarse"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga lenta • En <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> terminará de cargarse"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar de usuario"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Cambiar de usuario (usuario actual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Usuario actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 85679b9..932ce3e 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -473,14 +473,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Isiltasun\nosoa"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Lehentasunezkoak\nsoilik"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Alarmak\nsoilik"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Hari gabe kargatzen • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> guztiz kargatu arte"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Kargatzen • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> guztiz kargatu arte"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Bizkor kargatzen • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> guztiz kargatu arte"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Mantso kargatzen • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> guztiz kargatu arte"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Aldatu erabiltzailea"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Aldatu erabiltzailea. <xliff:g id="CURRENT_USER_NAME">%s</xliff:g> da saioa hasita daukana."</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Erabiltzailea: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 052a87a..89b4fbd 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -289,11 +289,9 @@
     <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"نقطه اتصال دستگاه همراه خاموش شد."</string>
     <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"نقطه اتصال دستگاه همراه روشن شد."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"فرستادن صفحه نمایش متوقف شد."</string>
-    <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
-    <skip />
+    <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"حالت کار موقتاً متوقف شده است."</string>
     <string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"حالت کار روشن."</string>
-    <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
-    <skip />
+    <string name="accessibility_quick_settings_work_mode_changed_off" msgid="2653550342355027441">"حالت کار موقتاً متوقف شد."</string>
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"حالت کار روشن شد."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"صرفه‌جویی داده خاموش شد."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"صرفه‌جویی داده روشن شد."</string>
@@ -412,8 +410,7 @@
     <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> محدودیت"</string>
     <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"هشدار <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="2754212289804324685">"نمایه کاری"</string>
-    <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
-    <skip />
+    <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"موقتاً متوقف"</string>
     <string name="quick_settings_night_display_label" msgid="8180030659141778180">"نور شب"</string>
     <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"غروب روشن می‌شود"</string>
     <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"تا طلوع"</string>
@@ -473,14 +470,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"سکوت\nکامل"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"فقط\nاولویت‌دار"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"فقط\nهشدارها"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • درحال شارژ کردن بی‌سیم • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> تا شارژ کامل"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • درحال شارژ کردن • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> تا شارژ کامل"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • درحال شارژ کردن سریع • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> تا شارژ کامل"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • درحال شارژ کردن آهسته • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> تا شارژ کامل"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"تغییر کاربر"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"تعویض کاربر، کاربر کنونی <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"کاربر کنونی <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +669,10 @@
     <string name="wallet_title" msgid="5369767670735827105">"کیف‌پول"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"نمایش همه"</string>
     <string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"باز کردن قفل برای پرداخت"</string>
-    <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
-    <skip />
+    <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"تنظیم‌نشده"</string>
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"برای استفاده، قفل را باز کنید"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"هنگام دریافت کارت‌ها مشکلی پیش آمد، لطفاً بعداً دوباره امتحان کنید"</string>
-    <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
-    <skip />
+    <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"تنظیمات صفحه قفل"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"نمایه کاری"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"حالت هواپیما"</string>
     <string name="add_tile" msgid="6239678623873086686">"افزودن کاشی"</string>
@@ -871,8 +862,7 @@
     <string name="switch_bar_on" msgid="1770868129120096114">"روشن"</string>
     <string name="switch_bar_off" msgid="5669805115416379556">"خاموش"</string>
     <string name="tile_unavailable" msgid="3095879009136616920">"در دسترس نیست"</string>
-    <!-- no translation found for tile_disabled (373212051546573069) -->
-    <skip />
+    <string name="tile_disabled" msgid="373212051546573069">"غیرفعال"</string>
     <string name="nav_bar" msgid="4642708685386136807">"نوار پیمایش"</string>
     <string name="nav_bar_layout" msgid="4716392484772899544">"طرح‌بندی"</string>
     <string name="left_nav_bar_button_type" msgid="2634852842345192790">"نوع دکمه منتهی‌الیه چپ"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index efe1833..a9acd50 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -473,14 +473,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Aucune\ninterruption"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Priorité\nuniquement"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Alarmes\nuniquement"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge sans fil • Chargé dans <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge • Chargé dans <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge rapide • Chargé dans <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Recharge lente • Chargé dans <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Changer d\'utilisateur"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Changer d\'utilisateur (utilisateur actuel : <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Utilisateur actuel : <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index d5da5d5..083435b 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -473,14 +473,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Silencio\ntotal"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Só\nprioridade"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Só\nalarmas"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando sen fíos • A carga completarase en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando • A carga completarase en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando rapidamente • A carga completarase en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando lentamente • A carga completarase en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar usuario"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Cambiar usuario, usuario actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Usuario actual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 7e92ccf..99d3af6 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -750,14 +750,10 @@
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;સ્ટેટસ:&lt;/b&gt; સાઇલન્ટ પર અવનત કરવામાં આવ્યું"</string>
     <string name="notification_channel_summary_automatic_promoted" msgid="1301710305149590426">"&lt;b&gt;સ્ટેટસ:&lt;/b&gt; ઉપલી રેંક આપવામાં આવી"</string>
     <string name="notification_channel_summary_automatic_demoted" msgid="1831303964660807700">"&lt;b&gt;સ્ટેટસ:&lt;/b&gt; નીચલી રેંક આપવામાં આવી"</string>
-    <!-- no translation found for notification_channel_summary_priority_baseline (46674690072551234) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_priority_bubble (1275413109619074576) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_priority_dnd (6665395023264154361) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_priority_all (7151752959650048285) -->
-    <skip />
+    <string name="notification_channel_summary_priority_baseline" msgid="46674690072551234">"વાતચીતના નોટિફિકેશન વિભાગની ટોચ પર અને લૉક કરેલી સ્ક્રીન પર પ્રોફાઇલ ફોટો તરીકે બતાવે છે"</string>
+    <string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"વાતચીતના નોટિફિકેશન વિભાગની ટોચ પર અને લૉક કરેલી સ્ક્રીન પર પ્રોફાઇલ ફોટો તરીકે બતાવે છે, બબલ તરીકે દેખાય છે"</string>
+    <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"વાતચીતના નોટિફિકેશન વિભાગની ટોચ પર અને લૉક કરેલી સ્ક્રીન પર પ્રોફાઇલ ફોટો તરીકે બતાવે છે, ખલેલ પાડશો નહીં મોડમાં વિક્ષેપ ઊભો કરે છે"</string>
+    <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"વાતચીતના નોટિફિકેશન વિભાગની ટોચ પર અને લૉક કરેલી સ્ક્રીન પર પ્રોફાઇલ ફોટો તરીકે બતાવે છે, બબલ તરીકે દેખાય છે, ખલેલ પાડશો નહીં મોડમાં વિક્ષેપ ઊભો કરે છે"</string>
     <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"સેટિંગ"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"પ્રાધાન્યતા"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> વાતચીતની સુવિધાઓને સપોર્ટ આપતી નથી"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index cfe2079..b526ff6 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -473,14 +473,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"पूरी तरह\nशांत"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"केवल\nप्राथमिकता"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"केवल\nअलार्म"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • वायरलेस तरीके से चार्ज हो रहा है • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> में पूरा चार्ज हो जाएगा"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • चार्ज हो रहा है • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> में पूरा चार्ज हो जाएगा"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • तेज़ चार्ज हो रहा है • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> में पूरा चार्ज हो जाएगा"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • धीरे चार्ज हो रहा है • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> में पूरा चार्ज हो जाएगा"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"उपयोगकर्ता बदलें"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"उपयोगकर्ता बदलें, मौजूदा उपयोगकर्ता <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"मौजूदा उपयोगकर्ता <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 9246023..fedeb77 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -475,14 +475,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Potpuna\ntišina"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Samo\nprioritetno"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Samo\nalarmi"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • bežično punjenje • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do napunjenosti"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • punjenje • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do napunjenosti"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • brzo punjenje • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do napunjenosti"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • sporo punjenje • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> do napunjenosti"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Promjena korisnika"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Promjena korisnika, trenutačni korisnik <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Trenutačan korisnik <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 3cfbaba..d3bd516 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -473,14 +473,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Teljes\nnémítás"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Csak\nprioritás"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Csak\nriasztások"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Vezeték nélküli töltés • A teljes töltöttségig: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Töltés • A teljes töltöttségig: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Gyors töltés • A teljes töltöttségig: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lassú töltés • A teljes töltöttségig: <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Felhasználóváltás"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Felhasználóváltás (a jelenlegi felhasználó: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Jelenlegi felhasználó (<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 4295b96..22feecfc 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -473,14 +473,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Algjör\nþögn"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Aðeins\nforgangur"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Aðeins\nvekjarar"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Þráðlaus hleðsla • Full hleðsla eftir <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Í hleðslu • Full hleðsla eftir <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Hraðhleðsla • Full hleðsla eftir <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Hæg hleðsla • Full hleðsla eftir <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Skipta um notanda"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Skipta um notanda; núverandi notandi er <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Núverandi notandi er <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 78b85c9..6cc699b 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -473,14 +473,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Silenzio\ntotale"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Solo con\npriorità"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Solo\nsveglie"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • In carica wireless • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> alla ricarica completa"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • In carica • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> alla ricarica completa"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ricarica veloce • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> alla ricarica completa"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ricarica lenta • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> alla ricarica completa"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambio utente"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Cambia utente, utente corrente <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Utente corrente <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 39abcde..0e331de 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -473,14 +473,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"サイレント\n"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"重要な\n通知のみ"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"アラーム\nのみ"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ワイヤレス充電中 • 完了まで <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 充電中 • 完了まで <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 急速充電中 • 完了まで <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • 低速充電中 • 完了まで <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ユーザーを切り替える"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"ユーザーを切り替える、現在のユーザーは<xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"現在のユーザー: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 78f228a..fddf305 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -473,14 +473,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"ស្ងៀមស្ងាត់\nទាំងស្រុង"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"អាទិភាព\nប៉ុណ្ណោះ"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"សំឡេងរោទ៍\nប៉ុណ្ណោះ"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • កំពុងសាកថ្មឥតខ្សែ • ពេញក្នុងរយៈពេល <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • កំពុងសាកថ្ម • ពេញក្នុងរយៈពេល <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • កំពុង​សាកថ្មយ៉ាង​ឆាប់រហ័ស • ពេញក្នុងរយៈពេល <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • កំពុង​សាកថ្ម​យឺត • ពេញក្នុងរយៈពេល <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ប្ដូរ​អ្នក​ប្រើ"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"ប្ដូរ​អ្នកប្រើ ​អ្នកប្រើ​បច្ចុប្បន្ន <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"អ្នកប្រើបច្ចុប្បន្ន <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 3cb20af..8d542ddf 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -289,11 +289,9 @@
     <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"ಮೊಬೈಲ್ ಹಾಟ್‌ಸ್ಪಾಟ್ ಆಫ್ ಮಾಡಲಾಗಿದೆ."</string>
     <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"ಮೊಬೈಲ್ ಹಾಟ್‌ಸ್ಪಾಟ್ ಆನ್ ಮಾಡಲಾಗಿದೆ."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"ಸ್ಕ್ರೀನ್ ಪ್ರಸಾರವನ್ನು ನಿಲ್ಲಿಸಲಾಗಿದೆ."</string>
-    <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
-    <skip />
+    <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"ಕೆಲಸದ ಮೋಡ್ ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ."</string>
     <string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"ಕೆಲಸದ ಮೋಡ್ ಆನ್ ಆಗಿದೆ."</string>
-    <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
-    <skip />
+    <string name="accessibility_quick_settings_work_mode_changed_off" msgid="2653550342355027441">"ಕೆಲಸದ ಮೋಡ್ ಅನ್ನು ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ."</string>
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"ಕೆಲಸದ ಮೋಡ್ ಆನ್ ಮಾಡಲಾಗಿದೆ."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"ಡೇಟಾ ಸೇವರ್ ಆಫ್ ಮಾಡಲಾಗಿದೆ."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"ಡೇಟಾ ಸೇವರ್ ಆನ್ ಮಾಡಲಾಗಿದೆ."</string>
@@ -412,8 +410,7 @@
     <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ಮಿತಿ"</string>
     <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> ಎಚ್ಚರಿಕೆ"</string>
     <string name="quick_settings_work_mode_label" msgid="2754212289804324685">"ಕೆಲಸದ ಪ್ರೊಫೈಲ್"</string>
-    <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
-    <skip />
+    <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ"</string>
     <string name="quick_settings_night_display_label" msgid="8180030659141778180">"ನೈಟ್ ಲೈಟ್"</string>
     <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"ಸೂರ್ಯಾಸ್ತದಲ್ಲಿ"</string>
     <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"ಸೂರ್ಯೋದಯದವರೆಗೆ"</string>
@@ -676,12 +673,10 @@
     <string name="wallet_title" msgid="5369767670735827105">"ವಾಲೆಟ್"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"ಎಲ್ಲವನ್ನೂ ತೋರಿಸಿ"</string>
     <string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"ಪಾವತಿಸಲು ಅನ್‌ಲಾಕ್ ಮಾಡಿ"</string>
-    <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
-    <skip />
+    <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"ಇನ್ನೂ ಸೆಟಪ್‌ ಮಾಡಿಲ್ಲ"</string>
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"ಬಳಸಲು ಅನ್‌ಲಾಕ್ ಮಾಡಿ"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"ನಿಮ್ಮ ಕಾರ್ಡ್‌ಗಳನ್ನು ಪಡೆಯುವಾಗ ಸಮಸ್ಯೆ ಉಂಟಾಗಿದೆ, ನಂತರ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ"</string>
-    <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
-    <skip />
+    <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"ಲಾಕ್ ಸ್ಕ್ರ್ರೀನ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"ಕೆಲಸದ ಪ್ರೊಫೈಲ್"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"ಏರ್‌ಪ್ಲೇನ್ ಮೋಡ್"</string>
     <string name="add_tile" msgid="6239678623873086686">"ಟೈಲ್ ಸೇರಿಸಿ"</string>
@@ -871,8 +866,7 @@
     <string name="switch_bar_on" msgid="1770868129120096114">"ಆನ್"</string>
     <string name="switch_bar_off" msgid="5669805115416379556">"ಆಫ್"</string>
     <string name="tile_unavailable" msgid="3095879009136616920">"ಲಭ್ಯವಿಲ್ಲ"</string>
-    <!-- no translation found for tile_disabled (373212051546573069) -->
-    <skip />
+    <string name="tile_disabled" msgid="373212051546573069">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
     <string name="nav_bar" msgid="4642708685386136807">"ನ್ಯಾವಿಗೇಷನ್ ಬಾರ್"</string>
     <string name="nav_bar_layout" msgid="4716392484772899544">"ಲೇಔಟ್"</string>
     <string name="left_nav_bar_button_type" msgid="2634852842345192790">"ಹೆಚ್ಚುವರಿ ಎಡ ಬಟನ್ ವಿಧ"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 91bdc6e..5d70e13 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -473,14 +473,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"ຄວາມ​ງຽບ\nທັງ​ໝົດ"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"ບຸ​ລິ​ມະ​ສິດ\nເທົ່າ​ນັ້ນ"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"ໂມງ​ປຸກ\nເທົ່າ​ນັ້ນ"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ກຳລັງສາກໄຟແບບໄຮ້ສາຍ • ຈະເຕັມໃນອີກ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ກຳລັງສາກໄຟ • ຈະເຕັມໃນອີກ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ກຳລັງສາກໄຟແບບໄວ • ຈະເຕັມໃນອີກ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ກຳລັງສາກໄຟແບບຊ້າ • ຈະເຕັມໃນອີກ <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ສະຫຼັບຜູ້ໃຊ້"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"ປ່ຽນຜູ່ໃຊ້, ຜູ່ໃຊ້ປະຈຸບັນ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"ຜູ້ໃຊ້ປະຈຸບັນ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 407db29..fdb8c10 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -477,14 +477,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Visiška\ntyla"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Tik\nprioritetiniai"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Tik\nsignalai"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Įkraunama be laidų • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> iki visiško įkrovimo"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Įkraunama • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> iki visiško įkrovimo"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Sparčiai įkraunama • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> iki visiško įkrovimo"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lėtai įkraunama • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> iki visiško įkrovimo"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Perjungti naudotoją"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Perjungti naudotoją, dabartinis naudotojas <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Dabartinis naudotojas <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index c4524f3..fdb76bb 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -473,14 +473,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Целосна\nтишина"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Само\nприоритетни"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Само\nаларми"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Се полни безжично • Полна по <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Се полни • Полна по <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Се полни брзо • Полна по <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Се полни бавно • Полна по <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Промени го корисникот"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Промени го корисникот, тековен корисник <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Тековен корисник <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 7001a1a..8d3dffd 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -473,14 +473,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Дуугүй\nболгох"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Зөвхөн\nхамгийн чухлыг"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Зөвхөн\nсэрүүлэг"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Утасгүй цэнэглэж байна • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-н дараа дүүрнэ"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Цэнэглэж байна • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-н дараа дүүрнэ"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Хурдтай цэнэглэж байна • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-н дараа дүүрнэ"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Удаан цэнэглэж байна • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>-н дараа дүүрнэ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Хэрэглэгчийг сэлгэх"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Хэрэглэгчийг сэлгэх, одоогийн хэрэглэгч <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Одоогийн хэрэглэгч <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 837d900..267b74a 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -289,11 +289,9 @@
     <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Tempat liputan mudah alih bergerak dimatikan."</string>
     <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Tempat liputan mudah alih bergerak dihidupkan."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Penghantaran skrin dihentikan."</string>
-    <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
-    <skip />
+    <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Mod kerja dijeda."</string>
     <string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Mod kerja hidup."</string>
-    <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
-    <skip />
+    <string name="accessibility_quick_settings_work_mode_changed_off" msgid="2653550342355027441">"Mod kerja dijeda."</string>
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Mod kerja dihidupkan."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Penjimat Data dimatikan."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Penjimat Data dihidupkan."</string>
@@ -412,8 +410,7 @@
     <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> had"</string>
     <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Amaran <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Profil kerja"</string>
-    <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
-    <skip />
+    <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Dijeda"</string>
     <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Cahaya Malam"</string>
     <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Dihidupkan pd senja"</string>
     <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Hingga matahari terbit"</string>
@@ -676,12 +673,10 @@
     <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"Tunjukkan semua"</string>
     <string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Buka kunci untuk membayar"</string>
-    <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
-    <skip />
+    <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Tidak disediakan"</string>
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Buka kunci untuk menggunakan"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Terdapat masalah sewaktu mendapatkan kad anda. Sila cuba sebentar lagi"</string>
-    <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
-    <skip />
+    <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Tetapan skrin kunci"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Profil kerja"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Mod pesawat"</string>
     <string name="add_tile" msgid="6239678623873086686">"Tambahkan jubin"</string>
@@ -871,8 +866,7 @@
     <string name="switch_bar_on" msgid="1770868129120096114">"Hidup"</string>
     <string name="switch_bar_off" msgid="5669805115416379556">"Mati"</string>
     <string name="tile_unavailable" msgid="3095879009136616920">"Tidak tersedia"</string>
-    <!-- no translation found for tile_disabled (373212051546573069) -->
-    <skip />
+    <string name="tile_disabled" msgid="373212051546573069">"Dilumpuhkan"</string>
     <string name="nav_bar" msgid="4642708685386136807">"Bar navigasi"</string>
     <string name="nav_bar_layout" msgid="4716392484772899544">"Reka letak"</string>
     <string name="left_nav_bar_button_type" msgid="2634852842345192790">"Jenis butang kiri tambahan"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index fa6e62e..cf82ea4 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -473,14 +473,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Total\nstillhet"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Bare\nPrioritet"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Bare\nalarmer"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lader trådløst • Fulladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lader • Fulladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lader raskt • Fulladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Lader sakte • Fulladet om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Bytt bruker"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Bytt bruker, gjeldende bruker er <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Gjeldende bruker: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 26412ce..be1735c 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -289,11 +289,9 @@
     <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobiele hotspot staat uit."</string>
     <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobiele hotspot staat aan."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Casten van scherm gestopt."</string>
-    <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
-    <skip />
+    <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Werkmodus is onderbroken."</string>
     <string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Werkmodus aan."</string>
-    <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
-    <skip />
+    <string name="accessibility_quick_settings_work_mode_changed_off" msgid="2653550342355027441">"Werkmodus is uitgezet."</string>
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Werkmodus staat aan."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Databesparing staat uit."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Databesparing staat aan."</string>
@@ -412,8 +410,7 @@
     <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limiet van <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Waarschuwing voor <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Werkprofiel"</string>
-    <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
-    <skip />
+    <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Onderbroken"</string>
     <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Nachtverlichting"</string>
     <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Aan bij zonsondergang"</string>
     <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Tot zonsopgang"</string>
@@ -473,14 +470,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Totale\nstilte"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Alleen\nprioriteit"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Alleen\nalarmen"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Draadloos opladen • Vol over <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Opladen • Vol over <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Snel opladen • Vol over <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Langzaam opladen • Vol over <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Gebruiker wijzigen"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Schakelen tussen gebruikers, huidige gebruiker <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Huidige gebruiker <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +669,10 @@
     <string name="wallet_title" msgid="5369767670735827105">"Wallet"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"Alles tonen"</string>
     <string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Ontgrendelen om te betalen"</string>
-    <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
-    <skip />
+    <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Niet ingesteld"</string>
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Ontgrendelen om te gebruiken"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Er is een probleem opgetreden bij het ophalen van je kaarten. Probeer het later opnieuw."</string>
-    <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
-    <skip />
+    <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Instellingen voor vergrendelscherm"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Werkprofiel"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Vliegtuigmodus"</string>
     <string name="add_tile" msgid="6239678623873086686">"Tegel toevoegen"</string>
@@ -871,8 +862,7 @@
     <string name="switch_bar_on" msgid="1770868129120096114">"Aan"</string>
     <string name="switch_bar_off" msgid="5669805115416379556">"Uit"</string>
     <string name="tile_unavailable" msgid="3095879009136616920">"Niet beschikbaar"</string>
-    <!-- no translation found for tile_disabled (373212051546573069) -->
-    <skip />
+    <string name="tile_disabled" msgid="373212051546573069">"Uit"</string>
     <string name="nav_bar" msgid="4642708685386136807">"Navigatiebalk"</string>
     <string name="nav_bar_layout" msgid="4716392484772899544">"Lay-out"</string>
     <string name="left_nav_bar_button_type" msgid="2634852842345192790">"Extra knoptype links"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 268c245..9a2cd80 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -473,14 +473,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"ସମ୍ପୂର୍ଣ୍ଣ\nନୀରବ"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"କେବଳ\nପ୍ରାଥମିକତା"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"କେବଳ\nଆଲାର୍ମ"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ୱାୟାରଲେସ୍ ଭାବେ ଚାର୍ଜ ହେଉଛି • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ରେ ସମ୍ପୂର୍ଣ୍ଣ ହେବ"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ଚାର୍ଜ ହେଉଛି • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ରେ ସମ୍ପୂର୍ଣ୍ଣ ହେବ"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ଶୀଘ୍ର ଚାର୍ଜ ହେଉଛି • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ରେ ସମ୍ପୂର୍ଣ୍ଣ ହେବ"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • ଧୀରେ ଚାର୍ଜ ହେଉଛି • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>ରେ ସମ୍ପୂର୍ଣ୍ଣ ହେବ"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"ୟୁଜର୍‍ ବଦଳାନ୍ତୁ"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"ୟୁଜର୍‍ ବଦଳାନ୍ତୁ, ବର୍ତ୍ତମାନର ୟୁଜର୍‍ ହେଉଛନ୍ତି <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"ବର୍ତ୍ତମାନର ୟୁଜର୍‍ ହେଉଛନ୍ତି <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -750,14 +746,10 @@
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;ସ୍ଥିତି:&lt;/b&gt; ନୀରବକୁ ଡିମୋଟ୍ କରାଯାଇଛି"</string>
     <string name="notification_channel_summary_automatic_promoted" msgid="1301710305149590426">"&lt;b&gt;ସ୍ଥିତି:&lt;/b&gt; ରେଙ୍କ ଉପରକୁ କରାଯାଇଛି"</string>
     <string name="notification_channel_summary_automatic_demoted" msgid="1831303964660807700">"&lt;b&gt;ସ୍ଥିତି:&lt;/b&gt; ରେଙ୍କ ତଳକୁ କରାଯାଇଛି"</string>
-    <!-- no translation found for notification_channel_summary_priority_baseline (46674690072551234) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_priority_bubble (1275413109619074576) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_priority_dnd (6665395023264154361) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_priority_all (7151752959650048285) -->
-    <skip />
+    <string name="notification_channel_summary_priority_baseline" msgid="46674690072551234">"ବାର୍ତ୍ତାଳାପ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକର ଶୀର୍ଷରେ ଏବଂ ଲକ୍ ସ୍କ୍ରିନରେ ଏକ ପ୍ରୋଫାଇଲ୍ ଛବି ଭାବେ ଦେଖାଏ"</string>
+    <string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"ବାର୍ତ୍ତାଳାପ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକର ଶୀର୍ଷରେ ଏବଂ ଲକ୍ ସ୍କ୍ରିନରେ ଏକ ପ୍ରୋଫାଇଲ୍ ଛବି ଭାବେ ଦେଖାଏ, ଏକ ବବଲ୍ ଭାବେ ଦେଖାଯାଏ"</string>
+    <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"ବାର୍ତ୍ତାଳାପ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକର ଶୀର୍ଷରେ ଏବଂ ଲକ୍ ସ୍କ୍ରିନରେ ଏକ ପ୍ରୋଫାଇଲ୍ ଛବି ଭାବେ ଦେଖାଏ, \'ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\'କୁ ବାଧା ଦିଏ"</string>
+    <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"ବାର୍ତ୍ତାଳାପ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକର ଶୀର୍ଷରେ ଏବଂ ଲକ୍ ସ୍କ୍ରିନରେ ଏକ ପ୍ରୋଫାଇଲ୍ ଛବି ଭାବେ ଦେଖାଏ, ଏକ ବବଲ୍ ଭାବେ ଦେଖାଯାଏ, \'ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\'କୁ ବାଧା ଦିଏ"</string>
     <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"ସେଟିଂସ୍"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"ପ୍ରାଥମିକତା"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ବାର୍ତ୍ତାଳାପ ଫିଚରଗୁଡ଼ିକୁ ସମର୍ଥନ କରେ ନାହିଁ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 635cb2f..bc9b2ee 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -750,14 +750,10 @@
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;ਸਥਿਤੀ:&lt;/b&gt; ਦਰਜਾ ਘਟਾ ਕੇ ਸ਼ਾਂਤ \'ਤੇ ਸੈੱਟ ਕੀਤਾ ਗਿਆ"</string>
     <string name="notification_channel_summary_automatic_promoted" msgid="1301710305149590426">"&lt;b&gt;ਸਥਿਤੀ:&lt;/b&gt; ਦਰਜਾ ਵਧਾਇਆ ਗਿਆ"</string>
     <string name="notification_channel_summary_automatic_demoted" msgid="1831303964660807700">"&lt;b&gt;ਸਥਿਤੀ:&lt;/b&gt; ਦਰਜਾ ਘਟਾਇਆ ਗਿਆ"</string>
-    <!-- no translation found for notification_channel_summary_priority_baseline (46674690072551234) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_priority_bubble (1275413109619074576) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_priority_dnd (6665395023264154361) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_priority_all (7151752959650048285) -->
-    <skip />
+    <string name="notification_channel_summary_priority_baseline" msgid="46674690072551234">"ਗੱਲਬਾਤ ਸੂਚਨਾਵਾਂ ਦੇ ਸਿਖਰ \'ਤੇ ਅਤੇ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ ਵਜੋਂ ਦਿਖਾਉਂਦਾ ਹੈ"</string>
+    <string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"ਗੱਲਬਾਤ ਸੂਚਨਾਵਾਂ ਦੇ ਸਿਖਰ \'ਤੇ ਅਤੇ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ, ਬਬਲ ਵਜੋਂ ਦਿਖਾਉਂਦਾ ਹੈ"</string>
+    <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"ਗੱਲਬਾਤ ਸੂਚਨਾਵਾਂ ਦੇ ਸਿਖਰ \'ਤੇ ਅਤੇ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ ਵਜੋਂ ਦਿਖਾਉਂਦਾ ਹੈ, \'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਸੁਵਿਧਾ ਵਿੱਚ ਵੀ ਵਿਘਨ ਪੈ ਸਕਦਾ ਹੈ"</string>
+    <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"ਗੱਲਬਾਤ ਸੂਚਨਾਵਾਂ ਦੇ ਸਿਖਰ \'ਤੇ ਅਤੇ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ, ਬਬਲ ਵਜੋਂ ਦਿਖਾਉਂਦਾ ਹੈ, \'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਸੁਵਿਧਾ ਵਿੱਚ ਵੀ ਵਿਘਨ ਪੈ ਸਕਦਾ ਹੈ"</string>
     <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"ਸੈਟਿੰਗਾਂ"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"ਤਰਜੀਹ"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਐਪ ਗੱਲਬਾਤ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੀ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 6ee7453..3c6578e 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -477,14 +477,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Całkowita\ncisza"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Tylko\npriorytetowe"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Tylko\nalarmy"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ładowanie bezprzewodowe • Pełne naładowanie za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Ładowanie • Pełne naładowanie za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Szybkie ładowanie • Pełne naładowanie za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Wolne ładowanie • Pełne naładowanie za <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Przełącz użytkownika"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Przełącz użytkownika. Bieżący użytkownik: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Bieżący użytkownik: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index b15a144..8228c0c 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -289,11 +289,9 @@
     <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"O ponto de acesso móvel foi desativado."</string>
     <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"O ponto de acesso móvel foi ativado."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"A transmissão de tela foi interrompida."</string>
-    <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
-    <skip />
+    <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Modo de trabalho pausado."</string>
     <string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Modo de trabalho ativado."</string>
-    <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
-    <skip />
+    <string name="accessibility_quick_settings_work_mode_changed_off" msgid="2653550342355027441">"O modo de trabalho foi pausado."</string>
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Modo de trabalho ativado."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Economia de dados desativada."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Economia de dados ativada."</string>
@@ -412,8 +410,7 @@
     <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limite: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Aviso de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Perfil de trabalho"</string>
-    <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
-    <skip />
+    <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Pausado"</string>
     <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Modo noturno"</string>
     <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Ativ. ao pôr do sol"</string>
     <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Até o nascer do sol"</string>
@@ -473,14 +470,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Silêncio\ntotal"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Somente\nprioridade"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Somente\nalarmes"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregando sem fio • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregando • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga rápida • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga lenta • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Trocar usuário"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Alternar usuário. Usuário atual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Usuário atual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +669,10 @@
     <string name="wallet_title" msgid="5369767670735827105">"Carteira"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"Mostrar tudo"</string>
     <string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Desbloqueie para pagar"</string>
-    <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
-    <skip />
+    <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Dispositivos não configurados"</string>
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Ocorreu um problema ao carregar os cards. Tente novamente mais tarde"</string>
-    <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
-    <skip />
+    <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configurações de tela de bloqueio"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabalho"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Modo avião"</string>
     <string name="add_tile" msgid="6239678623873086686">"Adicionar bloco"</string>
@@ -871,8 +862,7 @@
     <string name="switch_bar_on" msgid="1770868129120096114">"Ativado"</string>
     <string name="switch_bar_off" msgid="5669805115416379556">"Desativado"</string>
     <string name="tile_unavailable" msgid="3095879009136616920">"Indisponível"</string>
-    <!-- no translation found for tile_disabled (373212051546573069) -->
-    <skip />
+    <string name="tile_disabled" msgid="373212051546573069">"Desativado"</string>
     <string name="nav_bar" msgid="4642708685386136807">"Barra de navegação"</string>
     <string name="nav_bar_layout" msgid="4716392484772899544">"Layout"</string>
     <string name="left_nav_bar_button_type" msgid="2634852842345192790">"Tipo de botão esquerdo extra"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 90b1931..afebc31 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -473,14 +473,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Silêncio\ntotal"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Apenas\nprioridade"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Apenas\nalarmes"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • A carregar sem fios • Carga completa em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • A carregar • Carga completa em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • A carregar rapidamente • Carga completa em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • A carregar lentamente • Carga completa em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Mudar utilizador"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Mudar de utilizador; o utilizador atual é <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Utilizador atual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index b15a144..8228c0c 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -289,11 +289,9 @@
     <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"O ponto de acesso móvel foi desativado."</string>
     <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"O ponto de acesso móvel foi ativado."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"A transmissão de tela foi interrompida."</string>
-    <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
-    <skip />
+    <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Modo de trabalho pausado."</string>
     <string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Modo de trabalho ativado."</string>
-    <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
-    <skip />
+    <string name="accessibility_quick_settings_work_mode_changed_off" msgid="2653550342355027441">"O modo de trabalho foi pausado."</string>
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Modo de trabalho ativado."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Economia de dados desativada."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Economia de dados ativada."</string>
@@ -412,8 +410,7 @@
     <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limite: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Aviso de <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Perfil de trabalho"</string>
-    <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
-    <skip />
+    <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Pausado"</string>
     <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Modo noturno"</string>
     <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Ativ. ao pôr do sol"</string>
     <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Até o nascer do sol"</string>
@@ -473,14 +470,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Silêncio\ntotal"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Somente\nprioridade"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Somente\nalarmes"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregando sem fio • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carregando • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga rápida • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga lenta • Conclusão em <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Trocar usuário"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Alternar usuário. Usuário atual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Usuário atual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -676,12 +669,10 @@
     <string name="wallet_title" msgid="5369767670735827105">"Carteira"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"Mostrar tudo"</string>
     <string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Desbloqueie para pagar"</string>
-    <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
-    <skip />
+    <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Dispositivos não configurados"</string>
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Desbloquear para usar"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Ocorreu um problema ao carregar os cards. Tente novamente mais tarde"</string>
-    <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
-    <skip />
+    <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Configurações de tela de bloqueio"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Perfil de trabalho"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Modo avião"</string>
     <string name="add_tile" msgid="6239678623873086686">"Adicionar bloco"</string>
@@ -871,8 +862,7 @@
     <string name="switch_bar_on" msgid="1770868129120096114">"Ativado"</string>
     <string name="switch_bar_off" msgid="5669805115416379556">"Desativado"</string>
     <string name="tile_unavailable" msgid="3095879009136616920">"Indisponível"</string>
-    <!-- no translation found for tile_disabled (373212051546573069) -->
-    <skip />
+    <string name="tile_disabled" msgid="373212051546573069">"Desativado"</string>
     <string name="nav_bar" msgid="4642708685386136807">"Barra de navegação"</string>
     <string name="nav_bar_layout" msgid="4716392484772899544">"Layout"</string>
     <string name="left_nav_bar_button_type" msgid="2634852842345192790">"Tipo de botão esquerdo extra"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 44325f7..407ad62 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -289,11 +289,9 @@
     <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"Mobilný hotspot je vypnutý."</string>
     <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"Mobilný hotspot je zapnutý."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Prenášanie bolo zastavené."</string>
-    <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
-    <skip />
+    <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Pracovný režim je pozastavený."</string>
     <string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Pracovný režim zapnutý"</string>
-    <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
-    <skip />
+    <string name="accessibility_quick_settings_work_mode_changed_off" msgid="2653550342355027441">"Pracovný režim bol pozastavený."</string>
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Pracovný režim je zapnutý."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Šetrič dát bol vypnutý."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Šetrič dát bol zapnutý."</string>
@@ -416,8 +414,7 @@
     <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"Limit: <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"Upozornenie pri <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
     <string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Pracovný profil"</string>
-    <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
-    <skip />
+    <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Pozastavené"</string>
     <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Nočný režim"</string>
     <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Zapne sa pri západe slnka"</string>
     <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Do východu slnka"</string>
@@ -477,14 +474,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Úplné\nticho"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Iba\nprioritné"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Iba\nbudíky"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíja sa bezdrôtovo • Do úplného nabitia zostáva <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíja sa • Do úplného nabitia zostáva <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíja sa rýchlo • Do úplného nabitia zostáva <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Nabíja sa pomaly • Do úplného nabitia zostáva <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Prepnutie používateľa"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Prepnúť používateľa (súčasný používateľ: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Aktuálny používateľ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -682,12 +675,10 @@
     <string name="wallet_title" msgid="5369767670735827105">"Peňaženka"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"Zobraziť všetko"</string>
     <string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Odomknúť a zaplatiť"</string>
-    <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
-    <skip />
+    <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Nenastavené"</string>
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Odomknúť a použiť"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Pri načítavaní kariet sa vyskytol problém. Skúste to neskôr."</string>
-    <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
-    <skip />
+    <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Nastavenia uzamknutej obrazovky"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Pracovný profil"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Režim v lietadle"</string>
     <string name="add_tile" msgid="6239678623873086686">"Pridať dlaždicu"</string>
@@ -881,8 +872,7 @@
     <string name="switch_bar_on" msgid="1770868129120096114">"Zapnuté"</string>
     <string name="switch_bar_off" msgid="5669805115416379556">"Vypnuté"</string>
     <string name="tile_unavailable" msgid="3095879009136616920">"Nedostupné"</string>
-    <!-- no translation found for tile_disabled (373212051546573069) -->
-    <skip />
+    <string name="tile_disabled" msgid="373212051546573069">"Deaktivované"</string>
     <string name="nav_bar" msgid="4642708685386136807">"Navigačný panel"</string>
     <string name="nav_bar_layout" msgid="4716392484772899544">"Rozloženie"</string>
     <string name="left_nav_bar_button_type" msgid="2634852842345192790">"Dodatočný typ ľavého tlačidla"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 70b53cb..d44bd22 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -473,14 +473,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Heshtje\ne plotë"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Vetëm\nme prioritet"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Vetëm\nalarmet"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Po karikohet me valë • Plot për <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Po karikohet • Plot për <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Po karikohet shpejt • Plot për <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Po karikohet ngadalë • Plot për <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Ndërro përdorues"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Ndërro përdoruesin. Përdoruesi aktual është <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Përdoruesi aktual <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
@@ -750,14 +746,10 @@
     <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"&lt;b&gt;Statusi:&lt;/b&gt; Ulur në nivel si në heshtje"</string>
     <string name="notification_channel_summary_automatic_promoted" msgid="1301710305149590426">"&lt;b&gt;Statusi:&lt;/b&gt; Renditur më lart"</string>
     <string name="notification_channel_summary_automatic_demoted" msgid="1831303964660807700">"&lt;b&gt;Statusi:&lt;/b&gt; Renditur më poshtë"</string>
-    <!-- no translation found for notification_channel_summary_priority_baseline (46674690072551234) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_priority_bubble (1275413109619074576) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_priority_dnd (6665395023264154361) -->
-    <skip />
-    <!-- no translation found for notification_channel_summary_priority_all (7151752959650048285) -->
-    <skip />
+    <string name="notification_channel_summary_priority_baseline" msgid="46674690072551234">"Shfaqet në krye të njoftimeve të bisedës dhe si fotografia e profilit në ekranin e kyçjes"</string>
+    <string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"Shfaqet në krye të njoftimeve të bisedës, shfaqet si fotografia e profilit në ekranin e kyçjes dhe shfaqet si flluskë"</string>
+    <string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"Shfaqet në krye të njoftimeve të bisedës, shfaqet si fotografia e profilit në ekranin e kyçjes dhe ndërpret modalitetin \"Mos shqetëso\""</string>
+    <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"Shfaqet në krye të njoftimeve të bisedës dhe si fotografia e profilit në ekranin e kyçjes, shfaqet si flluskë dhe ndërpret modalitetin \"Mos shqetëso\""</string>
     <string name="notification_conversation_channel_settings" msgid="2409977688430606835">"Cilësimet"</string>
     <string name="notification_priority_title" msgid="2079708866333537093">"Përparësia"</string>
     <string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> nuk mbështet veçoritë e bisedës"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index de1c9a4..93fb30f 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -475,14 +475,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Потпуна\nтишина"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Само\nприорит. прекиди"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Само\nаларми"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Бежично се пуни • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до краја пуњења"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Пуни се • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до краја пуњења"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Брзо се пуни • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до краја пуњења"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Споро се пуни • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> до краја пуњења"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Замени корисника"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Промените корисника, актуелни корисник је <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Актуелни корисник <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index fcbe07d..3e7e1b1 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -473,14 +473,10 @@
     <string name="interruption_level_none_twoline" msgid="8579382742855486372">"Helt\ntyst"</string>
     <string name="interruption_level_priority_twoline" msgid="8523482736582498083">"Endast\nprioriterade"</string>
     <string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Endast\nalarm"</string>
-    <!-- no translation found for keyguard_indication_charging_time_wireless (577856646141738675) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time (6492711711891071502) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_fast (8390311020603859480) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_charging_time_slowly (301936949731705417) -->
-    <skip />
+    <string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laddas trådlöst • Fulladdat om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laddas • Fulladdat om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laddas snabbt • Fulladdat om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+    <string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Laddas långsamt • Fulladdat om <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Byt användare"</string>
     <string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Byt användare. Aktuell användare: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
     <string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"Aktuell användare <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 1c70acc..0ec4a4b 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -289,11 +289,9 @@
     <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"I-hotspot ivaliwe."</string>
     <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"I-hotspot ivuliwe."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Ukusakaza kwesikrini kumisiwe."</string>
-    <!-- no translation found for accessibility_quick_settings_work_mode_off (9106217884005620744) -->
-    <skip />
+    <string name="accessibility_quick_settings_work_mode_off" msgid="9106217884005620744">"Imodi yokusebenza imisiwe."</string>
     <string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"Imodi yomsebenzi ivuliwe."</string>
-    <!-- no translation found for accessibility_quick_settings_work_mode_changed_off (2653550342355027441) -->
-    <skip />
+    <string name="accessibility_quick_settings_work_mode_changed_off" msgid="2653550342355027441">"Imodi yokusebenza imisiwe."</string>
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"Imodi yomsebenzi ivuliwe."</string>
     <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"Iseva yedatha ivaliwe."</string>
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"Iseva yedatha ivuliwe."</string>
@@ -412,8 +410,7 @@
     <string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> umkhawulo"</string>
     <string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> isexwayiso"</string>
     <string name="quick_settings_work_mode_label" msgid="2754212289804324685">"Iphrofayela yomsebenzi"</string>
-    <!-- no translation found for quick_settings_work_mode_paused (4841109346916998613) -->
-    <skip />
+    <string name="quick_settings_work_mode_paused" msgid="4841109346916998613">"Kumisiwe"</string>
     <string name="quick_settings_night_display_label" msgid="8180030659141778180">"Ukukhanya kwasebusuku"</string>
     <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"Kuvulwe ekushoneni kwelanga"</string>
     <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Kuze kube sekuphumeni kwelanga"</string>
@@ -676,12 +673,10 @@
     <string name="wallet_title" msgid="5369767670735827105">"I-wallet"</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"Bonisa konke"</string>
     <string name="wallet_action_button_label_unlock" msgid="8663239748726774487">"Vula ukuze ukhokhele"</string>
-    <!-- no translation found for wallet_secondary_label_no_card (1282609666895946317) -->
-    <skip />
+    <string name="wallet_secondary_label_no_card" msgid="1282609666895946317">"Akusethiwe"</string>
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Vula ukuze usebenzise"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Kube khona inkinga yokuthola amakhadi akho, sicela uzame futhi ngemuva kwesikhathi"</string>
-    <!-- no translation found for wallet_lockscreen_settings_label (3539105300870383570) -->
-    <skip />
+    <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Amasethingi okukhiya isikrini"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Iphrofayela yomsebenzi"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Imodi yendiza"</string>
     <string name="add_tile" msgid="6239678623873086686">"Engeza ithayili"</string>
@@ -871,8 +866,7 @@
     <string name="switch_bar_on" msgid="1770868129120096114">"Vuliwe"</string>
     <string name="switch_bar_off" msgid="5669805115416379556">"Valiwe"</string>
     <string name="tile_unavailable" msgid="3095879009136616920">"Akutholakali"</string>
-    <!-- no translation found for tile_disabled (373212051546573069) -->
-    <skip />
+    <string name="tile_disabled" msgid="373212051546573069">"Kukhutshaziwe"</string>
     <string name="nav_bar" msgid="4642708685386136807">"Ibha yokuzula"</string>
     <string name="nav_bar_layout" msgid="4716392484772899544">"Isakhiwo"</string>
     <string name="left_nav_bar_button_type" msgid="2634852842345192790">"Uhlobo lwenkinobho engakwesokunxele engeziwe"</string>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index eb72442..067d56f 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -131,8 +131,6 @@
 
     <declare-styleable name="SmartReplyView">
         <attr name="spacing" format="dimension" />
-        <attr name="singleLineButtonPaddingHorizontal" format="dimension" />
-        <attr name="doubleLineButtonPaddingHorizontal" format="dimension" />
         <attr name="buttonStrokeWidth" format="dimension" />
     </declare-styleable>
 
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 08a2e19..8d2a82c 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -286,7 +286,5 @@
     <color name="accessibility_floating_menu_stroke_dark">#26FFFFFF</color> <!-- 15% -->
 
     <!-- Wallet screen -->
-    <color name="wallet_white">#FFFFFF</color>
     <color name="wallet_card_border">#33FFFFFF</color>
-    <color name="wallet_primary_text">@color/GM2_grey_900</color>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2283844..57e6cc3 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1147,15 +1147,14 @@
     <!-- Smart reply button. Total height 48dp, visible height 32dp. -->
     <dimen name="smart_reply_button_spacing">8dp</dimen>
     <dimen name="smart_reply_button_padding_vertical">14dp</dimen>
-    <!-- Note: The following two paddings need to be different until b/78876518 is fixed. -->
-    <dimen name="smart_reply_button_padding_horizontal_single_line">20dp</dimen>
-    <dimen name="smart_reply_button_padding_horizontal_double_line">19dp</dimen>
+    <dimen name="smart_reply_button_padding_horizontal">16dp</dimen>
+    <dimen name="smart_reply_button_action_padding_left">8dp</dimen>
     <dimen name="smart_reply_button_min_height">48dp</dimen>
     <dimen name="smart_reply_button_stroke_width">1dp</dimen>
     <dimen name="smart_reply_button_font_size">14sp</dimen>
     <dimen name="smart_reply_button_line_spacing_extra">6sp</dimen> <!-- Total line height 20sp. -->
     <!-- Corner radius = half of min_height to create rounded sides. -->
-    <dimen name="smart_reply_button_corner_radius">24dp</dimen>
+    <dimen name="smart_reply_button_corner_radius">8dp</dimen>
     <dimen name="smart_action_button_icon_size">18dp</dimen>
     <dimen name="smart_action_button_icon_padding">8dp</dimen>
 
@@ -1443,6 +1442,15 @@
     <dimen name="accessibility_floating_menu_large_single_radius">33dp</dimen>
     <dimen name="accessibility_floating_menu_large_multiple_radius">35dp</dimen>
 
+    <dimen name="accessibility_floating_tooltip_arrow_width">8dp</dimen>
+    <dimen name="accessibility_floating_tooltip_arrow_height">16dp</dimen>
+    <dimen name="accessibility_floating_tooltip_arrow_margin">-2dp</dimen>
+    <dimen name="accessibility_floating_tooltip_arrow_corner_radius">2dp</dimen>
+    <dimen name="accessibility_floating_tooltip_text_corner_radius">8dp</dimen>
+    <dimen name="accessibility_floating_tooltip_margin">16dp</dimen>
+    <dimen name="accessibility_floating_tooltip_padding">16dp</dimen>
+    <dimen name="accessibility_floating_tooltip_font_size">14sp</dimen>
+
     <dimen name="rounded_slider_height">48dp</dimen>
     <!-- rounded_slider_height / 2 -->
     <dimen name="rounded_slider_corner_radius">24dp</dimen>
@@ -1484,7 +1492,8 @@
 
     <!-- Wallet activity screen specs -->
     <dimen name="wallet_icon_size">36sp</dimen>
-    <dimen name="wallet_view_header_icon_size">56dp</dimen>
+    <dimen name="wallet_screen_header_icon_size">56dp</dimen>
+    <dimen name="wallet_screen_header_view_size">80dp</dimen>
     <dimen name="card_margin">16dp</dimen>
     <dimen name="card_carousel_dot_offset">24dp</dimen>
     <dimen name="card_carousel_dot_unselected_radius">2dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 30add20..150fc73 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -689,12 +689,12 @@
     <string name="accessibility_quick_settings_hotspot_changed_on">Mobile hotspot turned on.</string>
     <!-- Announcement made when the screen stopped casting (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_casting_turned_off">Screen casting stopped.</string>
-    <!-- Content description of the work mode title in quick settings when off (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <!-- Content description of the work mode title in quick settings when off (not shown on the screen). Paused is used as an adjective [CHAR LIMIT=NONE] -->
     <string name="accessibility_quick_settings_work_mode_off">Work mode paused.</string>
     <!-- Content description of the work mode title in quick settings when on (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_quick_settings_work_mode_on">Work mode on.</string>
-    <!-- Announcement made when the work mode changes to off (not shown on the screen). [CHAR LIMIT=NONE] -->
-    <string name="accessibility_quick_settings_work_mode_changed_off">Work mode turned paused.</string>
+    <!-- Announcement made when the work mode changes to off (not shown on the screen). Paused is used as a verb. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_quick_settings_work_mode_changed_off">Work mode paused.</string>
     <!-- Announcement made when the work mode changes to on (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_quick_settings_work_mode_changed_on">Work mode turned on.</string>
     <!-- Announcement made when the Data Saver changes to off (not shown on the screen). [CHAR LIMIT=NONE] -->
@@ -2828,7 +2828,7 @@
     <!-- Label for button to go to media control settings screen [CHAR_LIMIT=30] -->
     <string name="controls_media_settings_button">Settings</string>
 
-    <!-- Title for Smartspace recommendation card within media controls [CHAR_LIMIT=50] -->
+    <!-- Title for Smartspace recommendation card within media controls. The "Play" means the action to play a media [CHAR_LIMIT=10] -->
     <string name="controls_media_smartspace_rec_title">Play</string>
 
     <!-- Error message indicating that a control timed out while waiting for an update [CHAR_LIMIT=30] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 10f7e40..9fa63dd 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -660,9 +660,12 @@
         <item name="android:tint">?android:attr/colorAccent</item>
     </style>
 
+    <style name="MediaPlayer.AppIcon.Recommendation" parent="MediaPlayer.AppIcon">
+        <item name="android:tint">@color/transparent</item>
+    </style>
+
     <style name="MediaPlayer.Album">
         <item name="android:backgroundTint">@color/media_player_album_bg</item>
-
     </style>
 
     <!-- Used to style charging animation AVD animation -->
@@ -692,6 +695,7 @@
         <item name="android:windowNoTitle">true</item>
         <item name="android:windowLightStatusBar">true</item>
         <item name="android:windowLightNavigationBar">true</item>
+        <item name="android:windowActivityTransitions">true</item>
     </style>
 
     <!-- Privacy dialog -->
@@ -874,12 +878,12 @@
 
     <style name="Wallet.TextAppearance">
       <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
-      <item name="android:textColor">@color/wallet_primary_text</item>
+      <item name="android:textColor">?android:attr/textColorPrimary</item>
       <item name="android:singleLine">true</item>
       <item name="android:textSize">14sp</item>
     </style>
 
-    <style name="Wallet.Theme">
-      <item name="android:colorControlHighlight">@*android:color/primary_text_material_dark</item>
+    <style name="Wallet.Theme" parent="@android:style/Theme.DeviceDefault">
+      <item name="android:colorBackground">@android:color/system_neutral1_900</item>
     </style>
 </resources>
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 927bce0..2cf3ad2 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
@@ -105,6 +105,8 @@
     public static final int SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY = 1 << 17;
     // The IME is showing
     public static final int SYSUI_STATE_IME_SHOWING = 1 << 18;
+    // The window magnification is overlapped with system gesture insets at the bottom.
+    public static final int SYSUI_STATE_MAGNIFICATION_OVERLAP = 1 << 19;
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({SYSUI_STATE_SCREEN_PINNING,
@@ -125,7 +127,8 @@
             SYSUI_STATE_GLOBAL_ACTIONS_SHOWING,
             SYSUI_STATE_ONE_HANDED_ACTIVE,
             SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
-            SYSUI_STATE_IME_SHOWING
+            SYSUI_STATE_IME_SHOWING,
+            SYSUI_STATE_MAGNIFICATION_OVERLAP
     })
     public @interface SystemUiStateFlags {}
 
@@ -153,6 +156,7 @@
         str.add((flags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0
                 ? "allow_gesture" : "");
         str.add((flags & SYSUI_STATE_IME_SHOWING) != 0 ? "ime_visible" : "");
+        str.add((flags & SYSUI_STATE_MAGNIFICATION_OVERLAP) != 0 ? "magnification_overlap" : "");
         return str.toString();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 76cec0b..64a683e 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -305,10 +305,15 @@
                     continue;
                 }
                 Rect subImage = new Rect(
-                        Math.round(area.left * b.getWidth()),
-                        Math.round(area.top * b.getHeight()),
-                        Math.round(area.right * b.getWidth()),
-                        Math.round(area.bottom * b.getHeight()));
+                        (int) Math.floor(area.left * b.getWidth()),
+                        (int) Math.floor(area.top * b.getHeight()),
+                        (int) Math.ceil(area.right * b.getWidth()),
+                        (int) Math.ceil(area.bottom * b.getHeight()));
+                if (subImage.isEmpty()) {
+                    // Do not notify client. treat it as too small to sample
+                    colors.add(null);
+                    continue;
+                }
                 Bitmap colorImg = Bitmap.createBitmap(b,
                         subImage.left, subImage.top, subImage.width(), subImage.height());
                 WallpaperColors color = WallpaperColors.fromBitmap(colorImg);
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 7f18379..bf09975 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -73,7 +73,8 @@
             Key.TOUCHED_RINGER_TOGGLE,
             Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP,
             Key.HAS_SEEN_REVERSE_BOTTOM_SHEET,
-            Key.CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT
+            Key.CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT,
+            Key.HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP
     })
     // TODO: annotate these with their types so {@link PrefsCommandLine} can know how to set them
     public @interface Key {
@@ -122,6 +123,8 @@
         String HAS_SEEN_ODI_CAPTIONS_TOOLTIP = "HasSeenODICaptionsTooltip";
         String HAS_SEEN_REVERSE_BOTTOM_SHEET = "HasSeenReverseBottomSheet";
         String CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT = "ControlsStructureSwipeTooltipCount";
+        String HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP =
+                "HasSeenAccessibilityFloatingMenuDockTooltip";
     }
 
     public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java b/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java
index b69001d..c472457 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java
@@ -63,17 +63,15 @@
     }
 
     /**
-     * Gets the object by the element index.
+     * Returns the object with the given display id.
      *
-     * <p> If the index is bigger than the array size, an {@link ArrayIndexOutOfBoundsException} is
-     * thrown for apps targeting {@link android.os.Build.VERSION_CODES#Q} and later </p>
      *
-     * @param index the element index
+     * @param displayId the logical display Id
      * @return T
-     * @see SparseArray#valueAt(int)
      */
-    public T valueAt(int index) {
-        return mSparseArray.valueAt(index);
+    @Nullable
+    public T valueAt(int displayId) {
+        return mSparseArray.get(displayId);
     }
 
     @NonNull
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java
index 4c892e29..4b30ec3 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java
@@ -113,7 +113,7 @@
         final float rawX = event.getRawX();
         final float rawY = event.getRawY();
         boolean handled = false;
-        switch (event.getAction()) {
+        switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
                 mPointerDown.set(rawX, rawY);
                 mHandler.postAtTime(mCancelTapGestureRunnable,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index 4f5fdc9..cee395b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -18,10 +18,11 @@
 
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
 
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
+
 import android.annotation.MainThread;
 import android.annotation.Nullable;
 import android.content.Context;
-import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
@@ -37,9 +38,13 @@
 import com.android.systemui.SystemUI;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.statusbar.CommandQueue;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
 import javax.inject.Inject;
 
 /**
@@ -52,34 +57,33 @@
 public class WindowMagnification extends SystemUI implements WindowMagnifierCallback,
         CommandQueue.Callbacks {
     private static final String TAG = "WindowMagnification";
-    private static final int CONFIG_MASK =
-            ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_ORIENTATION
-                    | ActivityInfo.CONFIG_LOCALE;
 
     private final ModeSwitchesController mModeSwitchesController;
     private final Handler mHandler;
     private final AccessibilityManager mAccessibilityManager;
     private final CommandQueue mCommandQueue;
+    private final OverviewProxyService mOverviewProxyService;
 
     private WindowMagnificationConnectionImpl mWindowMagnificationConnectionImpl;
     private Configuration mLastConfiguration;
+    private SysUiState mSysUiState;
 
     private static class AnimationControllerSupplier extends
             DisplayIdIndexSupplier<WindowMagnificationAnimationController> {
 
         private final Context mContext;
         private final Handler mHandler;
-        private final NavigationModeController mNavigationModeController;
         private final WindowMagnifierCallback mWindowMagnifierCallback;
+        private final SysUiState mSysUiState;
 
         AnimationControllerSupplier(Context context, Handler handler,
-                NavigationModeController navigationModeController,
-                WindowMagnifierCallback windowMagnifierCallback, DisplayManager displayManager) {
+                WindowMagnifierCallback windowMagnifierCallback,
+                DisplayManager displayManager, SysUiState sysUiState) {
             super(displayManager);
             mContext = context;
             mHandler = handler;
-            mNavigationModeController = navigationModeController;
             mWindowMagnifierCallback = windowMagnifierCallback;
+            mSysUiState = sysUiState;
         }
 
         @Override
@@ -89,10 +93,7 @@
             final WindowMagnificationController controller = new WindowMagnificationController(
                     mContext,
                     mHandler, new SfVsyncFrameCallbackProvider(), null,
-                    new SurfaceControl.Transaction(), mWindowMagnifierCallback);
-            final int navBarMode = mNavigationModeController.addListener(
-                    controller::onNavigationModeChanged);
-            controller.onNavigationModeChanged(navBarMode);
+                    new SurfaceControl.Transaction(), mWindowMagnifierCallback, mSysUiState);
             return new WindowMagnificationAnimationController(windowContext, controller);
         }
     }
@@ -103,24 +104,22 @@
     @Inject
     public WindowMagnification(Context context, @Main Handler mainHandler,
             CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
-            NavigationModeController navigationModeController) {
+            SysUiState sysUiState, OverviewProxyService overviewProxyService) {
         super(context);
         mHandler = mainHandler;
         mLastConfiguration = new Configuration(context.getResources().getConfiguration());
         mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
         mCommandQueue = commandQueue;
         mModeSwitchesController = modeSwitchesController;
+        mSysUiState = sysUiState;
+        mOverviewProxyService = overviewProxyService;
         mAnimationControllerSupplier = new AnimationControllerSupplier(context,
-                mHandler, navigationModeController, this,
-                context.getSystemService(DisplayManager.class));
+                mHandler, this, context.getSystemService(DisplayManager.class), sysUiState);
     }
 
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
         final int configDiff = newConfig.diff(mLastConfiguration);
-        if ((configDiff & CONFIG_MASK) == 0) {
-            return;
-        }
         mLastConfiguration.setTo(newConfig);
         mAnimationControllerSupplier.forEach(
                 animationController -> animationController.onConfigurationChanged(configDiff));
@@ -132,6 +131,28 @@
     @Override
     public void start() {
         mCommandQueue.addCallback(this);
+        mOverviewProxyService.addCallback(new OverviewProxyService.OverviewProxyListener() {
+            @Override
+            public void onConnectionChanged(boolean isConnected) {
+                if (isConnected) {
+                    updateSysUiStateFlag();
+                }
+            }
+        });
+    }
+
+    private void updateSysUiStateFlag() {
+        //TODO(b/187510533): support multi-display once SysuiState supports it.
+        final WindowMagnificationAnimationController controller =
+                mAnimationControllerSupplier.valueAt(Display.DEFAULT_DISPLAY);
+        if (controller != null) {
+            controller.updateSysUiStateFlag();
+        } else {
+            // The instance is initialized when there is an IPC request. Considering
+            // self-crash cases, we need to reset the flag in such situation.
+            mSysUiState.setFlag(SYSUI_STATE_MAGNIFICATION_OVERLAP, false)
+                    .commitUpdate(Display.DEFAULT_DISPLAY);
+        }
     }
 
     @MainThread
@@ -210,6 +231,13 @@
         }
     }
 
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println(TAG);
+        mAnimationControllerSupplier.forEach(
+                animationController -> animationController.dump(pw));
+    }
+
     private void setWindowMagnificationConnection() {
         if (mWindowMagnificationConnectionImpl == null) {
             mWindowMagnificationConnectionImpl = new WindowMagnificationConnectionImpl(this,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
index 5758b15..36fef3e 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
@@ -31,6 +31,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.R;
 
+import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -269,6 +270,14 @@
         mController.enableWindowMagnification(sentScale, centerX, centerY);
     }
 
+    public void updateSysUiStateFlag() {
+        mController.updateSysUIStateFlag();
+    }
+
+    void dump(PrintWriter pw) {
+        mController.dump(pw);
+    }
+
     private static ValueAnimator newValueAnimator(Resources resources) {
         final ValueAnimator valueAnimator = new ValueAnimator();
         valueAnimator.setDuration(
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index ae16703..fcb090a 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -16,9 +16,10 @@
 
 package com.android.systemui.accessibility;
 
+import static android.view.WindowInsets.Type.systemGestures;
 import static android.view.WindowManager.LayoutParams;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
 
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
@@ -28,6 +29,7 @@
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.res.Resources;
+import android.graphics.Insets;
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
@@ -52,14 +54,17 @@
 import android.view.View;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
+import android.view.WindowMetrics;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
 import com.android.systemui.R;
+import com.android.systemui.model.SysUiState;
 import com.android.systemui.shared.system.WindowManagerWrapper;
 
+import java.io.PrintWriter;
 import java.text.NumberFormat;
 import java.util.Locale;
 
@@ -111,6 +116,7 @@
     private final View.OnLayoutChangeListener mMirrorSurfaceViewLayoutChangeListener;
     private final Runnable mMirrorViewRunnable;
     private final Runnable mUpdateStateDescriptionRunnable;
+    private final Runnable mWindowInsetChangeRunnable;
     private View mMirrorView;
     private SurfaceView mMirrorSurfaceView;
     private int mMirrorSurfaceMargin;
@@ -119,9 +125,8 @@
     private int mOuterBorderSize;
     // The boundary of magnification frame.
     private final Rect mMagnificationFrameBoundary = new Rect();
-
-    private int mNavBarMode;
-    private int mNavGestureHeight;
+    // The top Y of the system gesture rect at the bottom. Set to -1 if it is invalid.
+    private int mSystemGestureTop = -1;
 
     private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
     private final MagnificationGestureDetector mGestureDetector;
@@ -130,6 +135,9 @@
     private Locale mLocale;
     private NumberFormat mPercentFormat;
     private float mBounceEffectAnimationScale;
+    private SysUiState mSysUiState;
+    // Set it to true when the view is overlapped with the gesture insets at the bottom.
+    private boolean mOverlapWithGestureInsets;
 
     @Nullable
     private MirrorWindowControl mMirrorWindowControl;
@@ -137,11 +145,12 @@
     WindowMagnificationController(@UiContext Context context, @NonNull Handler handler,
             SfVsyncFrameCallbackProvider sfVsyncFrameProvider,
             MirrorWindowControl mirrorWindowControl, SurfaceControl.Transaction transaction,
-            @NonNull WindowMagnifierCallback callback) {
+            @NonNull WindowMagnifierCallback callback, SysUiState sysUiState) {
         mContext = context;
         mHandler = handler;
         mSfVsyncFrameProvider = sfVsyncFrameProvider;
         mWindowMagnifierCallback = callback;
+        mSysUiState = sysUiState;
 
         final Display display = mContext.getDisplay();
         mDisplayId = mContext.getDisplayId();
@@ -170,13 +179,18 @@
         mMirrorViewRunnable = () -> {
             if (mMirrorView != null) {
                 mMirrorView.getBoundsOnScreen(mMirrorViewBounds);
+                updateSystemUIStateIfNeeded();
                 mWindowMagnifierCallback.onWindowMagnifierBoundsChanged(
                         mDisplayId, mMirrorViewBounds);
             }
         };
         mMirrorViewLayoutChangeListener =
-                (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
+                (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+                    if (!mHandler.hasCallbacks(mMirrorViewRunnable)) {
                         mHandler.post(mMirrorViewRunnable);
+                    }
+                };
+
         mMirrorSurfaceViewLayoutChangeListener =
                 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom)
                         -> applyTapExcludeRegion();
@@ -199,6 +213,7 @@
                 mMirrorView.setStateDescription(formatStateDescription(mScale));
             }
         };
+        mWindowInsetChangeRunnable = this::onWindowInsetChanged;
     }
 
     private void updateDimensions() {
@@ -210,7 +225,6 @@
                 R.dimen.magnification_drag_view_size);
         mOuterBorderSize = mResources.getDimensionPixelSize(
                 R.dimen.magnification_outer_border_margin);
-        updateNavigationBarDimensions();
     }
 
     private void computeBounceAnimationScale() {
@@ -220,16 +234,16 @@
         mBounceEffectAnimationScale = Math.min(animationScaleMax, ANIMATION_BOUNCE_EFFECT_SCALE);
     }
 
-    private void updateNavigationBarDimensions() {
-        if (!supportsSwipeUpGesture()) {
-            mNavGestureHeight = 0;
-            return;
+    private boolean updateSystemGestureInsetsTop() {
+        final WindowMetrics windowMetrics = mWm.getCurrentWindowMetrics();
+        final Insets insets = windowMetrics.getWindowInsets().getInsets(systemGestures());
+        final int gestureTop =
+                insets.bottom != 0 ? windowMetrics.getBounds().bottom - insets.bottom : -1;
+        if (gestureTop != mSystemGestureTop) {
+            mSystemGestureTop = gestureTop;
+            return true;
         }
-        mNavGestureHeight = (mWindowBounds.width() > mWindowBounds.height())
-                ? mResources.getDimensionPixelSize(
-                com.android.internal.R.dimen.navigation_bar_height_landscape)
-                : mResources.getDimensionPixelSize(
-                        com.android.internal.R.dimen.navigation_bar_gesture_height);
+        return false;
     }
 
     /**
@@ -255,6 +269,7 @@
         if (mMirrorWindowControl != null) {
             mMirrorWindowControl.destroyControl();
         }
+        updateSystemUIStateIfNeeded();
     }
 
     /**
@@ -277,6 +292,10 @@
         }
     }
 
+    private void updateSystemUIStateIfNeeded() {
+        updateSysUIState(false);
+    }
+
     private void updateAccessibilityWindowTitleIfNeeded() {
         if (!isWindowVisible()) return;
         LayoutParams params = (LayoutParams) mMirrorView.getLayoutParams();
@@ -284,13 +303,6 @@
         mWm.updateViewLayout(mMirrorView, params);
     }
 
-    /** Handles MirrorWindow position when the navigation bar mode changed. */
-    public void onNavigationModeChanged(int mode) {
-        mNavBarMode = mode;
-        updateNavigationBarDimensions();
-        updateMirrorViewLayout();
-    }
-
     /** Handles MirrorWindow position when the device rotation changed. */
     private void onRotate() {
         final Display display = mContext.getDisplay();
@@ -299,7 +311,6 @@
 
         setMagnificationFrameBoundary();
         mRotation = display.getRotation();
-        updateNavigationBarDimensions();
 
         if (!isWindowVisible()) {
             return;
@@ -351,6 +362,7 @@
         params.x = mMagnificationFrame.left - mMirrorSurfaceMargin;
         params.y = mMagnificationFrame.top - mMirrorSurfaceMargin;
         params.layoutInDisplayCutoutMode = LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+        params.receiveInsetsIgnoringZOrder = true;
         params.setTitle(mContext.getString(R.string.magnification_window_title));
         params.accessibilityTitle = getAccessibilityWindowTitle();
 
@@ -368,6 +380,12 @@
                 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
         mMirrorView.addOnLayoutChangeListener(mMirrorViewLayoutChangeListener);
         mMirrorView.setAccessibilityDelegate(new MirrorWindowA11yDelegate());
+        mMirrorView.setOnApplyWindowInsetsListener((v, insets) -> {
+            if (!mHandler.hasCallbacks(mWindowInsetChangeRunnable)) {
+                mHandler.post(mWindowInsetChangeRunnable);
+            }
+            return v.onApplyWindowInsets(insets);
+        });
 
         mWm.addView(mMirrorView, params);
 
@@ -377,6 +395,12 @@
         addDragTouchListeners();
     }
 
+    private void onWindowInsetChanged() {
+        if (updateSystemGestureInsetsTop()) {
+            updateSystemUIStateIfNeeded();
+        }
+    }
+
     private void applyTapExcludeRegion() {
         final Region tapExcludeRegion = calculateTapExclude();
         final IWindow window = IWindow.Stub.asInterface(mMirrorView.getWindowToken());
@@ -464,17 +488,12 @@
             return;
         }
         final int maxMirrorViewX = mWindowBounds.width() - mMirrorView.getWidth();
-        final int maxMirrorViewY =
-                mWindowBounds.height() - mMirrorView.getHeight() - mNavGestureHeight;
+        final int maxMirrorViewY = mWindowBounds.height() - mMirrorView.getHeight();
+
         LayoutParams params =
                 (LayoutParams) mMirrorView.getLayoutParams();
         params.x = mMagnificationFrame.left - mMirrorSurfaceMargin;
         params.y = mMagnificationFrame.top - mMirrorSurfaceMargin;
-        // If nav bar mode supports swipe-up gesture, the Y position of mirror view should not
-        // overlap nav bar window to prevent window-dragging obscured.
-        if (supportsSwipeUpGesture()) {
-            params.y = Math.min(params.y, maxMirrorViewY);
-        }
 
         // Translates MirrorView position to make MirrorSurfaceView that is inside MirrorView
         // able to move close to the screen edges.
@@ -508,6 +527,10 @@
         return false;
     }
 
+    public void updateSysUIStateFlag() {
+        updateSysUIState(true);
+    }
+
     /**
      * Calculates the desired source bounds. This will be the area under from the center of  the
      * displayFrame, factoring in scale.
@@ -569,6 +592,16 @@
         return false;
     }
 
+    private void updateSysUIState(boolean force) {
+        final boolean overlap = isWindowVisible() && mSystemGestureTop > 0
+                && mMirrorViewBounds.bottom > mSystemGestureTop;
+        if (force || overlap != mOverlapWithGestureInsets) {
+            mOverlapWithGestureInsets = overlap;
+            mSysUiState.setFlag(SYSUI_STATE_MAGNIFICATION_OVERLAP, mOverlapWithGestureInsets)
+                    .commitUpdate(mDisplayId);
+        }
+    }
+
     @Override
     public void surfaceCreated(SurfaceHolder holder) {
         createMirror();
@@ -676,10 +709,6 @@
         return mMirrorView != null;
     }
 
-    private boolean supportsSwipeUpGesture() {
-        return mNavBarMode == NAV_BAR_MODE_2BUTTON || mNavBarMode == NAV_BAR_MODE_GESTURAL;
-    }
-
     private CharSequence formatStateDescription(float scale) {
         // Cache the locale-appropriate NumberFormat.  Configuration locale is guaranteed
         // non-null, so the first time this is called we will always get the appropriate
@@ -722,6 +751,14 @@
         scaleAnimator.start();
     }
 
+    public void dump(PrintWriter pw) {
+        pw.println("WindowMagnificationController (displayId=" + mDisplayId + "):");
+        pw.println("      mOverlapWithGestureInsets:" + mOverlapWithGestureInsets);
+        pw.println("      mScale:" + mScale);
+        pw.println("      mMirrorViewBounds:" + (isWindowVisible() ? mMirrorViewBounds : "empty"));
+        pw.println("      mSystemGestureTop:" + mSystemGestureTop);
+    }
+
     private class MirrorWindowA11yDelegate extends View.AccessibilityDelegate {
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
index 3b3bad3..ee62768 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenu.java
@@ -18,11 +18,13 @@
 
 import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED;
 import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_ICON_TYPE;
+import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT;
 import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY;
 import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE;
 import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
 
 import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getTargets;
+import static com.android.systemui.Prefs.Key.HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP;
 import static com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuView.ShapeType;
 import static com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuView.SizeType;
 
@@ -34,15 +36,19 @@
 import android.provider.Settings;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Prefs;
 
 /**
  * Contains logic for an accessibility floating menu view.
  */
 public class AccessibilityFloatingMenu implements IAccessibilityFloatingMenu {
-    private static final int DEFAULT_FADE_EFFECT_ENABLED = 1;
+    private static final int DEFAULT_FADE_EFFECT_IS_ENABLED = 1;
+    private static final int DEFAULT_MIGRATION_TOOLTIP_PROMPT_IS_DISABLED = 0;
     private static final float DEFAULT_OPACITY_VALUE = 0.55f;
     private final Context mContext;
     private final AccessibilityFloatingMenuView mMenuView;
+    private final MigrationTooltipView mMigrationTooltipView;
+    private final DockTooltipView mDockTooltipView;
     private final Handler mHandler = new Handler(Looper.getMainLooper());
 
     private final ContentObserver mContentObserver =
@@ -86,6 +92,8 @@
     AccessibilityFloatingMenu(Context context, AccessibilityFloatingMenuView menuView) {
         mContext = context;
         mMenuView = menuView;
+        mMigrationTooltipView = new MigrationTooltipView(mContext, mMenuView);
+        mDockTooltipView = new DockTooltipView(mContext, mMenuView);
     }
 
     @Override
@@ -105,6 +113,9 @@
                 getOpacityValue(mContext));
         mMenuView.setSizeType(getSizeType(mContext));
         mMenuView.setShapeType(getShapeType(mContext));
+        mMenuView.setOnDragEndListener(this::showDockTooltipIfNecessary);
+
+        showMigrationTooltipIfNecessary();
 
         registerContentObservers();
     }
@@ -116,14 +127,48 @@
         }
 
         mMenuView.hide();
+        mMigrationTooltipView.hide();
+        mDockTooltipView.hide();
 
         unregisterContentObservers();
     }
 
+    // Migration tooltip was the android S feature. It's just used on the Android version from R
+    // to S. In addition, it only shows once.
+    private void showMigrationTooltipIfNecessary() {
+        if (isMigrationTooltipPromptEnabled(mContext)) {
+            mMigrationTooltipView.show();
+
+            Settings.Secure.putInt(mContext.getContentResolver(),
+                    ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT, /* disabled */ 0);
+        }
+    }
+
+    private static boolean isMigrationTooltipPromptEnabled(Context context) {
+        return Settings.Secure.getInt(
+                context.getContentResolver(), ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT,
+                DEFAULT_MIGRATION_TOOLTIP_PROMPT_IS_DISABLED) == /* enabled */ 1;
+    }
+
+    /**
+     * Shows tooltip when user drags accessibility floating menu for the first time.
+     */
+    private void showDockTooltipIfNecessary() {
+        if (!Prefs.get(mContext).getBoolean(
+                HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP, false)) {
+            // if the menu is an oval, the user has already dragged it out, so show the tooltip.
+            if (mMenuView.isOvalShape()) {
+                mDockTooltipView.show();
+            }
+
+            Prefs.putBoolean(mContext, HAS_SEEN_ACCESSIBILITY_FLOATING_MENU_DOCK_TOOLTIP, true);
+        }
+    }
+
     private static boolean isFadeEffectEnabled(Context context) {
         return Settings.Secure.getInt(
                 context.getContentResolver(), ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED,
-                DEFAULT_FADE_EFFECT_ENABLED) == /* enable */ 1;
+                DEFAULT_FADE_EFFECT_IS_ENABLED) == /* enabled */ 1;
     }
 
     private static float getOpacityValue(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
index 934e20d..49e6b47 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
@@ -19,6 +19,8 @@
 import static android.util.MathUtils.constrain;
 import static android.util.MathUtils.sq;
 
+import static java.util.Objects.requireNonNull;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
@@ -43,7 +45,9 @@
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import android.view.animation.Animation;
 import android.view.animation.OvershootInterpolator;
+import android.view.animation.TranslateAnimation;
 import android.widget.FrameLayout;
 
 import androidx.annotation.DimenRes;
@@ -60,6 +64,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Optional;
 
 /**
  * Accessibility floating menu is used for the actions of accessibility features, it's also the
@@ -78,6 +83,10 @@
     private static final int MIN_WINDOW_Y = 0;
     private static final float LOCATION_Y_PERCENTAGE = 0.8f;
 
+    private static final int ANIMATION_START_OFFSET = 600;
+    private static final int ANIMATION_DURATION_MS = 600;
+    private static final float ANIMATION_TO_X_VALUE = 0.5f;
+
     private boolean mIsFadeEffectEnabled;
     private boolean mIsShowing;
     private boolean mIsDownInEnlargedTouchArea;
@@ -107,6 +116,7 @@
     private float mPercentageY = LOCATION_Y_PERCENTAGE;
     private float mSquareScaledTouchSlop;
     private final Configuration mLastConfiguration;
+    private Optional<OnDragEndListener> mOnDragEndListener = Optional.empty();
     private final RecyclerView mListView;
     private final AccessibilityTargetAdapter mAdapter;
     private float mFadeOutValue;
@@ -161,6 +171,17 @@
         int RIGHT = 1;
     }
 
+    /**
+     * Interface for a callback to be invoked when the floating menu was dragging.
+     */
+    interface OnDragEndListener {
+
+        /**
+         * Invoked when the floating menu has dragged end.
+         */
+        void onDragEnd();
+    }
+
     public AccessibilityFloatingMenuView(Context context) {
         this(context, new RecyclerView(context));
     }
@@ -201,6 +222,8 @@
                 updateRadiusWith(mSizeType, mRadiusType, mTargets.size());
 
                 fadeOut();
+
+                mOnDragEndListener.ifPresent(OnDragEndListener::onDragEnd);
             }
         });
 
@@ -266,7 +289,7 @@
 
                 // Must switch the oval shape type before tapping the corresponding item in the
                 // list view, otherwise it can't work on it.
-                if (mShapeType == ShapeType.HALF_OVAL) {
+                if (!isOvalShape()) {
                     setShapeType(ShapeType.OVAL);
 
                     return true;
@@ -299,10 +322,6 @@
 
     @Override
     public boolean performAccessibilityAction(int action, Bundle arguments) {
-        if (super.performAccessibilityAction(action, arguments)) {
-            return true;
-        }
-
         fadeIn();
 
         final Rect bounds = getAvailableBounds();
@@ -340,7 +359,7 @@
             return true;
         }
 
-        return false;
+        return super.performAccessibilityAction(action, arguments);
     }
 
     void show() {
@@ -367,6 +386,10 @@
         return mIsShowing;
     }
 
+    boolean isOvalShape() {
+        return mShapeType == ShapeType.OVAL;
+    }
+
     void onTargetsChanged(List<AccessibilityTarget> newTargets) {
         fadeIn();
 
@@ -411,6 +434,41 @@
         fadeOut();
     }
 
+    public void setOnDragEndListener(OnDragEndListener onDragListener) {
+        mOnDragEndListener = Optional.ofNullable(onDragListener);
+    }
+
+    void startTranslateXAnimation() {
+        fadeIn();
+
+        final float toXValue = mAlignment == Alignment.RIGHT
+                ? ANIMATION_TO_X_VALUE
+                : -ANIMATION_TO_X_VALUE;
+        final TranslateAnimation animation =
+                new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0,
+                        Animation.RELATIVE_TO_SELF, toXValue,
+                        Animation.RELATIVE_TO_SELF, 0,
+                        Animation.RELATIVE_TO_SELF, 0);
+        animation.setDuration(ANIMATION_DURATION_MS);
+        animation.setRepeatMode(Animation.REVERSE);
+        animation.setInterpolator(new OvershootInterpolator());
+        animation.setRepeatCount(Animation.INFINITE);
+        animation.setStartOffset(ANIMATION_START_OFFSET);
+        mListView.startAnimation(animation);
+    }
+
+    void stopTranslateXAnimation() {
+        mListView.clearAnimation();
+
+        fadeOut();
+    }
+
+    Rect getWindowLocationOnScreen() {
+        final int left = mCurrentLayoutParams.x;
+        final int top = mCurrentLayoutParams.y;
+        return new Rect(left, top, left + getWindowWidth(), top + getWindowHeight());
+    }
+
     void updateOpacityWith(boolean isFadeEffectEnabled, float newOpacityValue) {
         mIsFadeEffectEnabled = isFadeEffectEnabled;
         mFadeOutValue = newOpacityValue;
@@ -534,11 +592,7 @@
     }
 
     private Handler createUiHandler() {
-        final Looper looper = Looper.myLooper();
-        if (looper == null) {
-            throw new IllegalArgumentException("looper must not be null");
-        }
-        return new Handler(looper);
+        return new Handler(requireNonNull(Looper.myLooper(), "looper must not be null"));
     }
 
     private void updateDimensions() {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpan.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpan.java
new file mode 100644
index 0000000..d8e80fe
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpan.java
@@ -0,0 +1,91 @@
+/*
+ * 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.accessibility.floatingmenu;
+
+import android.text.Annotation;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.style.ClickableSpan;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+
+import java.util.Arrays;
+import java.util.Optional;
+
+/**
+ * A span that turns the text wrapped by annotation tag into the clickable link text.
+ */
+class AnnotationLinkSpan extends ClickableSpan {
+    private final Optional<View.OnClickListener> mClickListener;
+
+    private AnnotationLinkSpan(View.OnClickListener listener) {
+        mClickListener = Optional.ofNullable(listener);
+    }
+
+    @Override
+    public void onClick(View view) {
+        mClickListener.ifPresent(listener -> listener.onClick(view));
+    }
+
+    /**
+     * Makes the text has the link with the click action. In addition, the span will match first
+     * LinkInfo and attach into the text.
+     *
+     * @param text the text wrapped by annotation tag
+     * @param linkInfos used to attach the click action into the corresponding span
+     * @return the text attached with the span
+     */
+    static CharSequence linkify(CharSequence text, LinkInfo... linkInfos) {
+        final SpannableString msg = new SpannableString(text);
+        final Annotation[] spans =
+                msg.getSpans(/* queryStart= */ 0, msg.length(), Annotation.class);
+        final SpannableStringBuilder builder = new SpannableStringBuilder(msg);
+
+        Arrays.asList(spans).forEach(annotationTag -> {
+            final String key = annotationTag.getValue();
+            final Optional<LinkInfo> linkInfo =
+                    Arrays.asList(linkInfos).stream().filter(
+                            info -> info.mAnnotation.isPresent()
+                                    && info.mAnnotation.get().equals(key)).findFirst();
+
+            linkInfo.flatMap(info -> info.mListener).ifPresent(listener -> {
+                final AnnotationLinkSpan span = new AnnotationLinkSpan(listener);
+                builder.setSpan(span,
+                        msg.getSpanStart(annotationTag),
+                        msg.getSpanEnd(annotationTag),
+                        msg.getSpanFlags(span));
+            });
+        });
+
+        return builder;
+    }
+
+    /**
+     * Data class to store the annotation and the click action.
+     */
+    static class LinkInfo {
+        static final String DEFAULT_ANNOTATION = "link";
+        private final Optional<String> mAnnotation;
+        private final Optional<View.OnClickListener> mListener;
+
+        LinkInfo(@NonNull String annotation, View.OnClickListener listener) {
+            mAnnotation = Optional.of(annotation);
+            mListener = Optional.ofNullable(listener);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipView.java
new file mode 100644
index 0000000..1abf559
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipView.java
@@ -0,0 +1,297 @@
+/*
+ * 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.accessibility.floatingmenu;
+
+import static android.util.TypedValue.COMPLEX_UNIT_PX;
+import static android.view.View.MeasureSpec.AT_MOST;
+import static android.view.View.MeasureSpec.UNSPECIFIED;
+
+import android.annotation.UiContext;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.CornerPathEffect;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.os.Bundle;
+import android.text.method.MovementMethod;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.settingslib.Utils;
+import com.android.systemui.R;
+import com.android.systemui.recents.TriangleShape;
+
+/**
+ * Base tooltip view that shows the information about the operation of the
+ * Accessibility floating menu. In addition, the anchor view is only for {@link
+ * AccessibilityFloatingMenuView}, it should be more suited for displaying one-off menus to avoid
+ * the performance hit for the extra window.
+ */
+class BaseTooltipView extends FrameLayout {
+    private int mFontSize;
+    private int mTextViewMargin;
+    private int mTextViewPadding;
+    private int mTextViewCornerRadius;
+    private int mArrowMargin;
+    private int mArrowWidth;
+    private int mArrowHeight;
+    private int mArrowCornerRadius;
+    private int mScreenWidth;
+    private boolean mIsShowing;
+    private TextView mTextView;
+    private final WindowManager.LayoutParams mCurrentLayoutParams;
+    private final WindowManager mWindowManager;
+    private final AccessibilityFloatingMenuView mAnchorView;
+
+    BaseTooltipView(@UiContext Context context, AccessibilityFloatingMenuView anchorView) {
+        super(context);
+        mWindowManager = context.getSystemService(WindowManager.class);
+        mAnchorView = anchorView;
+        mCurrentLayoutParams = createDefaultLayoutParams();
+
+        updateDimensions();
+        initViews();
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+
+        updateDimensions();
+        updateTextView();
+
+        mAnchorView.onConfigurationChanged(newConfig);
+        final Rect anchorViewLocation = mAnchorView.getWindowLocationOnScreen();
+        updateArrowWith(anchorViewLocation);
+        updateWidthWith(anchorViewLocation);
+        updateLocationWith(anchorViewLocation);
+
+        mWindowManager.updateViewLayout(this, mCurrentLayoutParams);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+            hide();
+        }
+
+        return super.onTouchEvent(event);
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+
+        info.addAction(AccessibilityAction.ACTION_DISMISS);
+    }
+
+    @Override
+    public boolean performAccessibilityAction(int action, Bundle arguments) {
+        if (action == AccessibilityAction.ACTION_DISMISS.getId()) {
+            hide();
+            return true;
+        }
+
+        return super.performAccessibilityAction(action, arguments);
+    }
+
+    void show() {
+        if (isShowing()) {
+            return;
+        }
+
+        mIsShowing = true;
+        final Rect anchorViewLocation = mAnchorView.getWindowLocationOnScreen();
+        updateArrowWith(anchorViewLocation);
+        updateWidthWith(anchorViewLocation);
+        updateLocationWith(anchorViewLocation);
+
+        mWindowManager.addView(this, mCurrentLayoutParams);
+    }
+
+    void hide() {
+        if (!isShowing()) {
+            return;
+        }
+
+        mIsShowing = false;
+        mWindowManager.removeView(this);
+    }
+
+    void setDescription(CharSequence text) {
+        mTextView.setText(text);
+    }
+
+    void setMovementMethod(MovementMethod movement) {
+        mTextView.setMovementMethod(movement);
+    }
+
+    private boolean isShowing() {
+        return mIsShowing;
+    }
+
+    private void initViews() {
+        final View contentView =
+                LayoutInflater.from(getContext()).inflate(
+                        R.layout.accessibility_floating_menu_tooltip, this, false);
+
+        mTextView = contentView.findViewById(R.id.text);
+
+        addView(contentView);
+    }
+
+    private static WindowManager.LayoutParams createDefaultLayoutParams() {
+        final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+                WindowManager.LayoutParams.WRAP_CONTENT,
+                WindowManager.LayoutParams.WRAP_CONTENT,
+                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
+                PixelFormat.TRANSLUCENT);
+        params.windowAnimations = android.R.style.Animation_Translucent;
+        params.gravity = Gravity.START | Gravity.TOP;
+
+        return params;
+    }
+
+    private void updateDimensions() {
+        final Resources res = getResources();
+        final DisplayMetrics dm = res.getDisplayMetrics();
+        mScreenWidth = dm.widthPixels;
+        mArrowWidth =
+                res.getDimensionPixelSize(R.dimen.accessibility_floating_tooltip_arrow_width);
+        mArrowHeight =
+                res.getDimensionPixelSize(R.dimen.accessibility_floating_tooltip_arrow_height);
+        mArrowMargin =
+                res.getDimensionPixelSize(
+                        R.dimen.accessibility_floating_tooltip_arrow_margin);
+        mArrowCornerRadius =
+                res.getDimensionPixelSize(
+                        R.dimen.accessibility_floating_tooltip_arrow_corner_radius);
+        mFontSize =
+                res.getDimensionPixelSize(R.dimen.accessibility_floating_tooltip_font_size);
+        mTextViewMargin =
+                res.getDimensionPixelSize(R.dimen.accessibility_floating_tooltip_margin);
+        mTextViewPadding =
+                res.getDimensionPixelSize(R.dimen.accessibility_floating_tooltip_padding);
+        mTextViewCornerRadius =
+                res.getDimensionPixelSize(
+                        R.dimen.accessibility_floating_tooltip_text_corner_radius);
+    }
+
+    private void updateTextView() {
+        mTextView.setTextSize(COMPLEX_UNIT_PX, mFontSize);
+        mTextView.setPadding(mTextViewPadding, mTextViewPadding, mTextViewPadding,
+                mTextViewPadding);
+
+        final GradientDrawable gradientDrawable = (GradientDrawable) mTextView.getBackground();
+        gradientDrawable.setCornerRadius(mTextViewCornerRadius);
+    }
+
+    private void updateArrowWith(Rect anchorViewLocation) {
+        final boolean isAnchorViewOnLeft = isAnchorViewOnLeft(anchorViewLocation);
+        final View arrowView = findViewById(isAnchorViewOnLeft
+                ? R.id.arrow_left
+                : R.id.arrow_right);
+        arrowView.setVisibility(VISIBLE);
+        drawArrow(arrowView, isAnchorViewOnLeft);
+
+        final LinearLayout.LayoutParams layoutParams =
+                (LinearLayout.LayoutParams) arrowView.getLayoutParams();
+        layoutParams.width = mArrowWidth;
+        layoutParams.height = mArrowHeight;
+
+        final int leftMargin = isAnchorViewOnLeft ? 0 : mArrowMargin;
+        final int rightMargin = isAnchorViewOnLeft ? mArrowMargin : 0;
+        layoutParams.setMargins(leftMargin, 0, rightMargin, 0);
+        arrowView.setLayoutParams(layoutParams);
+    }
+
+    private void updateWidthWith(Rect anchorViewLocation) {
+        final ViewGroup.LayoutParams layoutParams = mTextView.getLayoutParams();
+        layoutParams.width = getTextWidthWith(anchorViewLocation);
+        mTextView.setLayoutParams(layoutParams);
+    }
+
+    private void updateLocationWith(Rect anchorViewLocation) {
+        mCurrentLayoutParams.x = isAnchorViewOnLeft(anchorViewLocation)
+                ? anchorViewLocation.width()
+                : mScreenWidth - getWindowWidthWith(anchorViewLocation)
+                        - anchorViewLocation.width();
+        mCurrentLayoutParams.y =
+                anchorViewLocation.centerY() - (getTextHeightWith(anchorViewLocation) / 2);
+    }
+
+    private void drawArrow(View view, boolean isPointingLeft) {
+        final ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+        final TriangleShape triangleShape =
+                TriangleShape.createHorizontal(layoutParams.width, layoutParams.height,
+                        isPointingLeft);
+        final ShapeDrawable arrowDrawable = new ShapeDrawable(triangleShape);
+        final Paint arrowPaint = arrowDrawable.getPaint();
+        arrowPaint.setColor(Utils.getColorAttrDefaultColor(getContext(),
+                com.android.internal.R.attr.colorAccentPrimary));
+        final CornerPathEffect effect = new CornerPathEffect(mArrowCornerRadius);
+        arrowPaint.setPathEffect(effect);
+        view.setBackground(arrowDrawable);
+    }
+
+    private boolean isAnchorViewOnLeft(Rect anchorViewLocation) {
+        return anchorViewLocation.left < (mScreenWidth / 2);
+    }
+
+    private int getTextWidthWith(Rect anchorViewLocation) {
+        final int widthSpec =
+                MeasureSpec.makeMeasureSpec(getAvailableTextWidthWith(anchorViewLocation), AT_MOST);
+        final int heightSpec =
+                MeasureSpec.makeMeasureSpec(0, UNSPECIFIED);
+        mTextView.measure(widthSpec, heightSpec);
+        return mTextView.getMeasuredWidth();
+    }
+
+    private int getTextHeightWith(Rect anchorViewLocation) {
+        final int widthSpec =
+                MeasureSpec.makeMeasureSpec(getAvailableTextWidthWith(anchorViewLocation), AT_MOST);
+        final int heightSpec =
+                MeasureSpec.makeMeasureSpec(0, UNSPECIFIED);
+        mTextView.measure(widthSpec, heightSpec);
+        return mTextView.getMeasuredHeight();
+    }
+
+    private int getAvailableTextWidthWith(Rect anchorViewLocation) {
+        return mScreenWidth - anchorViewLocation.width() - mArrowWidth - mArrowMargin
+                - mTextViewMargin;
+    }
+
+    private int getWindowWidthWith(Rect anchorViewLocation) {
+        return getTextWidthWith(anchorViewLocation) + mArrowWidth + mArrowMargin;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DockTooltipView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DockTooltipView.java
new file mode 100644
index 0000000..49056a6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DockTooltipView.java
@@ -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.systemui.accessibility.floatingmenu;
+
+import android.content.Context;
+
+import com.android.systemui.R;
+
+/**
+ * Dock tooltip view that shows the info about moving the Accessibility button to the edge to hide.
+ */
+class DockTooltipView extends BaseTooltipView {
+    private final AccessibilityFloatingMenuView mAnchorView;
+
+    DockTooltipView(Context context, AccessibilityFloatingMenuView anchorView) {
+        super(context, anchorView);
+        mAnchorView = anchorView;
+
+        setDescription(
+                getContext().getText(R.string.accessibility_floating_button_docking_tooltip));
+    }
+
+    @Override
+    void hide() {
+        super.hide();
+
+        mAnchorView.stopTranslateXAnimation();
+    }
+
+    @Override
+    void show() {
+        super.show();
+
+        mAnchorView.startTranslateXAnimation();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipView.java
new file mode 100644
index 0000000..e4f3e31
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipView.java
@@ -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.accessibility.floatingmenu;
+
+import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_BUTTON_COMPONENT_NAME;
+
+import android.content.Context;
+import android.content.Intent;
+import android.provider.Settings;
+import android.text.method.LinkMovementMethod;
+
+import com.android.systemui.R;
+
+/**
+ * Migration tooltip view that shows the information about the Accessibility button was replaced
+ * with the floating menu.
+ */
+class MigrationTooltipView extends BaseTooltipView {
+    MigrationTooltipView(Context context, AccessibilityFloatingMenuView anchorView) {
+        super(context, anchorView);
+
+        final Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_DETAILS_SETTINGS);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.putExtra(Intent.EXTRA_COMPONENT_NAME,
+                ACCESSIBILITY_BUTTON_COMPONENT_NAME.flattenToShortString());
+
+        final AnnotationLinkSpan.LinkInfo linkInfo = new AnnotationLinkSpan.LinkInfo(
+                AnnotationLinkSpan.LinkInfo.DEFAULT_ANNOTATION,
+                v -> {
+                    getContext().startActivity(intent);
+                    hide();
+                });
+
+        final int textResId = R.string.accessibility_floating_button_migration_tooltip;
+        setDescription(AnnotationLinkSpan.linkify(getContext().getText(textResId), linkInfo));
+        setMovementMethod(LinkMovementMethod.getInstance());
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 5a50f0e..875bfdb 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -59,7 +59,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.R;
-import com.android.systemui.biometrics.HbmTypes.HbmType;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.doze.DozeReceiver;
@@ -72,6 +71,8 @@
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
+import java.util.Optional;
+
 import javax.inject.Inject;
 
 /**
@@ -87,7 +88,7 @@
  */
 @SuppressWarnings("deprecation")
 @SysUISingleton
-public class UdfpsController implements DozeReceiver, HbmCallback {
+public class UdfpsController implements DozeReceiver {
     private static final String TAG = "UdfpsController";
     private static final long AOD_INTERRUPT_TIMEOUT_MILLIS = 1000;
 
@@ -110,6 +111,7 @@
     @NonNull private final FalsingManager mFalsingManager;
     @NonNull private final PowerManager mPowerManager;
     @NonNull private final AccessibilityManager mAccessibilityManager;
+    @Nullable private final UdfpsHbmCallback mHbmCallback;
     // 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.
     @VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps;
@@ -466,7 +468,8 @@
             @NonNull PowerManager powerManager,
             @NonNull AccessibilityManager accessibilityManager,
             @NonNull ScreenLifecycle screenLifecycle,
-            @Nullable Vibrator vibrator) {
+            @Nullable Vibrator vibrator,
+            @NonNull Optional<UdfpsHbmCallback> hbmCallback) {
         mContext = context;
         // TODO (b/185124905): inject main handler and vibrator once done prototyping
         mMainHandler = new Handler(Looper.getMainLooper());
@@ -486,6 +489,7 @@
         mFalsingManager = falsingManager;
         mPowerManager = powerManager;
         mAccessibilityManager = accessibilityManager;
+        mHbmCallback = hbmCallback.orElse(null);
         screenLifecycle.addObserver(mScreenObserver);
         mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON;
 
@@ -619,7 +623,7 @@
                     Log.v(TAG, "showUdfpsOverlay | adding window reason=" + reason);
                     mView = (UdfpsView) mInflater.inflate(R.layout.udfps_view, null, false);
                     mView.setSensorProperties(mSensorProps);
-                    mView.setHbmCallback(this);
+                    mView.setHbmCallback(mHbmCallback);
                     UdfpsAnimationViewController animation = inflateUdfpsAnimation(reason);
                     animation.init();
                     mView.setAnimationViewController(animation);
@@ -791,17 +795,6 @@
         mView.stopIllumination();
     }
 
-    @Override
-    public void enableHbm(@HbmType int hbmType, @Nullable Surface surface) {
-        // Do nothing. This method can be implemented for devices that require the high-brightness
-        // mode for fingerprint illumination.
-    }
-
-    @Override
-    public void disableHbm(@HbmType int hbmType, @Nullable Surface surface) {
-        // Do nothing. This method can be implemented for devices that require the high-brightness
-        // mode for fingerprint illumination.
-    }
 
     private VibrationEffect getVibration(String effect, VibrationEffect defaultEffect) {
         if (TextUtils.isEmpty(effect)) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/HbmCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmCallback.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/biometrics/HbmCallback.java
rename to packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmCallback.java
index d90d0f8..85f0d27 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/HbmCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmCallback.java
@@ -19,18 +19,18 @@
 import android.annotation.Nullable;
 import android.view.Surface;
 
-import com.android.systemui.biometrics.HbmTypes.HbmType;
+import com.android.systemui.biometrics.UdfpsHbmTypes.HbmType;
 
 /**
  * Interface for controlling the high-brightness mode (HBM). UdfpsView can use this callback to
  * enable the HBM while showing the fingerprint illumination, and to disable the HBM after the
  * illumination is no longer necessary.
  */
-public interface HbmCallback {
+public interface UdfpsHbmCallback {
     /**
      * UdfpsView will call this to enable the HBM when the fingerprint illumination is needed.
      *
-     * @param hbmType The type of HBM that should be enabled. See {@link HbmTypes}.
+     * @param hbmType The type of HBM that should be enabled. See {@link UdfpsHbmTypes}.
      * @param surface The surface for which the HBM is requested, in case the HBM implementation
      *                needs to set special surface flags to enable the HBM. Can be null.
      */
@@ -39,7 +39,7 @@
     /**
      * UdfpsView will call this to disable the HBM when the illumination is not longer needed.
      *
-     * @param hbmType The type of HBM that should be disabled. See {@link HbmTypes}.
+     * @param hbmType The type of HBM that should be disabled. See {@link UdfpsHbmTypes}.
      * @param surface The surface for which the HBM is requested, in case the HBM implementation
      *                needs to unset special surface flags to disable the HBM. Can be null.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/HbmTypes.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmTypes.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/biometrics/HbmTypes.java
rename to packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmTypes.java
index f798005..3ab0bd6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/HbmTypes.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmTypes.java
@@ -25,7 +25,7 @@
 /**
  * Different high-brightness mode (HBM) types that are relevant to this package.
  */
-public final class HbmTypes {
+public final class UdfpsHbmTypes {
     /** HBM that applies to the whole screen. */
     public static final int GLOBAL_HBM = IUdfpsHbmListener.GLOBAL_HBM;
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java
index 8bea05b..1676bcd 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java
@@ -26,7 +26,7 @@
     /**
      * @param callback Invoked when HBM should be enabled or disabled.
      */
-    void setHbmCallback(@Nullable HbmCallback callback);
+    void setHbmCallback(@Nullable UdfpsHbmCallback callback);
 
     /**
      * Invoked when illumination should start.
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
index 4d441bd..aa5f0f6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
@@ -31,7 +31,7 @@
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
 
-import com.android.systemui.biometrics.HbmTypes.HbmType;
+import com.android.systemui.biometrics.UdfpsHbmTypes.HbmType;
 
 /**
  * Under-display fingerprint sensor Surface View. The surface should be used for HBM-specific things
@@ -41,7 +41,7 @@
     private static final String TAG = "UdfpsSurfaceView";
     private static final String SETTING_HBM_TYPE =
             "com.android.systemui.biometrics.UdfpsSurfaceView.hbmType";
-    private static final @HbmType int DEFAULT_HBM_TYPE = HbmTypes.GLOBAL_HBM;
+    private static final @HbmType int DEFAULT_HBM_TYPE = UdfpsHbmTypes.GLOBAL_HBM;
 
     /**
      * This is used instead of {@link android.graphics.drawable.Drawable}, because the latter has
@@ -57,7 +57,7 @@
     private final @HbmType int mHbmType;
 
     @NonNull private RectF mSensorRect;
-    @Nullable private HbmCallback mHbmCallback;
+    @Nullable private UdfpsHbmCallback mHbmCallback;
 
     public UdfpsSurfaceView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -90,7 +90,7 @@
     }
 
     @Override
-    public void setHbmCallback(@Nullable HbmCallback callback) {
+    public void setHbmCallback(@Nullable UdfpsHbmCallback callback) {
         mHbmCallback = callback;
     }
 
@@ -102,7 +102,7 @@
             Log.e(TAG, "startIllumination | mHbmCallback is null");
         }
 
-        if (mHbmType == HbmTypes.GLOBAL_HBM) {
+        if (mHbmType == UdfpsHbmTypes.GLOBAL_HBM) {
             drawImmediately(mIlluminationDotDrawable);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index f10d5f3..a1d3040 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -101,7 +101,7 @@
     }
 
     @Override
-    public void setHbmCallback(@Nullable HbmCallback callback) {
+    public void setHbmCallback(@Nullable UdfpsHbmCallback callback) {
         mHbmSurfaceView.setHbmCallback(callback);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 26be987..a904cef 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -120,7 +120,7 @@
     private val onSeedingComplete = Consumer<Boolean> {
         accepted ->
             if (accepted) {
-                selectedStructure = controlsController.get().getFavorites().maxBy {
+                selectedStructure = controlsController.get().getFavorites().maxByOrNull {
                     it.controls.size
                 } ?: EMPTY_STRUCTURE
                 updatePreferences(selectedStructure)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 7fa48d4..1396099 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -29,6 +29,7 @@
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.appops.dagger.AppOpsModule;
 import com.android.systemui.assist.AssistModule;
+import com.android.systemui.biometrics.UdfpsHbmCallback;
 import com.android.systemui.classifier.FalsingModule;
 import com.android.systemui.controls.dagger.ControlsModule;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -160,6 +161,9 @@
     @BindsOptionalOf
     abstract StatusBar optionalStatusBar();
 
+    @BindsOptionalOf
+    abstract UdfpsHbmCallback optionalUdfpsHbmCallback();
+
     @SysUISingleton
     @Binds
     abstract SystemClock bindSystemClock(SystemClockImpl systemClock);
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index a83b13c..3873c25 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -226,7 +226,7 @@
         ActionsDialog dialog = new ActionsDialog(getContext(), mAdapter, mOverflowAdapter,
                 this::getWalletViewController, mDepthController, mSysuiColorExtractor,
                 mStatusBarService, mNotificationShadeWindowController,
-                mSysUiState, this::onRotate, isKeyguardShowing(), mPowerAdapter);
+                mSysUiState, this::onRotate, isKeyguardShowing(), mPowerAdapter, getEventLogger());
 
         if (shouldShowLockMessage(dialog)) {
             dialog.showLockMessage();
@@ -294,11 +294,11 @@
                 SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
                 NotificationShadeWindowController notificationShadeWindowController,
                 SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
-                MyPowerOptionsAdapter powerAdapter) {
+                MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger) {
             super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions,
                     adapter, overflowAdapter, depthController, sysuiColorExtractor,
                     statusBarService, notificationShadeWindowController, sysuiState,
-                    onRotateCallback, keyguardShowing, powerAdapter);
+                    onRotateCallback, keyguardShowing, powerAdapter, uiEventLogger);
             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 2eb3762..f9bb35fc 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -249,7 +249,43 @@
         GA_SCREENSHOT_PRESS(347),
 
         @UiEvent(doc = "The global actions screenshot button was long pressed.")
-        GA_SCREENSHOT_LONG_PRESS(348);
+        GA_SCREENSHOT_LONG_PRESS(348),
+
+        @UiEvent(doc = "The global actions power off button was pressed.")
+        GA_SHUTDOWN_PRESS(802),
+
+        @UiEvent(doc = "The global actions power off button was long pressed.")
+        GA_SHUTDOWN_LONG_PRESS(803),
+
+        @UiEvent(doc = "The global actions reboot button was pressed.")
+        GA_REBOOT_PRESS(349),
+
+        @UiEvent(doc = "The global actions reboot button was long pressed.")
+        GA_REBOOT_LONG_PRESS(804),
+
+        @UiEvent(doc = "The global actions lockdown button was pressed.")
+        GA_LOCKDOWN_PRESS(354), // already created by cwren apparently
+
+        @UiEvent(doc = "Power menu was opened via quick settings button.")
+        GA_OPEN_QS(805),
+
+        @UiEvent(doc = "Power menu was opened via power + volume up.")
+        GA_OPEN_POWER_VOLUP(806),
+
+        @UiEvent(doc = "Power menu was opened via long press on power.")
+        GA_OPEN_LONG_PRESS_POWER(807),
+
+        @UiEvent(doc = "Power menu was closed via long press on power.")
+        GA_CLOSE_LONG_PRESS_POWER(808),
+
+        @UiEvent(doc = "Power menu was dismissed by back gesture.")
+        GA_CLOSE_BACK(809),
+
+        @UiEvent(doc = "Power menu was dismissed by tapping outside dialog.")
+        GA_CLOSE_TAP_OUTSIDE(810),
+
+        @UiEvent(doc = "Power menu was closed via power + volume up.")
+        GA_CLOSE_POWER_VOLUP(811);
 
         private final int mId;
 
@@ -349,6 +385,10 @@
         return mContext;
     }
 
+    protected UiEventLogger getEventLogger() {
+        return mUiEventLogger;
+    }
+
     /**
      * Show the global actions dialog (creating if necessary)
      *
@@ -581,7 +621,7 @@
                 mAdapter, mOverflowAdapter,
                 mDepthController, mSysuiColorExtractor,
                 mStatusBarService, mNotificationShadeWindowController,
-                mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter);
+                mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter, mUiEventLogger);
 
         dialog.setOnDismissListener(this);
         dialog.setOnShowListener(this);
@@ -679,13 +719,14 @@
 
     @VisibleForTesting
     final class ShutDownAction extends SinglePressAction implements LongPressAction {
-        private ShutDownAction() {
+        ShutDownAction() {
             super(R.drawable.ic_lock_power_off,
                     R.string.global_action_power_off);
         }
 
         @Override
         public boolean onLongPress() {
+            mUiEventLogger.log(GlobalActionsEvent.GA_SHUTDOWN_LONG_PRESS);
             if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
                 mWindowManagerFuncs.reboot(true);
                 return true;
@@ -705,6 +746,7 @@
 
         @Override
         public void onPress() {
+            mUiEventLogger.log(GlobalActionsEvent.GA_SHUTDOWN_PRESS);
             // shutdown by making sure radio and power are handled accordingly.
             mWindowManagerFuncs.shutdown();
         }
@@ -807,12 +849,13 @@
 
     @VisibleForTesting
     final class RestartAction extends SinglePressAction implements LongPressAction {
-        private RestartAction() {
+        RestartAction() {
             super(R.drawable.ic_restart, R.string.global_action_restart);
         }
 
         @Override
         public boolean onLongPress() {
+            mUiEventLogger.log(GlobalActionsEvent.GA_REBOOT_LONG_PRESS);
             if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
                 mWindowManagerFuncs.reboot(true);
                 return true;
@@ -832,6 +875,7 @@
 
         @Override
         public void onPress() {
+            mUiEventLogger.log(GlobalActionsEvent.GA_REBOOT_PRESS);
             mWindowManagerFuncs.reboot(false);
         }
     }
@@ -1062,6 +1106,7 @@
         public void onPress() {
             mLockPatternUtils.requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN,
                     UserHandle.USER_ALL);
+            mUiEventLogger.log(GlobalActionsEvent.GA_LOCKDOWN_PRESS);
             try {
                 mIWindowManager.lockNow(null);
                 // Lock profiles (if any) on the background thread.
@@ -2049,6 +2094,7 @@
         private ListPopupWindow mOverflowPopup;
         private Dialog mPowerOptionsDialog;
         protected final Runnable mOnRotateCallback;
+        private UiEventLogger mUiEventLogger;
 
         protected ViewGroup mContainer;
 
@@ -2058,7 +2104,7 @@
                 SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
                 NotificationShadeWindowController notificationShadeWindowController,
                 SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
-                MyPowerOptionsAdapter powerAdapter) {
+                MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger) {
             super(context, themeRes);
             mContext = context;
             mAdapter = adapter;
@@ -2071,6 +2117,7 @@
             mSysUiState = sysuiState;
             mOnRotateCallback = onRotateCallback;
             mKeyguardShowing = keyguardShowing;
+            mUiEventLogger = uiEventLogger;
 
             // Window initialization
             Window window = getWindow();
@@ -2141,7 +2188,7 @@
             mGlobalActionsLayout.setAdapter(mAdapter);
             mContainer = findViewById(com.android.systemui.R.id.global_actions_container);
             mContainer.setOnClickListener(v -> {
-                // TODO: add logging (b/182830510)
+                mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
                 cancel();
             });
 
@@ -2218,6 +2265,12 @@
         }
 
         @Override
+        public void onBackPressed() {
+            super.onBackPressed();
+            mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_BACK);
+        }
+
+        @Override
         public void show() {
             super.show();
             // split this up so we can override but still call Dialog.show
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index 644876c..ef53233 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -115,7 +115,7 @@
     private var needsReordering: Boolean = false
     private var keysNeedRemoval = mutableSetOf<String>()
     private var bgColor = getBackgroundColor()
-    private var shouldScrollToActivePlayer: Boolean = false
+    protected var shouldScrollToActivePlayer: Boolean = false
     private var isRtl: Boolean = false
         set(value) {
             if (value != field) {
@@ -184,7 +184,14 @@
                 true /* persistent */)
         mediaManager.addListener(object : MediaDataManager.Listener {
             override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
-                addOrUpdatePlayer(key, oldKey, data)
+                if (addOrUpdatePlayer(key, oldKey, data)) {
+                    MediaPlayerData.getMediaPlayer(key, null)?.let {
+                        logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED
+                                it.mInstanceId,
+                                /* isRecommendationCard */ false,
+                                it.surfaceForSmartspaceLogging)
+                    }
+                }
                 val canRemove = data.isPlaying?.let { !it } ?: data.isClearable && !data.active
                 if (canRemove && !Utils.useMediaResumption(context)) {
                     // This view isn't playing, let's remove this! This happens e.g when
@@ -203,6 +210,12 @@
             override fun onSmartspaceMediaDataLoaded(key: String, data: SmartspaceTarget) {
                 Log.d(TAG, "My Smartspace media update is here")
                 addSmartspaceMediaRecommendations(key, data)
+                MediaPlayerData.getMediaPlayer(key, null)?.let {
+                    logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED
+                            it.mInstanceId,
+                            /* isRecommendationCard */ true,
+                            it.surfaceForSmartspaceLogging)
+                }
                 if (mediaCarouselScrollHandler.visibleToUser) {
                     logSmartspaceImpression()
                 }
@@ -276,7 +289,8 @@
         }
     }
 
-    private fun addOrUpdatePlayer(key: String, oldKey: String?, data: MediaData) {
+    // Returns true if new player is added
+    private fun addOrUpdatePlayer(key: String, oldKey: String?, data: MediaData): Boolean {
         val dataCopy = data.copy(backgroundColor = bgColor)
         val existingPlayer = MediaPlayerData.getMediaPlayer(key, oldKey)
         if (existingPlayer == null) {
@@ -295,7 +309,7 @@
         } else {
             existingPlayer.bindPlayer(dataCopy, key)
             MediaPlayerData.addMediaPlayer(key, dataCopy, existingPlayer)
-            if (visualStabilityManager.isReorderingAllowed) {
+            if (visualStabilityManager.isReorderingAllowed || shouldScrollToActivePlayer) {
                 reorderAllPlayers()
             } else {
                 needsReordering = true
@@ -309,6 +323,7 @@
         if (MediaPlayerData.players().size != mediaContent.childCount) {
             Log.wtf(TAG, "Size of players list and number of views in carousel are out of sync")
         }
+        return existingPlayer == null
     }
 
     private fun addSmartspaceMediaRecommendations(key: String, data: SmartspaceTarget) {
@@ -325,7 +340,7 @@
         val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
             ViewGroup.LayoutParams.WRAP_CONTENT)
         newRecs.recommendationViewHolder?.recommendations?.setLayoutParams(lp)
-        newRecs.bindRecommendation(data, bgColor, { v -> shouldScrollToActivePlayer = true })
+        newRecs.bindRecommendation(data, bgColor)
         MediaPlayerData.addMediaRecommendation(key, newRecs)
         updatePlayerToState(newRecs, noAnimation = true)
         reorderAllPlayers()
@@ -520,12 +535,20 @@
             this.desiredLocation = desiredLocation
             this.desiredHostState = it
             currentlyExpanded = it.expansion > 0
+
+            val shouldCloseGuts = !currentlyExpanded && !mediaManager.hasActiveMedia() &&
+                    desiredHostState.showsOnlyActiveMedia
+
             for (mediaPlayer in MediaPlayerData.players()) {
                 if (animate) {
                     mediaPlayer.mediaViewController.animatePendingStateChange(
                             duration = duration,
                             delay = startDelay)
                 }
+                if (shouldCloseGuts && mediaPlayer.mediaViewController.isGutsVisible) {
+                    mediaPlayer.closeGuts(!animate)
+                }
+
                 mediaPlayer.mediaViewController.onLocationPreChange(desiredLocation)
             }
             mediaCarouselScrollHandler.showsSettingsButton = !it.showsOnlyActiveMedia
@@ -541,9 +564,9 @@
         }
     }
 
-    fun closeGuts() {
+    fun closeGuts(immediate: Boolean = true) {
         MediaPlayerData.players().forEach {
-            it.closeGuts(true)
+            it.closeGuts(immediate)
         }
     }
 
@@ -641,7 +664,7 @@
 @VisibleForTesting
 internal object MediaPlayerData {
     private val EMPTY = MediaData(-1, false, 0, null, null, null, null, null,
-        emptyList(), emptyList(), "INVALID", null, null, null, false, null)
+        emptyList(), emptyList(), "INVALID", null, null, null, true, null)
 
     data class MediaSortKey(
         // Is Smartspace media recommendation. When the Smartspace media is present, it should
@@ -694,7 +717,7 @@
     /** Returns the index of the first non-timeout media. */
     fun getActiveMediaIndex(): Int {
         mediaPlayers.entries.forEachIndexed { index, e ->
-            if (e.key.data.active) {
+            if (!e.key.isSsMediaRec && e.key.data.active) {
                 return index
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
index e5a6271..c806bcf 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
@@ -59,7 +59,7 @@
     private val mainExecutor: DelayableExecutor,
     private val dismissCallback: () -> Unit,
     private var translationChangedListener: () -> Unit,
-    private val closeGuts: () -> Unit,
+    private val closeGuts: (immediate: Boolean) -> Unit,
     private val falsingCollector: FalsingCollector,
     private val falsingManager: FalsingManager,
     private val logSmartspaceImpression: () -> Unit
@@ -473,7 +473,7 @@
             if (oldIndex != visibleMediaIndex && visibleToUser) {
                 logSmartspaceImpression()
             }
-            closeGuts()
+            closeGuts(false)
             updatePlayerVisibilities()
         }
         val relativeLocation = visibleMediaIndex.toFloat() + if (playerWidthPlusPadding > 0)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index fe3463f..3e9559b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -25,7 +25,8 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.res.ColorStateList;
-import android.graphics.PorterDuff;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
@@ -212,7 +213,8 @@
                 mMediaViewController.openGuts();
                 return true;
             } else {
-                return false;
+                closeGuts();
+                return true;
             }
         });
         mPlayerViewHolder.getCancel().setOnClickListener(v -> {
@@ -276,7 +278,7 @@
                 if (mMediaViewController.isGutsVisible()) return;
 
                 logSmartspaceCardReported(760, // SMARTSPACE_CARD_CLICK
-                        false);
+                        /* isRecommendationCard */ false);
                 mActivityStarter.postStartActivityDismissingKeyguard(clickIntent,
                         buildLaunchAnimatorController(mPlayerViewHolder.getPlayer()));
             });
@@ -384,7 +386,7 @@
                 button.setEnabled(true);
                 button.setOnClickListener(v -> {
                     logSmartspaceCardReported(760, // SMARTSPACE_CARD_CLICK
-                            false);
+                            /* isRecommendationCard */ false);
                     action.run();
                 });
             }
@@ -418,7 +420,7 @@
         mPlayerViewHolder.getDismiss().setEnabled(isDismissible);
         mPlayerViewHolder.getDismiss().setOnClickListener(v -> {
             logSmartspaceCardReported(761, // SMARTSPACE_CARD_DISMISS
-                    false);
+                    /* isRecommendationCard */ false);
 
             if (mKey != null) {
                 closeGuts();
@@ -472,10 +474,7 @@
     }
 
     /** Bind this recommendation view based on the data given. */
-    public void bindRecommendation(
-            @NonNull SmartspaceTarget target,
-            @NonNull int backgroundColor,
-            @Nullable View.OnClickListener callback) {
+    public void bindRecommendation(@NonNull SmartspaceTarget target, @NonNull int backgroundColor) {
         if (mRecommendationViewHolder == null) {
             return;
         }
@@ -515,8 +514,9 @@
                 // Get the logo from app's package name when applicable.
                 String packageName = extras.getString(EXTRAS_MEDIA_SOURCE_PACKAGE_NAME);
                 try {
-                    icon = mContext.getPackageManager().getApplicationIcon(
+                    Drawable drawable = mContext.getPackageManager().getApplicationIcon(
                             packageName);
+                    icon = convertToGrayscale(drawable);
                 } catch (PackageManager.NameNotFoundException e) {
                     Log.w(TAG, "No media source icon can be fetched via package name", e);
                 }
@@ -528,18 +528,13 @@
             // Set up media source app's logo.
             ImageView mediaSourceLogoImageView = mediaLogoItems.get(uiComponentIndex);
             mediaSourceLogoImageView.setImageDrawable(icon);
-            // TODO(b/186699032): Tint the app logo using the accent color.
-            mediaSourceLogoImageView.setColorFilter(backgroundColor, PorterDuff.Mode.XOR);
 
             // Set up media item cover.
             ImageView mediaCoverImageView = mediaCoverItems.get(uiComponentIndex);
             mediaCoverImageView.setImageIcon(recommendation.getIcon());
 
             // Set up the click listener if applicable.
-            setSmartspaceRecItemOnClickListener(
-                    mediaCoverImageView,
-                    recommendation,
-                    callback);
+            setSmartspaceRecItemOnClickListener(mediaCoverImageView, recommendation);
 
             if (uiComponentIndex < MEDIA_RECOMMENDATION_ITEMS_PER_ROW) {
                 setVisibleAndAlpha(collapsedSet,
@@ -563,7 +558,7 @@
         // Set up long press to show guts setting panel.
         mRecommendationViewHolder.getDismiss().setOnClickListener(v -> {
             logSmartspaceCardReported(761, // SMARTSPACE_CARD_DISMISS
-                    true);
+                    /* isRecommendationCard */ true);
             closeGuts();
             mKeyguardDismissUtil.executeWhenUnlocked(() -> {
                 mMediaDataManagerLazy.get().dismissSmartspaceRecommendation(
@@ -651,6 +646,15 @@
         return (state.getState() == PlaybackState.STATE_PLAYING);
     }
 
+    /** Convert the pass-in source drawable to a grayscale one. */
+    private Drawable convertToGrayscale(Drawable drawable) {
+        ColorMatrix matrix = new ColorMatrix();
+        matrix.setSaturation(0);
+        ColorMatrixColorFilter filter = new ColorMatrixColorFilter(matrix);
+        drawable.setColorFilter(filter);
+        return drawable;
+    }
+
     private void setVisibleAndAlpha(ConstraintSet set, int actionId, boolean visible) {
         set.setVisibility(actionId, visible ? ConstraintSet.VISIBLE : ConstraintSet.GONE);
         set.setAlpha(actionId, visible ? 1.0f : 0.0f);
@@ -658,8 +662,7 @@
 
     private void setSmartspaceRecItemOnClickListener(
             @NonNull View view,
-            @NonNull SmartspaceAction action,
-            @Nullable View.OnClickListener callback) {
+            @NonNull SmartspaceAction action) {
         if (view == null || action == null || action.getIntent() == null) {
             Log.e(TAG, "No tap action can be set up");
             return;
@@ -668,7 +671,7 @@
         view.setOnClickListener(v -> {
             // When media recommendation card is shown, it will always be the top card.
             logSmartspaceCardReported(760, // SMARTSPACE_CARD_CLICK
-                    true);
+                    /* isRecommendationCard */ true);
 
             if (shouldSmartspaceRecItemOpenInForeground(action)) {
                 // Request to unlock the device if the activity needs to be opened in foreground.
@@ -682,9 +685,8 @@
                 view.getContext().startActivity(action.getIntent());
             }
 
-            if (callback != null) {
-                callback.onClick(v);
-            }
+            // Automatically scroll to the active player once the media is loaded.
+            mMediaCarouselController.setShouldScrollToActivePlayer(true);
         });
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index 3c28f6e..60e832a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -273,6 +273,7 @@
                 } else {
                     updateDesiredLocation()
                     qsExpanded = false
+                    closeGuts()
                 }
                 mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = isVisibleToUser()
             }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java
index 2d0d5cd..40f908b 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarOverlayController.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.navigationbar;
 
-import android.annotation.ColorInt;
 import android.content.Context;
 import android.view.View;
 
@@ -46,10 +45,9 @@
     }
 
     /**
-     * Initialize the controller with visibility change callback and light/dark icon color.
+     * Initialize the controller with visibility change callback.
      */
-    public void init(Consumer<Boolean> visibilityChangeCallback, @ColorInt int lightIconColor,
-            @ColorInt int darkIconColor) {}
+    public void init(Consumer<Boolean> visibilityChangeCallback) {}
 
     /**
      * Set whether the view can be shown.
@@ -72,11 +70,6 @@
     public void unregisterListeners() {}
 
     /**
-     * Set the dark intensity for all drawables.
-     */
-    public void setDarkIntensity(float darkIntensity) {}
-
-    /**
      * Return the current view.
      */
     public View getCurrentView() {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
index fbc7c92..b4f8c10 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java
@@ -198,7 +198,6 @@
         }
         mView.getRotationButtonController().setDarkIntensity(darkIntensity);
 
-        Dependency.get(NavigationBarOverlayController.class).setDarkIntensity(darkIntensity);
         for (DarkIntensityListener listener : mDarkIntensityListeners) {
             listener.onDarkIntensity(darkIntensity);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index f82d265d..fcbd596 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -61,7 +61,6 @@
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-import android.view.inputmethod.InputMethodManager;
 import android.widget.FrameLayout;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -326,8 +325,7 @@
         mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);
         mNavBarOverlayController = Dependency.get(NavigationBarOverlayController.class);
         if (mNavBarOverlayController.isNavigationBarOverlayEnabled()) {
-            mNavBarOverlayController.init(
-                    mNavbarOverlayVisibilityChangeCallback, mLightIconColor, mDarkIconColor);
+            mNavBarOverlayController.init(mNavbarOverlayVisibilityChangeCallback);
         }
 
         mConfiguration = new Configuration();
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java b/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java
index 0f66456..b55d86e 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java
@@ -18,7 +18,9 @@
 
 import android.content.ContentProvider;
 import android.content.ContentValues;
+import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Binder;
@@ -27,16 +29,19 @@
 import android.util.Log;
 import android.widget.RemoteViews;
 
+import com.android.systemui.SystemUIAppComponentFactory;
 import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
 import com.android.systemui.shared.system.PeopleProviderUtils;
 
 import javax.inject.Inject;
 
 /** API that returns a People Tile preview. */
-public class PeopleProvider extends ContentProvider {
+public class PeopleProvider extends ContentProvider implements
+        SystemUIAppComponentFactory.ContextInitializer {
     private static final String TAG = "PeopleProvider";
     private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
     private static final String EMPTY_STRING = "";
+    private SystemUIAppComponentFactory.ContextAvailableCallback mCallback;
 
     @Inject
     PeopleSpaceWidgetManager mPeopleSpaceWidgetManager;
@@ -82,8 +87,8 @@
             Log.e(TAG, "Could not initialize people widget manager");
             return null;
         }
-        RemoteViews view =
-                mPeopleSpaceWidgetManager.getPreview(shortcutId, userHandle, packageName, extras);
+        RemoteViews view = mPeopleSpaceWidgetManager.getPreview(shortcutId, userHandle, packageName,
+                extras);
         if (view == null) {
             if (DEBUG) Log.d(TAG, "No preview available for shortcutId: " + shortcutId);
             return null;
@@ -130,5 +135,17 @@
     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
         throw new IllegalArgumentException("Invalid method");
     }
+
+    @Override
+    public void attachInfo(Context context, ProviderInfo info) {
+        mCallback.onContextAvailable(context);
+        super.attachInfo(context, info);
+    }
+
+    @Override
+    public void setContextAvailableCallback(
+            SystemUIAppComponentFactory.ContextAvailableCallback callback) {
+        mCallback = callback;
+    }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 29685a4..7b5ab0d 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -416,9 +416,8 @@
                 && birthdayString == null;
         boolean addBirthdayStatus = !hasBirthdayStatus(storedTile, context)
                 && birthdayString != null;
-        boolean shouldUpdate =
-                storedTile.getContactAffinity() != affinity || outdatedBirthdayStatus
-                        || addBirthdayStatus;
+        boolean shouldUpdate = storedTile.getContactAffinity() != affinity || outdatedBirthdayStatus
+                || addBirthdayStatus;
         if (shouldUpdate) {
             if (DEBUG) Log.d(TAG, "Update " + storedTile.getUserName() + " from contacts");
             manager.updateAppWidgetOptionsAndView(appWidgetId,
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
index 06f8a60..9fc9cad 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
@@ -41,8 +41,6 @@
 import android.app.people.PeopleSpaceTile;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
@@ -57,13 +55,13 @@
 import android.util.IconDrawableFactory;
 import android.util.Log;
 import android.util.Pair;
+import android.view.Gravity;
 import android.view.View;
 import android.widget.RemoteViews;
 import android.widget.TextView;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.launcher3.icons.FastBitmapDrawable;
-import com.android.settingslib.Utils;
 import com.android.systemui.R;
 import com.android.systemui.people.widget.LaunchConversationActivity;
 import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
@@ -195,16 +193,10 @@
      */
     private RemoteViews getViewForTile() {
         if (DEBUG) Log.d(TAG, "Creating view for tile key: " + mKey.toString());
-        if (mTile == null || mTile.isPackageSuspended() || mTile.isUserQuieted()) {
-            if (DEBUG) Log.d(TAG, "Create empty view: " + mTile);
-            return createEmptyView();
-        }
-
-        boolean dndBlockingTileData = isDndBlockingTileData(mTile);
-        if (dndBlockingTileData) {
-            if (DEBUG) Log.d(TAG, "Create DND view: " + mTile.getNotificationPolicyState());
-            // TODO: Create DND view.
-            return createEmptyView();
+        if (mTile == null || mTile.isPackageSuspended() || mTile.isUserQuieted()
+                || isDndBlockingTileData(mTile)) {
+            if (DEBUG) Log.d(TAG, "Create suppressed view: " + mTile);
+            return createSuppressedView();
         }
 
         if (Objects.equals(mTile.getNotificationCategory(), CATEGORY_MISSED_CALL)) {
@@ -265,34 +257,27 @@
         return !tile.canBypassDnd();
     }
 
-    private RemoteViews createEmptyView() {
-        RemoteViews views = new RemoteViews(mContext.getPackageName(),
-                R.layout.people_tile_empty_layout);
-        Drawable appIcon = getAppBadge(mKey.getPackageName(), mKey.getUserId());
+    private RemoteViews createSuppressedView() {
+        RemoteViews views;
+        if (mTile.isUserQuieted()) {
+            views = new RemoteViews(mContext.getPackageName(),
+                    R.layout.people_tile_work_profile_quiet_layout);
+        } else {
+            views = new RemoteViews(mContext.getPackageName(),
+                    R.layout.people_tile_suppressed_layout);
+        }
+        Drawable appIcon = mContext.getDrawable(R.drawable.ic_conversation_icon);
         Bitmap appIconAsBitmap = convertDrawableToBitmap(appIcon);
-        FastBitmapDrawable drawable = new FastBitmapDrawable(
-                appIconAsBitmap);
+        FastBitmapDrawable drawable = new FastBitmapDrawable(appIconAsBitmap);
         drawable.setIsDisabled(true);
         Bitmap convertedBitmap = convertDrawableToBitmap(drawable);
-        views.setImageViewBitmap(R.id.item, convertedBitmap);
+        views.setImageViewBitmap(R.id.icon, convertedBitmap);
         return views;
     }
 
-    private Drawable getAppBadge(String packageName, int userId) {
-        Drawable badge = null;
-        try {
-            final ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfoAsUser(
-                    packageName, PackageManager.GET_META_DATA, userId);
-            badge = Utils.getBadgedIcon(mContext, appInfo);
-        } catch (PackageManager.NameNotFoundException e) {
-            badge = mContext.getPackageManager().getDefaultActivityIcon();
-        }
-        return badge;
-    }
-
     private void setMaxLines(RemoteViews views, boolean showSender) {
         int textSize = mLayoutSize == LAYOUT_LARGE ? getSizeInDp(
-                R.dimen.content_text_size_for_medium)
+                R.dimen.content_text_size_for_large)
                 : getSizeInDp(R.dimen.content_text_size_for_medium);
         int lineHeight = getLineHeight(textSize);
         int notificationContentHeight = getContentHeightForLayout(lineHeight);
@@ -422,9 +407,6 @@
                 views.setViewVisibility(R.id.availability, View.GONE);
             }
 
-            if (mTile.getUserName() != null) {
-                views.setTextViewText(R.id.name, mTile.getUserName().toString());
-            }
             views.setBoolean(R.id.image, "setClipToOutline", true);
             views.setImageViewBitmap(R.id.person_icon,
                     getPersonIconBitmap(mContext, mTile, maxAvatarSize));
@@ -537,25 +519,31 @@
             statusText = getStatusTextByType(status.getActivity());
         }
         views.setViewVisibility(R.id.predefined_icon, View.VISIBLE);
-        views.setViewVisibility(R.id.messages_count, View.GONE);
-        setMaxLines(views, false);
-        // Secondary text color for statuses.
-        views.setColorAttr(R.id.text_content, "setTextColor", android.R.attr.textColorSecondary);
         views.setTextViewText(R.id.text_content, statusText);
+        if (mLayoutSize == LAYOUT_LARGE) {
+            views.setInt(R.id.content, "setGravity", Gravity.BOTTOM);
+        }
 
         Icon statusIcon = status.getIcon();
         if (statusIcon != null) {
-            // No multi-line text with status images on medium layout.
-            views.setViewVisibility(R.id.text_content, View.GONE);
+            // No text content styled text on medium or large.
+            views.setViewVisibility(R.id.scrim_layout, View.VISIBLE);
+            views.setImageViewIcon(R.id.status_icon, statusIcon);
             // Show 1-line subtext on large layout with status images.
             if (mLayoutSize == LAYOUT_LARGE) {
-                views.setViewVisibility(R.id.subtext, View.VISIBLE);
-                views.setTextViewText(R.id.subtext, statusText);
+                if (DEBUG) Log.d(TAG, "Remove name for large");
+                views.setViewVisibility(R.id.name, View.GONE);
+                views.setColorAttr(R.id.text_content, "setTextColor",
+                        android.R.attr.textColorPrimary);
+            } else if (mLayoutSize == LAYOUT_MEDIUM) {
+                views.setViewVisibility(R.id.text_content, View.GONE);
+                views.setTextViewText(R.id.name, statusText);
             }
-            views.setViewVisibility(R.id.image, View.VISIBLE);
-            views.setImageViewIcon(R.id.image, statusIcon);
         } else {
-            views.setViewVisibility(R.id.image, View.GONE);
+            // Secondary text color for statuses without icons.
+            views.setColorAttr(R.id.text_content, "setTextColor",
+                    android.R.attr.textColorSecondary);
+            setMaxLines(views, false);
         }
         // TODO: Set status pre-defined icons
         views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_person);
@@ -741,16 +729,23 @@
             views.setViewVisibility(R.id.name, View.VISIBLE);
             views.setViewVisibility(R.id.text_content, View.VISIBLE);
             views.setViewVisibility(R.id.subtext, View.GONE);
+            views.setViewVisibility(R.id.image, View.GONE);
+            views.setViewVisibility(R.id.scrim_layout, View.GONE);
         }
 
         if (mLayoutSize == LAYOUT_MEDIUM) {
             if (DEBUG) Log.d(TAG, "Set vertical padding: " + mMediumVerticalPadding);
             int horizontalPadding = (int) Math.floor(MAX_MEDIUM_PADDING * mDensity);
             int verticalPadding = (int) Math.floor(mMediumVerticalPadding * mDensity);
-            views.setViewPadding(R.id.item, horizontalPadding, verticalPadding, horizontalPadding,
+            views.setViewPadding(R.id.content, horizontalPadding, verticalPadding,
+                    horizontalPadding,
                     verticalPadding);
         }
         views.setViewVisibility(R.id.messages_count, View.GONE);
+        if (mTile.getUserName() != null) {
+            views.setTextViewText(R.id.name, mTile.getUserName());
+        }
+
         return views;
     }
 
@@ -761,6 +756,9 @@
             views.setViewVisibility(R.id.predefined_icon, View.GONE);
             views.setViewVisibility(R.id.messages_count, View.GONE);
         }
+        if (mTile.getUserName() != null) {
+            views.setTextViewText(R.id.name, mTile.getUserName());
+        }
         String status = getLastInteractionString(mContext,
                 mTile.getLastInteractionTimestamp());
         if (status != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index dd1a4af..c459963 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -196,13 +196,10 @@
 
     void updateResources(QSPanelController qsPanelController,
             QuickStatusBarHeaderController quickStatusBarHeaderController) {
-        LayoutParams layoutParams = (LayoutParams) mQSPanelContainer.getLayoutParams();
-        layoutParams.topMargin = mContext.getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.quick_qs_offset_height);
-        mQSPanelContainer.setLayoutParams(layoutParams);
         mQSPanelContainer.setPaddingRelative(
                 mQSPanelContainer.getPaddingStart(),
-                mQSPanelContainer.getPaddingTop(),
+                mContext.getResources().getDimensionPixelSize(
+                        com.android.internal.R.dimen.quick_qs_offset_height),
                 mQSPanelContainer.getPaddingEnd(),
                 mContext.getResources().getDimensionPixelSize(R.dimen.qs_container_bottom_padding)
         );
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
index 1fa9260..f6d9389 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -29,6 +29,7 @@
 import android.widget.Toast;
 
 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;
@@ -73,6 +74,7 @@
     private final View mPowerMenuLite;
     private final boolean mShowPMLiteButton;
     private GlobalActionsDialogLite mGlobalActionsDialog;
+    private final UiEventLogger mUiEventLogger;
 
     private final UserInfoController.OnUserInfoChangedListener mOnUserInfoChangedListener =
             new UserInfoController.OnUserInfoChangedListener() {
@@ -122,6 +124,7 @@
                     startSettingsActivity();
                 }
             } else if (v == mPowerMenuLite) {
+                mUiEventLogger.log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS);
                 mGlobalActionsDialog.showOrHideDialog(false, true);
             }
         }
@@ -139,7 +142,7 @@
             QuickQSPanelController quickQSPanelController,
             TunerService tunerService, MetricsLogger metricsLogger, FalsingManager falsingManager,
             @Named(PM_LITE_ENABLED) boolean showPMLiteButton,
-            GlobalActionsDialogLite globalActionsDialog) {
+            GlobalActionsDialogLite globalActionsDialog, UiEventLogger uiEventLogger) {
         super(view);
         mUserManager = userManager;
         mUserInfoController = userInfoController;
@@ -161,6 +164,7 @@
         mPowerMenuLite = mView.findViewById(R.id.pm_lite);
         mShowPMLiteButton = showPMLiteButton;
         mGlobalActionsDialog = globalActionsDialog;
+        mUiEventLogger = uiEventLogger;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 73a6b34..26332f4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -23,7 +23,6 @@
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.util.AttributeSet;
-import android.util.MathUtils;
 import android.util.Pair;
 import android.view.DisplayCutout;
 import android.view.View;
@@ -36,9 +35,7 @@
 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.qs.QSDetail.Callback;
-import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
 import com.android.systemui.statusbar.phone.StatusBarWindowView;
 import com.android.systemui.statusbar.phone.StatusIconContainer;
@@ -67,7 +64,11 @@
 
     private View mQSCarriers;
     private Clock mClockView;
-    private Space mSpace;
+    private Space mDatePrivacySeparator;
+    private View mClockIconsSeparator;
+    private boolean mShowClockIconsSeparator;
+    private ViewGroup mRightLayout;
+
     private BatteryMeterView mBatteryRemainingIcon;
     private StatusIconContainer mIconContainer;
     private View mPrivacyChip;
@@ -80,7 +81,7 @@
     private int mWaterfallTopInset;
     private int mCutOutPaddingLeft;
     private int mCutOutPaddingRight;
-    private float mClockIconsAlpha = 1.0f;
+    private float mViewAlpha = 1.0f;
     private float mKeyguardExpansionFraction;
     private int mTextColorPrimary = Color.TRANSPARENT;
     private int mTopViewMeasureHeight;
@@ -117,9 +118,11 @@
         mPrivacyChip = findViewById(R.id.privacy_chip);
         mDateView = findViewById(R.id.date);
         mSecurityHeaderView = findViewById(R.id.header_text_container);
+        mClockIconsSeparator = findViewById(R.id.separator);
+        mRightLayout = findViewById(R.id.rightLayout);
 
         mClockView = findViewById(R.id.clock);
-        mSpace = findViewById(R.id.space);
+        mDatePrivacySeparator = findViewById(R.id.space);
         // Tint for the battery icons are handled in setupHost()
         mBatteryRemainingIcon = findViewById(R.id.batteryRemainingIcon);
 
@@ -230,36 +233,36 @@
     }
 
     private void updateAlphaAnimator() {
-        StatusBarIconView noCallingIcon =
-                ((StatusBarIconView) mIconContainer.getViewForSlot(mMobileSlotName));
-        StatusBarIconView callStrengthIcon =
-                ((StatusBarIconView) mIconContainer.getViewForSlot(mCallStrengthSlotName));
         TouchAnimator.Builder builder = new TouchAnimator.Builder()
                 // The following two views have to be hidden manually, so as not to hide the
                 // Privacy chip in QQS
                 .addFloat(mDateView, "alpha", 0, 1)
                 .addFloat(mSecurityHeaderView, "alpha", 0, 1)
-                .addFloat(mQSCarriers, "alpha", 0, 1);
-        builder.setListener(new TouchAnimator.ListenerAdapter() {
-            @Override
-            public void onAnimationAtEnd() {
-                mIconContainer.addIgnoredSlot(mMobileSlotName);
-                mIconContainer.addIgnoredSlot(mCallStrengthSlotName);
-            }
+                .addFloat(mQSCarriers, "alpha", 0, 1)
+                .setListener(new TouchAnimator.ListenerAdapter() {
+                    @Override
+                    public void onAnimationAtEnd() {
+                        mIconContainer.addIgnoredSlot(mMobileSlotName);
+                        mIconContainer.addIgnoredSlot(mCallStrengthSlotName);
+                    }
 
-            @Override
-            public void onAnimationStarted() {
-                mIconContainer.addIgnoredSlot(mMobileSlotName);
-                mIconContainer.addIgnoredSlot(mCallStrengthSlotName);
-            }
+                    @Override
+                    public void onAnimationStarted() {
+                        mIconContainer.addIgnoredSlot(mMobileSlotName);
+                        mIconContainer.addIgnoredSlot(mCallStrengthSlotName);
 
-            @Override
-            public void onAnimationAtStart() {
-                super.onAnimationAtStart();
-                mIconContainer.removeIgnoredSlot(mMobileSlotName);
-                mIconContainer.removeIgnoredSlot(mCallStrengthSlotName);
-            }
-        });
+                        setSeparatorVisibility(false);
+                    }
+
+                    @Override
+                    public void onAnimationAtStart() {
+                        super.onAnimationAtStart();
+                        mIconContainer.removeIgnoredSlot(mMobileSlotName);
+                        mIconContainer.removeIgnoredSlot(mCallStrengthSlotName);
+
+                        setSeparatorVisibility(mShowClockIconsSeparator);
+                    }
+                });
         mAlphaAnimator = builder.build();
     }
 
@@ -337,20 +340,30 @@
                         cutout, cornerCutoutPadding, -1);
         mDatePrivacyView.setPadding(padding.first, 0, padding.second, 0);
         mClockIconsView.setPadding(padding.first, 0, padding.second, 0);
-        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mSpace.getLayoutParams();
+        LinearLayout.LayoutParams datePrivacySeparatorLayoutParams =
+                (LinearLayout.LayoutParams) mDatePrivacySeparator.getLayoutParams();
+        LinearLayout.LayoutParams mClockIconsSeparatorLayoutParams =
+                (LinearLayout.LayoutParams) mClockIconsSeparator.getLayoutParams();
         boolean cornerCutout = cornerCutoutPadding != null
                 && (cornerCutoutPadding.first == 0 || cornerCutoutPadding.second == 0);
         if (cutout != null) {
             Rect topCutout = cutout.getBoundingRectTop();
             if (topCutout.isEmpty() || cornerCutout) {
-                lp.width = 0;
-                mSpace.setVisibility(View.GONE);
+                datePrivacySeparatorLayoutParams.width = 0;
+                mDatePrivacySeparator.setVisibility(View.GONE);
+                mClockIconsSeparatorLayoutParams.width = 0;
+                setSeparatorVisibility(false);
+                mShowClockIconsSeparator = false;
             } else {
-                lp.width = topCutout.width();
-                mSpace.setVisibility(View.VISIBLE);
+                datePrivacySeparatorLayoutParams.width = topCutout.width();
+                mDatePrivacySeparator.setVisibility(View.VISIBLE);
+                mClockIconsSeparatorLayoutParams.width = topCutout.width();
+                mShowClockIconsSeparator = true;
+                setSeparatorVisibility(mKeyguardExpansionFraction == 0f);
             }
         }
-        mSpace.setLayoutParams(lp);
+        mDatePrivacySeparator.setLayoutParams(datePrivacySeparatorLayoutParams);
+        mClockIconsSeparator.setLayoutParams(mClockIconsSeparatorLayoutParams);
         mCutOutPaddingLeft = padding.first;
         mCutOutPaddingRight = padding.second;
         mWaterfallTopInset = cutout == null ? 0 : cutout.getWaterfallInsets().top;
@@ -358,6 +371,32 @@
         return super.onApplyWindowInsets(insets);
     }
 
+    /**
+     * Sets the visibility of the separator between clock and icons.
+     *
+     * This separator is "visible" when there is a center cutout, to block that space. In that
+     * case, the clock and the layout on the right (containing the icons and the battery meter) are
+     * set to weight 1 to take the available space.
+     * @param visible whether the separator between clock and icons should be visible.
+     */
+    private void setSeparatorVisibility(boolean visible) {
+        int newVisibility = visible ? View.VISIBLE : View.GONE;
+        if (mClockIconsSeparator.getVisibility() == newVisibility) return;
+
+        mClockIconsSeparator.setVisibility(visible ? View.VISIBLE : View.GONE);
+        mQSCarriers.setVisibility(visible ? View.GONE : View.VISIBLE);
+
+        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mClockView.getLayoutParams();
+        lp.width = visible ? 0 : WRAP_CONTENT;
+        lp.weight = visible ? 1f : 0f;
+        mClockView.setLayoutParams(lp);
+
+        lp = (LinearLayout.LayoutParams) mRightLayout.getLayoutParams();
+        lp.width = visible ? 0 : WRAP_CONTENT;
+        lp.weight = visible ? 1f : 0f;
+        mRightLayout.setLayoutParams(lp);
+    }
+
     private void updateHeadersPadding() {
         setContentMargins(mDatePrivacyView, 0, 0);
         setContentMargins(mClockIconsView, 0, 0);
@@ -408,35 +447,12 @@
     }
 
     /**
-     * When QS is scrolling, mClockIconsAlpha should scroll away and fade out.
-     *
-     * For a given scroll level, this method does the following:
-     * <ol>
-     *     <li>Determine the alpha that {@code mClockIconsView} should have when the panel is fully
-     *         expanded.</li>
-     *     <li>Set the scroll of {@code mClockIconsView} to the same of {@code QSPanel}.</li>
-     *     <li>Set the alpha of {@code mClockIconsView} to that determined by the expansion of
-     *         the panel, interpolated between 1 (no expansion) and {@code mClockIconsAlpha} (fully
-     *         expanded), matching the animator.</li>
-     * </ol>
+     * Scroll the headers away.
      *
      * @param scrollY the scroll of the QSPanel container
      */
     public void setExpandedScrollAmount(int scrollY) {
-        // The scrolling of the expanded qs has changed. Since the header text isn't part of it,
-        // but would overlap content, we're fading it out.
-        float newAlpha = 1.0f;
-        if (mClockIconsView.getHeight() > 0) {
-            newAlpha = MathUtils.map(0, mClockIconsView.getHeight() / 2.0f, 1.0f, 0.0f,
-                    scrollY);
-            newAlpha = Interpolators.ALPHA_OUT.getInterpolation(newAlpha);
-        }
         mClockIconsView.setScrollY(scrollY);
-        if (newAlpha != mClockIconsAlpha) {
-            mClockIconsAlpha = newAlpha;
-            mClockIconsView.setAlpha(MathUtils.lerp(1.0f, mClockIconsAlpha,
-                    mKeyguardExpansionFraction));
-            updateAlphaAnimator();
-        }
+        mDatePrivacyView.setScrollY(scrollY);
     }
 }
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 2d777a5..b3ec39f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.qs.tileimpl
 
+import android.animation.ArgbEvaluator
+import android.animation.PropertyValuesHolder
 import android.animation.ValueAnimator
 import android.content.Context
 import android.content.res.ColorStateList
@@ -43,6 +45,7 @@
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.plugins.qs.QSTile.BooleanState
 import com.android.systemui.plugins.qs.QSTileView
+import com.android.systemui.qs.tileimpl.QSIconViewImpl.QS_ANIM_LENGTH
 import java.util.Objects
 
 private const val TAG = "QSTileViewImpl"
@@ -54,6 +57,10 @@
 
     companion object {
         private const val INVALID = -1
+        private const val BACKGROUND_NAME = "background"
+        private const val LABEL_NAME = "label"
+        private const val SECONDARY_LABEL_NAME = "secondaryLabel"
+        private const val CHEVRON_NAME = "chevron"
     }
 
     override var heightOverride: Int = HeightOverrideable.NO_OVERRIDE
@@ -83,9 +90,19 @@
     private lateinit var ripple: RippleDrawable
     private lateinit var colorBackgroundDrawable: Drawable
     private var paintColor: Int = 0
-    private var paintAnimator: ValueAnimator? = null
-    private var labelAnimator: ValueAnimator? = null
-    private var secondaryLabelAnimator: ValueAnimator? = null
+    private val singleAnimator: ValueAnimator = ValueAnimator().apply {
+        setDuration(QS_ANIM_LENGTH)
+        addUpdateListener { animation ->
+            setAllColors(
+                // These casts will throw an exception if some property is missing. We should
+                // always have all properties.
+                animation.getAnimatedValue(BACKGROUND_NAME) as Int,
+                animation.getAnimatedValue(LABEL_NAME) as Int,
+                animation.getAnimatedValue(SECONDARY_LABEL_NAME) as Int,
+                animation.getAnimatedValue(CHEVRON_NAME) as Int
+            )
+        }
+    }
 
     private var accessibilityClass: String? = null
     private var stateDescriptionDeltas: CharSequence? = null
@@ -104,8 +121,7 @@
         clipToPadding = false
         isFocusable = true
         background = createTileBackground()
-        paintColor = getCircleColor(QSTile.State.DEFAULT_STATE)
-        colorBackgroundDrawable.setTint(paintColor)
+        setColor(getBackgroundColorForState(QSTile.State.DEFAULT_STATE))
 
         val padding = resources.getDimensionPixelSize(R.dimen.qs_tile_padding)
         val startPadding = resources.getDimensionPixelSize(R.dimen.qs_tile_start_padding)
@@ -166,8 +182,8 @@
             labelContainer.ignoreLastView = true
             secondaryLabel.alpha = 0f
         }
-        label.setTextColor(getLabelColor(QSTile.State.DEFAULT_STATE))
-        secondaryLabel.setTextColor(getSecondaryLabelColor(QSTile.State.DEFAULT_STATE))
+        setLabelColor(getLabelColorForState(QSTile.State.DEFAULT_STATE))
+        setSecondaryLabelColor(getSecondaryLabelColorForState(QSTile.State.DEFAULT_STATE))
         addView(labelContainer)
     }
 
@@ -176,6 +192,7 @@
                 .inflate(R.layout.qs_tile_side_icon, this, false) as ViewGroup
         customDrawableView = sideView.requireViewById(R.id.customDrawable)
         chevronView = sideView.requireViewById(R.id.chevron)
+        setChevronColor(getChevronColorForState(QSTile.State.DEFAULT_STATE))
         addView(sideView)
     }
 
@@ -322,19 +339,6 @@
         icon.setIcon(state, allowAnimations)
         contentDescription = state.contentDescription
 
-        // Background color animation
-        val newColor = getCircleColor(state.state)
-        if (allowAnimations) {
-            animateBackground(newColor)
-        } else {
-            clearBackgroundAnimator()
-            colorBackgroundDrawable.setTintList(ColorStateList.valueOf(newColor)).also {
-                paintColor = newColor
-            }
-            paintColor = newColor
-        }
-        //
-
         // State handling and description
         val stateDescription = StringBuilder()
         val stateText = getStateText(state)
@@ -383,23 +387,80 @@
             }
         }
 
-        if (allowAnimations) {
-            animateLabelColor(getLabelColor(state.state))
-            animateSecondaryLabelColor(getSecondaryLabelColor(state.state))
-        } else {
-            label.setTextColor(getLabelColor(state.state))
-            secondaryLabel.setTextColor(getSecondaryLabelColor(state.state))
+        // Colors
+        if (state.state != lastState) {
+            singleAnimator.cancel()
+            if (allowAnimations) {
+                singleAnimator.setValues(
+                        colorValuesHolder(
+                                BACKGROUND_NAME,
+                                paintColor,
+                                getBackgroundColorForState(state.state)
+                        ),
+                        colorValuesHolder(
+                                LABEL_NAME,
+                                label.currentTextColor,
+                                getLabelColorForState(state.state)
+                        ),
+                        colorValuesHolder(
+                                SECONDARY_LABEL_NAME,
+                                label.currentTextColor,
+                                getSecondaryLabelColorForState(state.state)
+                        ),
+                        colorValuesHolder(
+                                CHEVRON_NAME,
+                                chevronView.imageTintList?.defaultColor ?: 0,
+                                getChevronColorForState(state.state)
+                        )
+                    )
+                singleAnimator.start()
+            } else {
+                setAllColors(
+                    getBackgroundColorForState(state.state),
+                    getLabelColorForState(state.state),
+                    getLabelColorForState(state.state),
+                    getChevronColorForState(state.state)
+                )
+            }
         }
 
         // Right side icon
         loadSideViewDrawableIfNecessary(state)
-        chevronView.imageTintList = ColorStateList.valueOf(getSecondaryLabelColor(state.state))
 
         label.isEnabled = !state.disabledByPolicy
 
         lastState = state.state
     }
 
+    private fun setAllColors(
+        backgroundColor: Int,
+        labelColor: Int,
+        secondaryLabelColor: Int,
+        chevronColor: Int
+    ) {
+        setColor(backgroundColor)
+        setLabelColor(labelColor)
+        setSecondaryLabelColor(secondaryLabelColor)
+        setChevronColor(chevronColor)
+    }
+
+    private fun setColor(color: Int) {
+        colorBackgroundDrawable.setTint(color)
+        paintColor = color
+    }
+
+    private fun setLabelColor(color: Int) {
+        label.setTextColor(color)
+    }
+
+    private fun setSecondaryLabelColor(color: Int) {
+        secondaryLabel.setTextColor(color)
+    }
+
+    private fun setChevronColor(color: Int) {
+        chevronView.imageTintList = ColorStateList.valueOf(color)
+    }
+
     private fun loadSideViewDrawableIfNecessary(state: QSTile.State) {
         if (state.sideViewCustomDrawable != null) {
             customDrawableView.setImageDrawable(state.sideViewCustomDrawable)
@@ -446,63 +507,7 @@
         return locInScreen.get(1) >= -height
     }
 
-    private fun animateBackground(newBackgroundColor: Int) {
-        if (newBackgroundColor != paintColor) {
-            clearBackgroundAnimator()
-            paintAnimator = ValueAnimator.ofArgb(paintColor, newBackgroundColor)
-                    .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
-                        addUpdateListener { animation: ValueAnimator ->
-                            val c = animation.animatedValue as Int
-                            colorBackgroundDrawable.setTintList(ColorStateList.valueOf(c)).also {
-                                paintColor = c
-                            }
-                        }
-                        start()
-                    }
-        }
-    }
-
-    private fun animateLabelColor(color: Int) {
-        val currentColor = label.textColors.defaultColor
-        if (currentColor != color) {
-            clearLabelAnimator()
-            labelAnimator = ValueAnimator.ofArgb(currentColor, color)
-                    .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
-                        addUpdateListener {
-                            label.setTextColor(it.animatedValue as Int)
-                        }
-                        start()
-                    }
-        }
-    }
-
-    private fun animateSecondaryLabelColor(color: Int) {
-        val currentColor = secondaryLabel.textColors.defaultColor
-        if (currentColor != color) {
-            clearSecondaryLabelAnimator()
-            secondaryLabelAnimator = ValueAnimator.ofArgb(currentColor, color)
-                    .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
-                        addUpdateListener {
-                            secondaryLabel.setTextColor(it.animatedValue as Int)
-                        }
-                        start()
-                    }
-        }
-    }
-
-    private fun clearBackgroundAnimator() {
-        paintAnimator?.cancel()?.also { paintAnimator = null }
-    }
-
-    private fun clearLabelAnimator() {
-        labelAnimator?.cancel()?.also { labelAnimator = null }
-    }
-
-    private fun clearSecondaryLabelAnimator() {
-        secondaryLabelAnimator?.cancel()?.also { secondaryLabelAnimator = null }
-    }
-
-    private fun getCircleColor(state: Int): Int {
+    private fun getBackgroundColorForState(state: Int): Int {
         return when (state) {
             Tile.STATE_ACTIVE -> colorActive
             Tile.STATE_INACTIVE -> colorInactive
@@ -514,7 +519,7 @@
         }
     }
 
-    private fun getLabelColor(state: Int): Int {
+    private fun getLabelColorForState(state: Int): Int {
         return when (state) {
             Tile.STATE_ACTIVE -> colorLabelActive
             Tile.STATE_INACTIVE -> colorLabelInactive
@@ -526,7 +531,7 @@
         }
     }
 
-    private fun getSecondaryLabelColor(state: Int): Int {
+    private fun getSecondaryLabelColorForState(state: Int): Int {
         return when (state) {
             Tile.STATE_ACTIVE -> colorLabelActive
             Tile.STATE_INACTIVE, Tile.STATE_UNAVAILABLE -> colorLabelUnavailable
@@ -536,4 +541,12 @@
             }
         }
     }
+
+    private fun getChevronColorForState(state: Int): Int = getSecondaryLabelColorForState(state)
+}
+
+private fun colorValuesHolder(name: String, vararg values: Int): PropertyValuesHolder {
+    return PropertyValuesHolder.ofInt(name, *values).apply {
+        setEvaluator(ArgbEvaluator.getInstance())
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
index fa28754..30c9b44 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -22,7 +22,6 @@
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.graphics.HardwareRenderer;
-import android.graphics.Matrix;
 import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
 import android.graphics.RenderNode;
@@ -35,7 +34,6 @@
 import android.util.Log;
 import android.view.ScrollCaptureResponse;
 import android.view.View;
-import android.view.Window;
 import android.widget.ImageView;
 
 import androidx.constraintlayout.widget.ConstraintLayout;
@@ -68,8 +66,6 @@
     public static final String EXTRA_CAPTURE_RESPONSE = "capture-response";
     private static final String KEY_SAVED_IMAGE_PATH = "saved-image-path";
 
-    private static final boolean USE_SHARED_ELEMENT = false;
-
     private final UiEventLogger mUiEventLogger;
     private final Executor mUiExecutor;
     private final Executor mBackgroundExecutor;
@@ -89,6 +85,7 @@
     private ListenableFuture<File> mCacheSaveFuture;
     private ListenableFuture<ImageLoader.Result> mCacheLoadFuture;
 
+    private Bitmap mOutputBitmap;
     private LongScreenshot mLongScreenshot;
     private boolean mTransitionStarted;
 
@@ -114,7 +111,7 @@
     public void onCreate(Bundle savedInstanceState) {
         Log.d(TAG, "onCreate(savedInstanceState = " + savedInstanceState + ")");
         super.onCreate(savedInstanceState);
-        getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
+
         setContentView(R.layout.long_screenshot);
 
         mPreview = requireViewById(R.id.preview);
@@ -173,7 +170,6 @@
                 }
             }, mUiExecutor);
             mCacheLoadFuture = null;
-            return;
         } else {
             LongScreenshot longScreenshot = mLongScreenshotHolder.takeLongScreenshot();
             if (longScreenshot != null) {
@@ -189,7 +185,6 @@
         Log.d(TAG, "onLongScreenshotReceived(longScreenshot=" + longScreenshot + ")");
         mLongScreenshot = longScreenshot;
         mPreview.setImageDrawable(mLongScreenshot.getDrawable());
-        mTransitionView.setImageDrawable(mLongScreenshot.getDrawable());
         updateImageDimensions();
         mCropView.setVisibility(View.VISIBLE);
         mMagnifierView.setDrawable(mLongScreenshot.getDrawable(),
@@ -310,19 +305,15 @@
         intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
                 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
 
-        if (USE_SHARED_ELEMENT) {
-            updateImageDimensions();
-            mTransitionView.setVisibility(View.VISIBLE);
-            // TODO: listen for transition completing instead of finishing onStop
-            mTransitionStarted = true;
-            startActivity(intent,
-                    ActivityOptions.makeSceneTransitionAnimation(this, mTransitionView,
-                            ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME).toBundle());
-        } else {
-            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
-            startActivityAsUser(intent, UserHandle.CURRENT);
-            finishAndRemoveTask();
-        }
+        mTransitionView.setImageBitmap(mOutputBitmap);
+        mTransitionView.setVisibility(View.VISIBLE);
+        mTransitionView.setTransitionName(
+                ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME);
+        // TODO: listen for transition completing instead of finishing onStop
+        mTransitionStarted = true;
+        startActivity(intent,
+                ActivityOptions.makeSceneTransitionAnimation(this, mTransitionView,
+                        ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME).toBundle());
     }
 
     private void doShare(Uri uri) {
@@ -368,9 +359,11 @@
             return;
         }
 
-        Bitmap output = renderBitmap(mPreview.getDrawable(), bounds);
+        updateImageDimensions();
+
+        mOutputBitmap = renderBitmap(drawable, bounds);
         ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export(
-                mBackgroundExecutor, UUID.randomUUID(), output, ZonedDateTime.now());
+                mBackgroundExecutor, UUID.randomUUID(), mOutputBitmap, ZonedDateTime.now());
         exportFuture.addListener(() -> onExportCompleted(action, exportFuture), mUiExecutor);
     }
 
@@ -419,7 +412,6 @@
         // The image width and height on screen
         int imageHeight = previewHeight;
         int imageWidth = previewWidth;
-        float scale;
         if (imageRatio > viewRatio) {
             // Image is full width and height is constrained, compute extra padding to inform
             // CropView
@@ -428,15 +420,13 @@
             mCropView.setExtraPadding(extraPadding + mPreview.getPaddingTop(),
                     extraPadding + mPreview.getPaddingBottom());
             imageTop += (previewHeight - imageHeight) / 2;
-            scale = imageHeight / bounds.height();
             mCropView.setExtraPadding(extraPadding, extraPadding);
             mCropView.setImageWidth(previewWidth);
         } else {
             imageWidth = (int) (previewWidth * imageRatio / viewRatio);
             imageLeft += (previewWidth - imageWidth) / 2;
-            scale = imageWidth / (float) bounds.width();
             // Image is full height
-            mCropView.setExtraPadding(mPreview.getPaddingTop(),  mPreview.getPaddingBottom());
+            mCropView.setExtraPadding(mPreview.getPaddingTop(), mPreview.getPaddingBottom());
             mCropView.setImageWidth((int) (previewHeight * imageRatio));
         }
 
@@ -449,10 +439,5 @@
         params.width = boundaries.width();
         params.height = boundaries.height();
         mTransitionView.setLayoutParams(params);
-
-        Matrix matrix = new Matrix();
-        matrix.postScale(scale, scale, 0, 0);
-        matrix.postTranslate(-boundaries.left, -boundaries.top);
-        mTransitionView.setImageMatrix(matrix);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index 7f31fdd..5437ce6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -18,7 +18,6 @@
 
 import static com.android.systemui.statusbar.RemoteInputController.processForRemoteInput;
 import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
-import static com.android.systemui.statusbar.phone.StatusBar.DEBUG;
 
 import android.annotation.NonNull;
 import android.annotation.SuppressLint;
@@ -35,6 +34,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.statusbar.dagger.StatusBarModule;
 import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins;
+import com.android.systemui.statusbar.phone.StatusBar;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -46,6 +46,7 @@
 @SuppressLint("OverrideAbstract")
 public class NotificationListener extends NotificationListenerWithPlugins {
     private static final String TAG = "NotificationListener";
+    private static final boolean DEBUG = StatusBar.DEBUG;
 
     private final Context mContext;
     private final NotificationManager mNotificationManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 70b3a7b..ca81a7b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -169,7 +169,7 @@
                     Pair<Intent, ActivityOptions> options = response.getLaunchOptions(view);
                     mLogger.logStartingIntentWithDefaultHandler(entry, pendingIntent);
                     boolean started = RemoteViews.startPendingIntent(view, pendingIntent, options);
-                    if (started) releaseNotificationIfKeptForRemoteInputHistory(entry.getKey());
+                    if (started) releaseNotificationIfKeptForRemoteInputHistory(entry);
                     return started;
             });
         }
@@ -608,7 +608,11 @@
      * (after unlock, if applicable), and will then wait a short time to allow the app to update the
      * notification in response to the action.
      */
-    private void releaseNotificationIfKeptForRemoteInputHistory(String key) {
+    private void releaseNotificationIfKeptForRemoteInputHistory(NotificationEntry entry) {
+        if (entry == null) {
+            return;
+        }
+        final String key = entry.getKey();
         if (isNotificationKeptForRemoteInputHistory(key)) {
             mMainHandler.postDelayed(() -> {
                 if (isNotificationKeptForRemoteInputHistory(key)) {
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 2481ed4..5f10e55 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
@@ -29,13 +29,11 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.StatusBarState.SHADE
+import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED
 import com.android.systemui.statusbar.phone.StatusBarLocationPublisher
 import com.android.systemui.statusbar.phone.StatusBarMarginUpdatedListener
 import com.android.systemui.util.concurrency.DelayableExecutor
-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 java.lang.IllegalStateException
 import java.util.concurrent.Executor
@@ -50,7 +48,7 @@
  * will have its gravity set towards the corner (i.e., top-right corner gets top|right gravity), and
  * the contained ImageView will be set to center_vertical and away from the corner horizontally. The
  * Views will match the status bar top padding and status bar height so that the dot can appear to
- * reside directly after the status bar system contents (basically to the right of the battery).
+ * reside directly after the status bar system contents (basically after the battery).
  *
  * NOTE: any operation that modifies views directly must run on the provided executor, because
  * these views are owned by ScreenDecorations and it runs in its own thread
@@ -85,21 +83,27 @@
 
     // Privacy dots are created in ScreenDecoration's UiThread, which is not the main thread
     private var uiExecutor: DelayableExecutor? = null
-    private var e: DelayableExecutor? = null
+
+    private val marginListener: StatusBarMarginUpdatedListener =
+            object : StatusBarMarginUpdatedListener {
+        override fun onStatusBarMarginUpdated(marginLeft: Int, marginRight: Int) {
+            setStatusBarMargins(marginLeft, marginRight)
+        }
+    }
 
     private val views: Sequence<View>
         get() = if (!this::tl.isInitialized) sequenceOf() else sequenceOf(tl, tr, br, bl)
 
     init {
-        locationPublisher.addCallback(object : StatusBarMarginUpdatedListener {
-            override fun onStatusBarMarginUpdated(marginLeft: Int, marginRight: Int) {
-                setStatusBarMargins(marginLeft, marginRight)
-            }
-        })
+        locationPublisher.addCallback(marginListener)
 
         stateController.addCallback(object : StatusBarStateController.StateListener {
             override fun onExpandedChanged(isExpanded: Boolean) {
-                setStatusBarExpanded(isExpanded)
+                updateStatusBarState()
+            }
+
+            override fun onStateChanged(newState: Int) {
+                updateStatusBarState()
             }
         })
     }
@@ -108,6 +112,13 @@
         uiExecutor = e
     }
 
+    fun setQsExpanded(expanded: Boolean) {
+        dlog("setQsExpanded $expanded")
+        synchronized(lock) {
+            nextViewState = nextViewState.copy(qsExpanded = expanded)
+        }
+    }
+
     @UiThread
     fun setNewRotation(rot: Int) {
         dlog("updateRotation: $rot")
@@ -125,8 +136,8 @@
         val index = newCorner.cornerIndex()
 
         val h = when (rot) {
-            ROTATION_NONE, ROTATION_UPSIDE_DOWN -> sbHeightPortrait
-            ROTATION_LANDSCAPE, ROTATION_SEASCAPE -> sbHeightLandscape
+            0, 2 -> sbHeightPortrait
+            1, 3 -> sbHeightLandscape
             else -> 0
         }
         synchronized(lock) {
@@ -326,15 +337,22 @@
         }
     }
 
-    /**
-     *  We won't show the dot when quick settings is showing
-     */
-    private fun setStatusBarExpanded(expanded: Boolean) {
+    private fun updateStatusBarState() {
         synchronized(lock) {
-            nextViewState = nextViewState.copy(hideDotForQuickSettings = expanded)
+            nextViewState = nextViewState.copy(shadeExpanded = isShadeInQs())
         }
     }
 
+    /**
+     * If we are unlocked with an expanded shade, QS is showing. On keyguard, the shade is always
+     * expanded so we use other signals from the panel view controller to know if QS is expanded
+     */
+    @GuardedBy("lock")
+    private fun isShadeInQs(): Boolean {
+        return (stateController.isExpanded && stateController.state == SHADE) ||
+                (stateController.state == SHADE_LOCKED)
+    }
+
     private fun scheduleUpdate() {
         dlog("scheduleUpdate: ")
 
@@ -431,13 +449,20 @@
     }
 }
 
+private fun vlog(s: String) {
+    if (DEBUG_VERBOSE) {
+        Log.d(TAG, s)
+    }
+}
+
 const val TOP_LEFT = 0
 const val TOP_RIGHT = 1
 const val BOTTOM_RIGHT = 2
 const val BOTTOM_LEFT = 3
 private const val DURATION = 160L
 private const val TAG = "PrivacyDotViewController"
-private const val DEBUG = false
+private const val DEBUG = true
+private const val DEBUG_VERBOSE = false
 
 private fun Int.toGravity(): Int {
     return when (this) {
@@ -460,10 +485,10 @@
 }
 
 private data class ViewState(
-    // don't @ me with names
     val systemPrivacyEventIsActive: Boolean = false,
-    val hideDotForQuickSettings: Boolean = false,
-    val statusBarExpanded: Boolean = false,
+    val shadeExpanded: Boolean = false,
+    val qsExpanded: Boolean = false,
+
     val rotation: Int = 0,
     val height: Int = 0,
     val marginLeft: Int = 0,
@@ -472,7 +497,7 @@
     val designatedCorner: View? = null
 ) {
     fun shouldShowDot(): Boolean {
-        return systemPrivacyEventIsActive && !hideDotForQuickSettings
+        return systemPrivacyEventIsActive && !shadeExpanded && !qsExpanded
     }
 
     fun needsLayout(other: ViewState): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
index d6356de..f40f24a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java
@@ -16,7 +16,9 @@
 
 package com.android.systemui.statusbar.notification.collection.legacy;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.Notification;
 import android.service.notification.StatusBarNotification;
 import android.util.ArraySet;
 import android.util.Log;
@@ -31,6 +33,7 @@
 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
+import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 import com.android.wm.shell.bubbles.Bubbles;
@@ -39,10 +42,12 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.TreeSet;
 
 import javax.inject.Inject;
 
@@ -58,13 +63,21 @@
 public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener, StateListener,
         GroupMembershipManager, GroupExpansionManager, Dumpable {
 
-    private static final String TAG = "NotificationGroupManager";
+    private static final String TAG = "NotifGroupManager";
+    private static final boolean DEBUG = StatusBar.DEBUG;
+    private static final boolean SPEW = StatusBar.SPEW;
+    /**
+     * The maximum amount of time (in ms) between the posting of notifications that can be
+     * considered part of the same update batch.
+     */
+    private static final long POST_BATCH_MAX_AGE = 5000;
     private final HashMap<String, NotificationGroup> mGroupMap = new HashMap<>();
     private final ArraySet<OnGroupExpansionChangeListener> mExpansionChangeListeners =
             new ArraySet<>();
     private final ArraySet<OnGroupChangeListener> mGroupChangeListeners = new ArraySet<>();
     private final Lazy<PeopleNotificationIdentifier> mPeopleNotificationIdentifier;
     private final Optional<Bubbles> mBubblesOptional;
+    private final EventBuffer mEventBuffer = new EventBuffer();
     private int mBarState = -1;
     private HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>();
     private HeadsUpManager mHeadsUpManager;
@@ -134,8 +147,14 @@
      * When we want to remove an entry from being tracked for grouping
      */
     public void onEntryRemoved(NotificationEntry removed) {
+        if (SPEW) {
+            Log.d(TAG, "onEntryRemoved: entry=" + removed);
+        }
         onEntryRemovedInternal(removed, removed.getSbn());
-        mIsolatedEntries.remove(removed.getKey());
+        StatusBarNotification oldSbn = mIsolatedEntries.remove(removed.getKey());
+        if (oldSbn != null) {
+            updateSuppression(mGroupMap.get(oldSbn.getGroupKey()));
+        }
     }
 
     /**
@@ -162,6 +181,9 @@
             // the close future. See b/23676310 for reference.
             return;
         }
+        if (SPEW) {
+            Log.d(TAG, "onEntryRemovedInternal: entry=" + removed + " group=" + group.groupKey);
+        }
         if (isGroupChild(removed.getKey(), isGroup, isGroupSummary)) {
             group.children.remove(removed.getKey());
         } else {
@@ -182,6 +204,9 @@
      * Notify the group manager that a new entry was added
      */
     public void onEntryAdded(final NotificationEntry added) {
+        if (SPEW) {
+            Log.d(TAG, "onEntryAdded: entry=" + added);
+        }
         updateIsolation(added);
         onEntryAddedInternal(added);
     }
@@ -195,13 +220,16 @@
         String groupKey = getGroupKey(sbn);
         NotificationGroup group = mGroupMap.get(groupKey);
         if (group == null) {
-            group = new NotificationGroup();
+            group = new NotificationGroup(groupKey);
             mGroupMap.put(groupKey, group);
 
             for (OnGroupChangeListener listener : mGroupChangeListeners) {
                 listener.onGroupCreated(group, groupKey);
             }
         }
+        if (SPEW) {
+            Log.d(TAG, "onEntryAddedInternal: entry=" + added + " group=" + group.groupKey);
+        }
         if (isGroupChild) {
             NotificationEntry existing = group.children.get(added.getKey());
             if (existing != null && existing != added) {
@@ -213,9 +241,11 @@
                         + " added removed" + added.isRowRemoved(), new Throwable());
             }
             group.children.put(added.getKey(), added);
+            addToPostBatchHistory(group, added);
             updateSuppression(group);
         } else {
             group.summary = added;
+            addToPostBatchHistory(group, added);
             group.expanded = added.areChildrenExpanded();
             updateSuppression(group);
             if (!group.children.isEmpty()) {
@@ -231,6 +261,27 @@
         }
     }
 
+    private void addToPostBatchHistory(NotificationGroup group, @Nullable NotificationEntry entry) {
+        if (entry == null) {
+            return;
+        }
+        boolean didAdd = group.postBatchHistory.add(new PostRecord(entry));
+        if (didAdd) {
+            trimPostBatchHistory(group.postBatchHistory);
+        }
+    }
+
+    /** remove all history that's too old to be in the batch. */
+    private void trimPostBatchHistory(@NonNull TreeSet<PostRecord> postBatchHistory) {
+        if (postBatchHistory.size() <= 1) {
+            return;
+        }
+        long batchStartTime = postBatchHistory.last().postTime - POST_BATCH_MAX_AGE;
+        while (!postBatchHistory.isEmpty() && postBatchHistory.first().postTime < batchStartTime) {
+            postBatchHistory.pollFirst();
+        }
+    }
+
     private void onEntryBecomingChild(NotificationEntry entry) {
         updateIsolation(entry);
     }
@@ -239,6 +290,9 @@
         if (group == null) {
             return;
         }
+        NotificationEntry prevAlertOverride = group.alertOverride;
+        group.alertOverride = getPriorityConversationAlertOverride(group);
+
         int childCount = 0;
         boolean hasBubbles = false;
         for (NotificationEntry entry : group.children.values()) {
@@ -255,18 +309,150 @@
         group.suppressed = group.summary != null && !group.expanded
                 && (childCount == 1
                 || (childCount == 0
-                        && group.summary.getSbn().getNotification().isGroupSummary()
-                        && (hasIsolatedChildren(group) || hasBubbles)));
-        if (prevSuppressed != group.suppressed) {
-            for (OnGroupChangeListener listener : mGroupChangeListeners) {
-                if (!mIsUpdatingUnchangedGroup) {
-                    listener.onGroupSuppressionChanged(group, group.suppressed);
-                    listener.onGroupsChanged();
+                && group.summary.getSbn().getNotification().isGroupSummary()
+                && (hasIsolatedChildren(group) || hasBubbles)));
+
+        boolean alertOverrideChanged = prevAlertOverride != group.alertOverride;
+        boolean suppressionChanged = prevSuppressed != group.suppressed;
+        if (alertOverrideChanged || suppressionChanged) {
+            if (DEBUG && alertOverrideChanged) {
+                Log.d(TAG, "updateSuppression: alertOverride was=" + prevAlertOverride
+                        + " now=" + group.alertOverride + " group:\n" + group);
+            }
+            if (DEBUG && suppressionChanged) {
+                Log.d(TAG,
+                        "updateSuppression: suppressed changed to " + group.suppressed
+                                + " group:\n" + group);
+            }
+            if (!mIsUpdatingUnchangedGroup) {
+                if (alertOverrideChanged) {
+                    mEventBuffer.notifyAlertOverrideChanged(group, prevAlertOverride);
+                }
+                if (suppressionChanged) {
+                    for (OnGroupChangeListener listener : mGroupChangeListeners) {
+                        listener.onGroupSuppressionChanged(group, group.suppressed);
+                    }
+                }
+                mEventBuffer.notifyGroupsChanged();
+            } else {
+                if (DEBUG) {
+                    Log.d(TAG, group + " did not notify listeners of above change(s)");
                 }
             }
         }
     }
 
+    /**
+     * Finds the isolated logical child of this group which is should be alerted instead.
+     *
+     * Notifications from priority conversations are isolated from their groups to make them more
+     * prominent, however apps may post these with a GroupAlertBehavior that has the group receiving
+     * the alert.  This would lead to the group alerting even though the conversation that was
+     * updated was not actually a part of that group.  This method finds the best priority
+     * conversation in this situation, if there is one, so they can be set as the alertOverride of
+     * the group.
+     *
+     * @param group the group to check
+     * @return the entry which should receive the alert instead of the group, if any.
+     */
+    @Nullable
+    private NotificationEntry getPriorityConversationAlertOverride(NotificationGroup group) {
+        // GOAL: if there is a priority child which wouldn't alert based on its groupAlertBehavior,
+        // but which should be alerting (because priority conversations are isolated), find it.
+        if (group == null || group.summary == null) {
+            if (SPEW) {
+                Log.d(TAG, "getPriorityConversationAlertOverride: null group or summary");
+            }
+            return null;
+        }
+        if (isIsolated(group.summary.getKey())) {
+            if (SPEW) {
+                Log.d(TAG, "getPriorityConversationAlertOverride: isolated group");
+            }
+            return null;
+        }
+
+        // Precondiions:
+        // * Only necessary when all notifications in the group use GROUP_ALERT_SUMMARY
+        // * Only necessary when at least one notification in the group is on a priority channel
+        if (group.summary.getSbn().getNotification().getGroupAlertBehavior()
+                != Notification.GROUP_ALERT_SUMMARY) {
+            if (SPEW) {
+                Log.d(TAG, "getPriorityConversationAlertOverride: summary != GROUP_ALERT_SUMMARY");
+            }
+            return null;
+        }
+
+        // Get the important children first, copy the keys for the final importance check,
+        // then add the non-isolated children to the map for unified lookup.
+        HashMap<String, NotificationEntry> children = getImportantConversations(group);
+        if (children == null || children.isEmpty()) {
+            if (SPEW) {
+                Log.d(TAG, "getPriorityConversationAlertOverride: no important conversations");
+            }
+            return null;
+        }
+        HashSet<String> importantChildKeys = new HashSet<>(children.keySet());
+        children.putAll(group.children);
+
+        // Ensure all children have GROUP_ALERT_SUMMARY
+        for (NotificationEntry child : children.values()) {
+            if (child.getSbn().getNotification().getGroupAlertBehavior()
+                    != Notification.GROUP_ALERT_SUMMARY) {
+                if (SPEW) {
+                    Log.d(TAG, "getPriorityConversationAlertOverride: "
+                            + "child != GROUP_ALERT_SUMMARY");
+                }
+                return null;
+            }
+        }
+
+        // Create a merged post history from all the children
+        TreeSet<PostRecord> combinedHistory = new TreeSet<>(group.postBatchHistory);
+        for (String importantChildKey : importantChildKeys) {
+            NotificationGroup importantChildGroup = mGroupMap.get(importantChildKey);
+            combinedHistory.addAll(importantChildGroup.postBatchHistory);
+        }
+        trimPostBatchHistory(combinedHistory);
+
+        // This is a streamlined implementation of the following idea:
+        // * From the subset of notifications in the latest 'batch' of updates.  A batch is:
+        //   * Notifs posted less than POST_BATCH_MAX_AGE before the most recently posted.
+        //   * Only including notifs newer than the second-to-last post of any notification.
+        // * Find the newest child in the batch -- the with the largest 'when' value.
+        // * If the newest child is a priority conversation, set that as the override.
+        HashSet<String> batchKeys = new HashSet<>();
+        long newestChildWhen = -1;
+        NotificationEntry newestChild = null;
+        // Iterate backwards through the post history, tracking the child with the smallest sort key
+        for (PostRecord record : combinedHistory.descendingSet()) {
+            if (batchKeys.contains(record.key)) {
+                // Once you see a notification again, the batch has ended
+                break;
+            }
+            batchKeys.add(record.key);
+            NotificationEntry child = children.get(record.key);
+            if (child != null) {
+                long childWhen = child.getSbn().getNotification().when;
+                if (newestChild == null || childWhen > newestChildWhen) {
+                    newestChildWhen = childWhen;
+                    newestChild = child;
+                }
+            }
+        }
+        if (newestChild != null && importantChildKeys.contains(newestChild.getKey())) {
+            if (SPEW) {
+                Log.d(TAG, "getPriorityConversationAlertOverride: result=" + newestChild);
+            }
+            return newestChild;
+        }
+        if (SPEW) {
+            Log.d(TAG, "getPriorityConversationAlertOverride: result=null, newestChild="
+                    + newestChild);
+        }
+        return null;
+    }
+
     private boolean hasIsolatedChildren(NotificationGroup group) {
         return getNumberOfIsolatedChildren(group.summary.getSbn().getGroupKey()) != 0;
     }
@@ -281,12 +467,33 @@
         return count;
     }
 
+    @Nullable
+    private HashMap<String, NotificationEntry> getImportantConversations(NotificationGroup group) {
+        String groupKey = group.summary.getSbn().getGroupKey();
+        HashMap<String, NotificationEntry> result = null;
+        for (StatusBarNotification sbn : mIsolatedEntries.values()) {
+            if (sbn.getGroupKey().equals(groupKey)) {
+                NotificationEntry entry = mGroupMap.get(sbn.getKey()).summary;
+                if (isImportantConversation(entry)) {
+                    if (result == null) {
+                        result = new HashMap<>();
+                    }
+                    result.put(sbn.getKey(), entry);
+                }
+            }
+        }
+        return result;
+    }
+
     /**
      * Update an entry's group information
      * @param entry notification entry to update
      * @param oldNotification previous notification info before this update
      */
     public void onEntryUpdated(NotificationEntry entry, StatusBarNotification oldNotification) {
+        if (SPEW) {
+            Log.d(TAG, "onEntryUpdated: entry=" + entry);
+        }
         onEntryUpdated(entry, oldNotification.getGroupKey(), oldNotification.isGroup(),
                 oldNotification.getNotification().isGroupSummary());
     }
@@ -325,7 +532,17 @@
      * Whether the given notification is the summary of a group that is being suppressed
      */
     public boolean isSummaryOfSuppressedGroup(StatusBarNotification sbn) {
-        return isGroupSuppressed(getGroupKey(sbn)) && sbn.getNotification().isGroupSummary();
+        return sbn.getNotification().isGroupSummary() && isGroupSuppressed(getGroupKey(sbn));
+    }
+
+    /**
+     * If the given notification is a summary, get the group for it.
+     */
+    public NotificationGroup getGroupForSummary(StatusBarNotification sbn) {
+        if (sbn.getNotification().isGroupSummary()) {
+            return mGroupMap.get(getGroupKey(sbn));
+        }
+        return null;
     }
 
     private boolean isOnlyChild(StatusBarNotification sbn) {
@@ -545,9 +762,7 @@
         if (!sbn.isGroup() || sbn.getNotification().isGroupSummary()) {
             return false;
         }
-        int peopleNotificationType =
-                mPeopleNotificationIdentifier.get().getPeopleNotificationType(entry);
-        if (peopleNotificationType == PeopleNotificationIdentifier.TYPE_IMPORTANT_PERSON) {
+        if (isImportantConversation(entry)) {
             return true;
         }
         if (mHeadsUpManager != null && !mHeadsUpManager.isAlerting(entry.getKey())) {
@@ -560,18 +775,25 @@
                     || isGroupNotFullyVisible(notificationGroup));
     }
 
+    private boolean isImportantConversation(NotificationEntry entry) {
+        int peopleNotificationType =
+                mPeopleNotificationIdentifier.get().getPeopleNotificationType(entry);
+        return peopleNotificationType == PeopleNotificationIdentifier.TYPE_IMPORTANT_PERSON;
+    }
+
     /**
      * Isolate a notification from its group so that it visually shows as its own group.
      *
      * @param entry the notification to isolate
      */
     private void isolateNotification(NotificationEntry entry) {
-        StatusBarNotification sbn = entry.getSbn();
-
+        if (SPEW) {
+            Log.d(TAG, "isolateNotification: entry=" + entry);
+        }
         // We will be isolated now, so lets update the groups
         onEntryRemovedInternal(entry, entry.getSbn());
 
-        mIsolatedEntries.put(sbn.getKey(), sbn);
+        mIsolatedEntries.put(entry.getKey(), entry.getSbn());
 
         onEntryAddedInternal(entry);
         // We also need to update the suppression of the old group, because this call comes
@@ -588,6 +810,14 @@
      * Update the isolation of an entry, splitting it from the group.
      */
     public void updateIsolation(NotificationEntry entry) {
+        // We need to buffer a few events because we do isolation changes in 3 steps:
+        // removeInternal, update mIsolatedEntries, addInternal.  This means that often the
+        // alertOverride will update on the removal, however processing the event in that case can
+        // cause problems because the mIsolatedEntries map is not in its final state, so the event
+        // listener may be unable to correctly determine the true state of the group.  By delaying
+        // the alertOverride change until after the add phase, we can ensure that listeners only
+        // have to handle a consistent state.
+        mEventBuffer.startBuffering();
         boolean isIsolated = isIsolated(entry.getSbn().getKey());
         if (shouldIsolate(entry)) {
             if (!isIsolated) {
@@ -596,6 +826,7 @@
         } else if (isIsolated) {
             stopIsolatingNotification(entry);
         }
+        mEventBuffer.flushAndStopBuffering();
     }
 
     /**
@@ -604,15 +835,15 @@
      * @param entry the notification to un-isolate
      */
     private void stopIsolatingNotification(NotificationEntry entry) {
-        StatusBarNotification sbn = entry.getSbn();
-        if (isIsolated(sbn.getKey())) {
-            // not isolated anymore, we need to update the groups
-            onEntryRemovedInternal(entry, entry.getSbn());
-            mIsolatedEntries.remove(sbn.getKey());
-            onEntryAddedInternal(entry);
-            for (OnGroupChangeListener listener : mGroupChangeListeners) {
-                listener.onGroupsChanged();
-            }
+        if (SPEW) {
+            Log.d(TAG, "stopIsolatingNotification: entry=" + entry);
+        }
+        // not isolated anymore, we need to update the groups
+        onEntryRemovedInternal(entry, entry.getSbn());
+        mIsolatedEntries.remove(entry.getKey());
+        onEntryAddedInternal(entry);
+        for (OnGroupChangeListener listener : mGroupChangeListeners) {
+            listener.onGroupsChanged();
         }
     }
 
@@ -648,33 +879,154 @@
     }
 
     /**
+     * A record of a notification being posted, containing the time of the post and the key of the
+     * notification entry.  These are stored in a TreeSet by the NotificationGroup and used to
+     * calculate a batch of notifications.
+     */
+    public static class PostRecord implements Comparable<PostRecord> {
+        public final long postTime;
+        public final String key;
+
+        /** constructs a record containing the post time and key from the notification entry */
+        public PostRecord(@NonNull NotificationEntry entry) {
+            this.postTime = entry.getSbn().getPostTime();
+            this.key = entry.getKey();
+        }
+
+        @Override
+        public int compareTo(PostRecord o) {
+            int postTimeComparison = Long.compare(this.postTime, o.postTime);
+            return postTimeComparison == 0
+                    ? String.CASE_INSENSITIVE_ORDER.compare(this.key, o.key)
+                    : postTimeComparison;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            PostRecord that = (PostRecord) o;
+            return postTime == that.postTime && key.equals(that.key);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(postTime, key);
+        }
+    }
+
+    /**
      * Represents a notification group in the notification shade.
      */
     public static class NotificationGroup {
+        public final String groupKey;
         public final HashMap<String, NotificationEntry> children = new HashMap<>();
+        public final TreeSet<PostRecord> postBatchHistory = new TreeSet<>();
         public NotificationEntry summary;
         public boolean expanded;
         /**
          * Is this notification group suppressed, i.e its summary is hidden
          */
         public boolean suppressed;
+        /**
+         * The child (which is isolated from this group) to which the alert should be transferred,
+         * due to priority conversations.
+         */
+        public NotificationEntry alertOverride;
+
+        NotificationGroup(String groupKey) {
+            this.groupKey = groupKey;
+        }
 
         @Override
         public String toString() {
-            String result = "    summary:\n      "
-                    + (summary != null ? summary.getSbn() : "null")
-                    + (summary != null && summary.getDebugThrowable() != null
-                            ? Log.getStackTraceString(summary.getDebugThrowable())
-                            : "");
-            result += "\n    children size: " + children.size();
+            StringBuilder sb = new StringBuilder();
+            sb.append("    groupKey: ").append(groupKey);
+            sb.append("\n    summary:");
+            appendEntry(sb, summary);
+            sb.append("\n    children size: ").append(children.size());
             for (NotificationEntry child : children.values()) {
-                result += "\n      " + child.getSbn()
-                        + (child.getDebugThrowable() != null
-                            ? Log.getStackTraceString(child.getDebugThrowable())
-                            : "");
+                appendEntry(sb, child);
             }
-            result += "\n    summary suppressed: " + suppressed;
-            return result;
+            sb.append("\n    alertOverride:");
+            appendEntry(sb, alertOverride);
+            sb.append("\n    summary suppressed: ").append(suppressed);
+            return sb.toString();
+        }
+
+        private void appendEntry(StringBuilder sb, NotificationEntry entry) {
+            sb.append("\n      ").append(entry != null ? entry.getSbn() : "null");
+            if (entry != null && entry.getDebugThrowable() != null) {
+                sb.append(Log.getStackTraceString(entry.getDebugThrowable()));
+            }
+        }
+    }
+
+    /**
+     * This class is a toggleable buffer for a subset of events of {@link OnGroupChangeListener}.
+     * When buffering, instead of notifying the listeners it will set internal state that will allow
+     * it to notify listeners of those events later
+     */
+    private class EventBuffer {
+        private final HashMap<String, NotificationEntry> mOldAlertOverrideByGroup = new HashMap<>();
+        private boolean mIsBuffering = false;
+        private boolean mDidGroupsChange = false;
+
+        void notifyAlertOverrideChanged(NotificationGroup group,
+                NotificationEntry oldAlertOverride) {
+            if (mIsBuffering) {
+                // The value in this map is the override before the event.  If there is an entry
+                // already in the map, then we are effectively coalescing two events, which means
+                // we need to preserve the original initial value.
+                mOldAlertOverrideByGroup.putIfAbsent(group.groupKey, oldAlertOverride);
+            } else {
+                for (OnGroupChangeListener listener : mGroupChangeListeners) {
+                    listener.onGroupAlertOverrideChanged(group, oldAlertOverride,
+                            group.alertOverride);
+                }
+            }
+        }
+
+        void notifyGroupsChanged() {
+            if (mIsBuffering) {
+                mDidGroupsChange = true;
+            } else {
+                for (OnGroupChangeListener listener : mGroupChangeListeners) {
+                    listener.onGroupsChanged();
+                }
+            }
+        }
+
+        void startBuffering() {
+            mIsBuffering = true;
+        }
+
+        void flushAndStopBuffering() {
+            // stop buffering so that we can call our own helpers
+            mIsBuffering = false;
+            // alert all group alert override changes for groups that were not removed
+            for (Map.Entry<String, NotificationEntry> entry : mOldAlertOverrideByGroup.entrySet()) {
+                NotificationGroup group = mGroupMap.get(entry.getKey());
+                if (group == null) {
+                    // The group can be null if this alertOverride changed before the group was
+                    // permanently removed, meaning that there's no guarantee that listeners will
+                    // that field clear.
+                    continue;
+                }
+                NotificationEntry oldAlertOverride = entry.getValue();
+                if (group.alertOverride == oldAlertOverride) {
+                    // If the final alertOverride equals the initial, it means we coalesced two
+                    // events which undid the change, so we can drop it entirely.
+                    continue;
+                }
+                notifyAlertOverrideChanged(group, oldAlertOverride);
+            }
+            mOldAlertOverrideByGroup.clear();
+            // alert that groups changed
+            if (mDidGroupsChange) {
+                notifyGroupsChanged();
+                mDidGroupsChange = false;
+            }
         }
     }
 
@@ -714,6 +1066,18 @@
                 boolean suppressed) {}
 
         /**
+         * The alert override of a group has changed.
+         *
+         * @param group the group that has changed
+         * @param oldAlertOverride the previous notification to which the group's alerts were sent
+         * @param newAlertOverride the notification to which the group's alerts should now be sent
+         */
+        default void onGroupAlertOverrideChanged(
+                NotificationGroup group,
+                @Nullable NotificationEntry oldAlertOverride,
+                @Nullable NotificationEntry newAlertOverride) {}
+
+        /**
          * A group of children just received a summary notification and should therefore become
          * children of it.
          *
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 2b51b56..b9fe9c4a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -23,6 +23,7 @@
 import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_IN;
 import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_OUT;
 import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.IDLE;
+import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.SHOWING_PERSISTENT_DOT;
 
 import android.animation.ValueAnimator;
 import android.annotation.Nullable;
@@ -348,7 +349,8 @@
 
     private void showSystemIconArea(boolean animate) {
         // Only show the system icon area if we are not currently animating
-        if (mAnimationScheduler.getAnimationState() == IDLE) {
+        int state = mAnimationScheduler.getAnimationState();
+        if (state == IDLE || state == SHOWING_PERSISTENT_DOT) {
             animateShow(mSystemIconArea, animate);
         }
     }
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 71ba091..c64b893 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -929,7 +929,7 @@
         }
         GetWalletCardsRequest request =
                 new GetWalletCardsRequest(1 /* cardWidth */, 1 /* cardHeight */,
-                        1 /* iconSizePx */, 2 /* maxCards */);
+                        1 /* iconSizePx */, 1 /* maxCards */);
         mQuickAccessWalletClient.getWalletCards(mUiExecutor, request, mCardRetriever);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index 3181f52..9787a944 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -22,12 +22,12 @@
 import android.os.SystemClock;
 import android.service.notification.StatusBarNotification;
 import android.util.ArrayMap;
+import android.util.Log;
 
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.Dependency;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.AlertingNotificationManager;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -41,17 +41,21 @@
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 
 /**
  * A helper class dealing with the alert interactions between {@link NotificationGroupManagerLegacy}
  * and {@link HeadsUpManager}. In particular, this class deals with keeping
- * the correct notification in a group alerting based off the group suppression.
+ * the correct notification in a group alerting based off the group suppression and alertOverride.
  */
 public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedListener,
         StateListener {
 
     private static final long ALERT_TRANSFER_TIMEOUT = 300;
+    private static final String TAG = "NotifGroupAlertTransfer";
+    private static final boolean DEBUG = StatusBar.DEBUG;
+    private static final boolean SPEW = StatusBar.SPEW;
 
     /**
      * The list of entries containing group alert metadata for each group. Keyed by group key.
@@ -142,41 +146,98 @@
 
         @Override
         public void onGroupSuppressionChanged(NotificationGroup group, boolean suppressed) {
-            if (suppressed) {
-                if (mHeadsUpManager.isAlerting(group.summary.getKey())) {
-                    handleSuppressedSummaryAlerted(group.summary, mHeadsUpManager);
-                }
-            } else {
-                // Group summary can be null if we are no longer suppressed because the summary was
-                // removed. In that case, we don't need to alert the summary.
-                if (group.summary == null) {
-                    return;
-                }
-                GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(mGroupManager.getGroupKey(
-                        group.summary.getSbn()));
-                // Group is no longer suppressed. We should check if we need to transfer the alert
-                // back to the summary now that it's no longer suppressed.
-                if (groupAlertEntry.mAlertSummaryOnNextAddition) {
-                    if (!mHeadsUpManager.isAlerting(group.summary.getKey())) {
-                        alertNotificationWhenPossible(group.summary, mHeadsUpManager);
-                    }
-                    groupAlertEntry.mAlertSummaryOnNextAddition = false;
-                } else {
-                    checkShouldTransferBack(groupAlertEntry);
-                }
+            if (DEBUG) {
+                Log.d(TAG, "!! onGroupSuppressionChanged: group.summary=" + group.summary
+                        + " suppressed=" + suppressed);
             }
+            NotificationEntry oldAlertOverride = group.alertOverride;
+            onGroupChanged(group, oldAlertOverride);
+        }
+
+        @Override
+        public void onGroupAlertOverrideChanged(NotificationGroup group,
+                @Nullable NotificationEntry oldAlertOverride,
+                @Nullable NotificationEntry newAlertOverride) {
+            if (DEBUG) {
+                Log.d(TAG, "!! onGroupAlertOverrideChanged: group.summary=" + group.summary
+                        + " oldAlertOverride=" + oldAlertOverride
+                        + " newAlertOverride=" + newAlertOverride);
+            }
+            onGroupChanged(group, oldAlertOverride);
         }
     };
 
-    @Override
-    public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
-        onAlertStateChanged(entry, isHeadsUp, mHeadsUpManager);
+    /**
+     * Called when either the suppressed or alertOverride fields of the group changed
+     *
+     * @param group the group which changed
+     * @param oldAlertOverride the previous value of group.alertOverride
+     */
+    private void onGroupChanged(NotificationGroup group,
+            NotificationEntry oldAlertOverride) {
+        // Group summary can be null if we are no longer suppressed because the summary was
+        // removed. In that case, we don't need to alert the summary.
+        if (group.summary == null) {
+            if (DEBUG) {
+                Log.d(TAG, "onGroupChanged: summary is null");
+            }
+            return;
+        }
+        if (group.suppressed || group.alertOverride != null) {
+            checkForForwardAlertTransfer(group.summary, oldAlertOverride);
+        } else {
+            if (DEBUG) {
+                Log.d(TAG, "onGroupChanged: maybe transfer back");
+            }
+            GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(mGroupManager.getGroupKey(
+                    group.summary.getSbn()));
+            // Group is no longer suppressed or overridden.
+            // We should check if we need to transfer the alert back to the summary.
+            if (groupAlertEntry.mAlertSummaryOnNextAddition) {
+                if (!mHeadsUpManager.isAlerting(group.summary.getKey())) {
+                    alertNotificationWhenPossible(group.summary);
+                }
+                groupAlertEntry.mAlertSummaryOnNextAddition = false;
+            } else {
+                checkShouldTransferBack(groupAlertEntry);
+            }
+        }
     }
 
-    private void onAlertStateChanged(NotificationEntry entry, boolean isAlerting,
-            AlertingNotificationManager alertManager) {
-        if (isAlerting && mGroupManager.isSummaryOfSuppressedGroup(entry.getSbn())) {
-            handleSuppressedSummaryAlerted(entry, alertManager);
+    @Override
+    public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
+        if (DEBUG) {
+            Log.d(TAG, "!! onHeadsUpStateChanged: entry=" + entry + " isHeadsUp=" + isHeadsUp);
+        }
+        if (isHeadsUp && entry.getSbn().getNotification().isGroupSummary()) {
+            // a group summary is alerting; trigger the forward transfer checks
+            checkForForwardAlertTransfer(entry, /* oldAlertOverride */ null);
+        }
+    }
+
+    /**
+     * Handles changes in a group's suppression or alertOverride, but where at least one of those
+     * conditions is still true (either the group is suppressed, the group has an alertOverride,
+     * or both).  The method determined which kind of child needs to receive the alert, finds the
+     * entry currently alerting, and makes the transfer.
+     *
+     * Internally, this is handled with two main cases: the override needs the alert, or there is
+     * no override but the summary is suppressed (so an isolated child needs the alert).
+     *
+     * @param summary the notification entry of the summary of the logical group.
+     * @param oldAlertOverride the former value of group.alertOverride, before whatever event
+     *                         required us to check for for a transfer condition.
+     */
+    private void checkForForwardAlertTransfer(NotificationEntry summary,
+            NotificationEntry oldAlertOverride) {
+        if (DEBUG) {
+            Log.d(TAG, "checkForForwardAlertTransfer: enter");
+        }
+        NotificationGroup group = mGroupManager.getGroupForSummary(summary.getSbn());
+        if (group != null && group.alertOverride != null) {
+            handleOverriddenSummaryAlerted(summary);
+        } else if (mGroupManager.isSummaryOfSuppressedGroup(summary.getSbn())) {
+            handleSuppressedSummaryAlerted(summary, oldAlertOverride);
         }
     }
 
@@ -186,9 +247,16 @@
         // see as early as we can if we need to abort a transfer.
         @Override
         public void onPendingEntryAdded(NotificationEntry entry) {
+            if (DEBUG) {
+                Log.d(TAG, "!! onPendingEntryAdded: entry=" + entry);
+            }
             String groupKey = mGroupManager.getGroupKey(entry.getSbn());
             GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(groupKey);
-            if (groupAlertEntry != null) {
+            if (groupAlertEntry != null && groupAlertEntry.mGroup.alertOverride == null) {
+                // new pending group entries require us to transfer back from the child to the
+                // group, but alertOverrides are only present in very limited circumstances, so
+                // while it's possible the group should ALSO alert, the previous detection which set
+                // this alertOverride won't be invalidated by this notification added to this group.
                 checkShouldTransferBack(groupAlertEntry);
             }
         }
@@ -262,43 +330,128 @@
     }
 
     /**
-     * Handles the scenario where a summary that has been suppressed is alerted.  A suppressed
+     * Handles the scenario where a summary that has been suppressed is itself, or has a former
+     * alertOverride (in the form of an isolated logical child) which was alerted.  A suppressed
      * summary should for all intents and purposes be invisible to the user and as a result should
      * not alert.  When this is the case, it is our responsibility to pass the alert to the
      * appropriate child which will be the representative notification alerting for the group.
      *
-     * @param summary the summary that is suppressed and alerting
-     * @param alertManager the alert manager that manages the alerting summary
+     * @param summary the summary that is suppressed and (potentially) alerting
+     * @param oldAlertOverride the alertOverride before whatever event triggered this method.  If
+     *                         the alert override was removed, this will be the entry that should
+     *                         be transferred back from.
      */
     private void handleSuppressedSummaryAlerted(@NonNull NotificationEntry summary,
-            @NonNull AlertingNotificationManager alertManager) {
-        StatusBarNotification sbn = summary.getSbn();
+            NotificationEntry oldAlertOverride) {
+        if (DEBUG) {
+            Log.d(TAG, "handleSuppressedSummaryAlerted: summary=" + summary);
+        }
         GroupAlertEntry groupAlertEntry =
-                mGroupAlertEntries.get(mGroupManager.getGroupKey(sbn));
+                mGroupAlertEntries.get(mGroupManager.getGroupKey(summary.getSbn()));
+
         if (!mGroupManager.isSummaryOfSuppressedGroup(summary.getSbn())
-                || !alertManager.isAlerting(sbn.getKey())
                 || groupAlertEntry == null) {
+            if (DEBUG) {
+                Log.d(TAG, "handleSuppressedSummaryAlerted: invalid state");
+            }
+            return;
+        }
+        boolean summaryIsAlerting = mHeadsUpManager.isAlerting(summary.getKey());
+        boolean priorityIsAlerting = oldAlertOverride != null
+                && mHeadsUpManager.isAlerting(oldAlertOverride.getKey());
+        if (!summaryIsAlerting && !priorityIsAlerting) {
+            if (DEBUG) {
+                Log.d(TAG, "handleSuppressedSummaryAlerted: no summary or override alerting");
+            }
             return;
         }
 
         if (pendingInflationsWillAddChildren(groupAlertEntry.mGroup)) {
             // New children will actually be added to this group, let's not transfer the alert.
+            if (DEBUG) {
+                Log.d(TAG, "handleSuppressedSummaryAlerted: pending inflations");
+            }
             return;
         }
 
         NotificationEntry child =
                 mGroupManager.getLogicalChildren(summary.getSbn()).iterator().next();
-        if (child != null) {
-            if (child.getRow().keepInParent()
-                    || child.isRowRemoved()
-                    || child.isRowDismissed()) {
-                // The notification is actually already removed. No need to alert it.
-                return;
+        if (summaryIsAlerting) {
+            if (DEBUG) {
+                Log.d(TAG, "handleSuppressedSummaryAlerted: transfer summary -> child");
             }
-            if (!alertManager.isAlerting(child.getKey()) && onlySummaryAlerts(summary)) {
-                groupAlertEntry.mLastAlertTransferTime = SystemClock.elapsedRealtime();
+            tryTransferAlertState(summary, /*from*/ summary, /*to*/ child, groupAlertEntry);
+            return;
+        }
+        // Summary didn't have the alert, so we're in "transfer back" territory.  First, make sure
+        // it's not too late to transfer back, then transfer the alert from the oldAlertOverride to
+        // the isolated child which should receive the alert.
+        if (!canStillTransferBack(groupAlertEntry)) {
+            if (DEBUG) {
+                Log.d(TAG, "handleSuppressedSummaryAlerted: transfer from override: too late");
             }
-            transferAlertState(summary, child, alertManager);
+            return;
+        }
+
+        if (DEBUG) {
+            Log.d(TAG, "handleSuppressedSummaryAlerted: transfer override -> child");
+        }
+        tryTransferAlertState(summary, /*from*/ oldAlertOverride, /*to*/ child, groupAlertEntry);
+    }
+
+    /**
+     * Checks for and handles the scenario where the given entry is the summary of a group which
+     * has an alertOverride, and either the summary itself or one of its logical isolated children
+     * is currently alerting (which happens if the summary is suppressed).
+     */
+    private void handleOverriddenSummaryAlerted(NotificationEntry summary) {
+        if (DEBUG) {
+            Log.d(TAG, "handleOverriddenSummaryAlerted: summary=" + summary);
+        }
+        GroupAlertEntry groupAlertEntry =
+                mGroupAlertEntries.get(mGroupManager.getGroupKey(summary.getSbn()));
+        NotificationGroup group = mGroupManager.getGroupForSummary(summary.getSbn());
+        if (group == null || group.alertOverride == null || groupAlertEntry == null) {
+            if (DEBUG) {
+                Log.d(TAG, "handleOverriddenSummaryAlerted: invalid state");
+            }
+            return;
+        }
+        boolean summaryIsAlerting = mHeadsUpManager.isAlerting(summary.getKey());
+        if (summaryIsAlerting) {
+            if (DEBUG) {
+                Log.d(TAG, "handleOverriddenSummaryAlerted: transfer summary -> override");
+            }
+            tryTransferAlertState(summary, /*from*/ summary, group.alertOverride, groupAlertEntry);
+            return;
+        }
+        // Summary didn't have the alert, so we're in "transfer back" territory.  First, make sure
+        // it's not too late to transfer back, then remove the alert from any of the logical
+        // children, and if one of them was alerting, we can alert the override.
+        if (!canStillTransferBack(groupAlertEntry)) {
+            if (DEBUG) {
+                Log.d(TAG, "handleOverriddenSummaryAlerted: transfer from child: too late");
+            }
+            return;
+        }
+        List<NotificationEntry> children = mGroupManager.getLogicalChildren(summary.getSbn());
+        if (children == null) {
+            if (DEBUG) {
+                Log.d(TAG, "handleOverriddenSummaryAlerted: no children");
+            }
+            return;
+        }
+        children.remove(group.alertOverride); // do not release the alert on our desired destination
+        boolean releasedChild = releaseChildAlerts(children);
+        if (releasedChild) {
+            if (DEBUG) {
+                Log.d(TAG, "handleOverriddenSummaryAlerted: transfer child -> override");
+            }
+            tryTransferAlertState(summary, /*from*/ null, group.alertOverride, groupAlertEntry);
+        } else {
+            if (DEBUG) {
+                Log.d(TAG, "handleOverriddenSummaryAlerted: no child alert released");
+            }
         }
     }
 
@@ -307,14 +460,37 @@
      * immediately to have the incorrect one up as short as possible. The second should alert
      * when possible.
      *
+     * @param summary entry of the summary
      * @param fromEntry entry to transfer alert from
      * @param toEntry entry to transfer to
-     * @param alertManager alert manager for the alert type
      */
-    private void transferAlertState(@NonNull NotificationEntry fromEntry, @NonNull NotificationEntry toEntry,
-            @NonNull AlertingNotificationManager alertManager) {
-        alertManager.removeNotification(fromEntry.getKey(), true /* releaseImmediately */);
-        alertNotificationWhenPossible(toEntry, alertManager);
+    private void tryTransferAlertState(
+            NotificationEntry summary,
+            NotificationEntry fromEntry,
+            NotificationEntry toEntry,
+            GroupAlertEntry groupAlertEntry) {
+        if (toEntry != null) {
+            if (toEntry.getRow().keepInParent()
+                    || toEntry.isRowRemoved()
+                    || toEntry.isRowDismissed()) {
+                // The notification is actually already removed. No need to alert it.
+                return;
+            }
+            if (!mHeadsUpManager.isAlerting(toEntry.getKey()) && onlySummaryAlerts(summary)) {
+                groupAlertEntry.mLastAlertTransferTime = SystemClock.elapsedRealtime();
+            }
+            if (DEBUG) {
+                Log.d(TAG, "transferAlertState: fromEntry=" + fromEntry + " toEntry=" + toEntry);
+            }
+            transferAlertState(fromEntry, toEntry);
+        }
+    }
+    private void transferAlertState(@Nullable NotificationEntry fromEntry,
+            @NonNull NotificationEntry toEntry) {
+        if (fromEntry != null) {
+            mHeadsUpManager.removeNotification(fromEntry.getKey(), true /* releaseImmediately */);
+        }
+        alertNotificationWhenPossible(toEntry);
     }
 
     /**
@@ -326,11 +502,13 @@
      * more children are coming. Thus, if a child is added within a certain timeframe after we
      * transfer, we back out and alert the summary again.
      *
+     * An alert can only transfer back within a small window of time after a transfer away from the
+     * summary to a child happened.
+     *
      * @param groupAlertEntry group alert entry to check
      */
     private void checkShouldTransferBack(@NonNull GroupAlertEntry groupAlertEntry) {
-        if (SystemClock.elapsedRealtime() - groupAlertEntry.mLastAlertTransferTime
-                < ALERT_TRANSFER_TIMEOUT) {
+        if (canStillTransferBack(groupAlertEntry)) {
             NotificationEntry summary = groupAlertEntry.mGroup.summary;
 
             if (!onlySummaryAlerts(summary)) {
@@ -338,30 +516,17 @@
             }
             ArrayList<NotificationEntry> children = mGroupManager.getLogicalChildren(
                     summary.getSbn());
-            int numChildren = children.size();
+            int numActiveChildren = children.size();
             int numPendingChildren = getPendingChildrenNotAlerting(groupAlertEntry.mGroup);
-            numChildren += numPendingChildren;
+            int numChildren = numActiveChildren + numPendingChildren;
             if (numChildren <= 1) {
                 return;
             }
-            boolean releasedChild = false;
-            for (int i = 0; i < children.size(); i++) {
-                NotificationEntry entry = children.get(i);
-                if (onlySummaryAlerts(entry) && mHeadsUpManager.isAlerting(entry.getKey())) {
-                    releasedChild = true;
-                    mHeadsUpManager.removeNotification(
-                            entry.getKey(), true /* releaseImmediately */);
-                }
-                if (mPendingAlerts.containsKey(entry.getKey())) {
-                    // This is the child that would've been removed if it was inflated.
-                    releasedChild = true;
-                    mPendingAlerts.get(entry.getKey()).mAbortOnInflation = true;
-                }
-            }
+            boolean releasedChild = releaseChildAlerts(children);
             if (releasedChild && !mHeadsUpManager.isAlerting(summary.getKey())) {
-                boolean notifyImmediately = (numChildren - numPendingChildren) > 1;
+                boolean notifyImmediately = numActiveChildren > 1;
                 if (notifyImmediately) {
-                    alertNotificationWhenPossible(summary, mHeadsUpManager);
+                    alertNotificationWhenPossible(summary);
                 } else {
                     // Should wait until the pending child inflates before alerting.
                     groupAlertEntry.mAlertSummaryOnNextAddition = true;
@@ -371,25 +536,61 @@
         }
     }
 
+    private boolean canStillTransferBack(@NonNull GroupAlertEntry groupAlertEntry) {
+        return SystemClock.elapsedRealtime() - groupAlertEntry.mLastAlertTransferTime
+                < ALERT_TRANSFER_TIMEOUT;
+    }
+
+    private boolean releaseChildAlerts(List<NotificationEntry> children) {
+        boolean releasedChild = false;
+        if (SPEW) {
+            Log.d(TAG, "releaseChildAlerts: numChildren=" + children.size());
+        }
+        for (int i = 0; i < children.size(); i++) {
+            NotificationEntry entry = children.get(i);
+            if (SPEW) {
+                Log.d(TAG, "releaseChildAlerts: checking i=" + i + " entry=" + entry
+                        + " onlySummaryAlerts=" + onlySummaryAlerts(entry)
+                        + " isAlerting=" + mHeadsUpManager.isAlerting(entry.getKey())
+                        + " isPendingAlert=" + mPendingAlerts.containsKey(entry.getKey()));
+            }
+            if (onlySummaryAlerts(entry) && mHeadsUpManager.isAlerting(entry.getKey())) {
+                releasedChild = true;
+                mHeadsUpManager.removeNotification(
+                        entry.getKey(), true /* releaseImmediately */);
+            }
+            if (mPendingAlerts.containsKey(entry.getKey())) {
+                // This is the child that would've been removed if it was inflated.
+                releasedChild = true;
+                mPendingAlerts.get(entry.getKey()).mAbortOnInflation = true;
+            }
+        }
+        if (SPEW) {
+            Log.d(TAG, "releaseChildAlerts: didRelease=" + releasedChild);
+        }
+        return releasedChild;
+    }
+
     /**
      * Tries to alert the notification. If its content view is not inflated, we inflate and continue
      * when the entry finishes inflating the view.
      *
      * @param entry entry to show
-     * @param alertManager alert manager for the alert type
      */
-    private void alertNotificationWhenPossible(@NonNull NotificationEntry entry,
-            @NonNull AlertingNotificationManager alertManager) {
-        @InflationFlag int contentFlag = alertManager.getContentFlag();
+    private void alertNotificationWhenPossible(@NonNull NotificationEntry entry) {
+        @InflationFlag int contentFlag = mHeadsUpManager.getContentFlag();
         final RowContentBindParams params = mRowContentBindStage.getStageParams(entry);
         if ((params.getContentViews() & contentFlag) == 0) {
+            if (DEBUG) {
+                Log.d(TAG, "alertNotificationWhenPossible: async requestRebind entry=" + entry);
+            }
             mPendingAlerts.put(entry.getKey(), new PendingAlertInfo(entry));
             params.requireContentViews(contentFlag);
             mRowContentBindStage.requestRebind(entry, en -> {
                 PendingAlertInfo alertInfo = mPendingAlerts.remove(entry.getKey());
                 if (alertInfo != null) {
                     if (alertInfo.isStillValid()) {
-                        alertNotificationWhenPossible(entry, mHeadsUpManager);
+                        alertNotificationWhenPossible(entry);
                     } else {
                         // The transfer is no longer valid. Free the content.
                         mRowContentBindStage.getStageParams(entry).markContentViewsFreeable(
@@ -400,10 +601,16 @@
             });
             return;
         }
-        if (alertManager.isAlerting(entry.getKey())) {
-            alertManager.updateNotification(entry.getKey(), true /* alert */);
+        if (mHeadsUpManager.isAlerting(entry.getKey())) {
+            if (DEBUG) {
+                Log.d(TAG, "alertNotificationWhenPossible: continue alerting entry=" + entry);
+            }
+            mHeadsUpManager.updateNotification(entry.getKey(), true /* alert */);
         } else {
-            alertManager.showNotification(entry);
+            if (DEBUG) {
+                Log.d(TAG, "alertNotificationWhenPossible: start alerting entry=" + entry);
+            }
+            mHeadsUpManager.showNotification(entry);
         }
     }
 
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 c0d713b..16abd12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -122,6 +122,7 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.events.PrivacyDotViewController;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.notification.ConversationNotificationManager;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
@@ -306,6 +307,7 @@
     private final QSDetailDisplayer mQSDetailDisplayer;
     private final FeatureFlags mFeatureFlags;
     private final ScrimController mScrimController;
+    private final PrivacyDotViewController mPrivacyDotViewController;
 
     // Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow card.
     // If there are exactly 1 + mMaxKeyguardNotifications, then still shows all notifications
@@ -602,6 +604,7 @@
             FeatureFlags featureFlags,
             QuickAccessWalletClient quickAccessWalletClient,
             KeyguardMediaController keyguardMediaController,
+            PrivacyDotViewController privacyDotViewController,
             @Main Executor uiExecutor) {
         super(view, falsingManager, dozeLog, keyguardStateController,
                 (SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
@@ -610,6 +613,7 @@
         mView = view;
         mVibratorHelper = vibratorHelper;
         mKeyguardMediaController = keyguardMediaController;
+        mPrivacyDotViewController = privacyDotViewController;
         mMetricsLogger = metricsLogger;
         mActivityManager = activityManager;
         mConfigurationController = configurationController;
@@ -1895,6 +1899,7 @@
             mKeyguardBypassController.setQSExpanded(expanded);
             mStatusBarKeyguardViewManager.setQsExpanded(expanded);
             mLockIconViewController.setQsExpanded(expanded);
+            mPrivacyDotViewController.setQsExpanded(expanded);
         }
     }
 
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 142cf21..ebf2465 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -97,13 +97,13 @@
         mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity);
 
         mIconController = iconController;
+        mCarrierConfigTracker = carrierConfigTracker;
         mNetworkController = Dependency.get(NetworkController.class);
         mSecurityController = Dependency.get(SecurityController.class);
 
         Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_HIDE_LIST);
         mNetworkController.addCallback(this);
         mSecurityController.addCallback(this);
-        mCarrierConfigTracker = carrierConfigTracker;
     }
 
     public void destroy() {
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 f45218d..07e9fed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -364,11 +364,11 @@
                 if (network.equals(mLastNetwork) && validated == lastValidated) {
                     // Should not rely on getTransportTypes() returning the same order of transport
                     // types. So sort the array before comparing.
-                    int[] newTypes = networkCapabilities.getTransportTypes();
+                    int[] newTypes = getProcessedTransportTypes(networkCapabilities);
                     Arrays.sort(newTypes);
 
                     int[] lastTypes = (mLastNetworkCapabilities != null)
-                            ? mLastNetworkCapabilities.getTransportTypes() : null;
+                            ? getProcessedTransportTypes(mLastNetworkCapabilities) : null;
                     if (lastTypes != null) Arrays.sort(lastTypes);
 
                     if (Arrays.equals(newTypes, lastTypes)) {
@@ -533,6 +533,21 @@
         return mPhone.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE;
     }
 
+    private int[] getProcessedTransportTypes(NetworkCapabilities networkCapabilities) {
+        int[] transportTypes = networkCapabilities.getTransportTypes();
+        for (int i = 0; i < transportTypes.length; i++) {
+            // For VCN over WiFi, the transportType is set to be TRANSPORT_CELLULAR in the
+            // NetworkCapabilities, but we need to convert it into TRANSPORT_WIFI in order to
+            // distinguish it from VCN over Cellular.
+            if (transportTypes[i] == NetworkCapabilities.TRANSPORT_CELLULAR
+                    && Utils.tryGetWifiInfoForVcn(networkCapabilities) != null) {
+                transportTypes[i] = NetworkCapabilities.TRANSPORT_WIFI;
+                break;
+            }
+        }
+        return transportTypes;
+    }
+
     private MobileSignalController getDataController() {
         int dataSubId = mSubDefaults.getActiveDataSubId();
         return getControllerWithSubId(dataSubId);
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 edec618..41b1dd1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -23,6 +23,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.Button;
+import android.widget.TextView;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ContrastColorUtil;
@@ -58,15 +59,6 @@
     /** Spacing to be applied between views. */
     private final int mSpacing;
 
-    /** Horizontal padding of smart reply buttons if all of them use only one line of text. */
-    private final int mSingleLineButtonPaddingHorizontal;
-
-    /** Horizontal padding of smart reply buttons if at least one of them uses two lines of text. */
-    private final int mDoubleLineButtonPaddingHorizontal;
-
-    /** Increase in width of a smart reply button as a result of using two lines instead of one. */
-    private final int mSingleToDoubleLineButtonWidthIncrease;
-
     private final BreakIterator mBreakIterator;
 
     private PriorityQueue<Button> mCandidateButtonQueueForSqueezing;
@@ -114,8 +106,6 @@
                 mDefaultBackgroundColor);
 
         int spacing = 0;
-        int singleLineButtonPaddingHorizontal = 0;
-        int doubleLineButtonPaddingHorizontal = 0;
         int strokeWidth = 0;
 
         final TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.SmartReplyView,
@@ -125,10 +115,6 @@
             int attr = arr.getIndex(i);
             if (attr == R.styleable.SmartReplyView_spacing) {
                 spacing = arr.getDimensionPixelSize(i, 0);
-            } else if (attr == R.styleable.SmartReplyView_singleLineButtonPaddingHorizontal) {
-                singleLineButtonPaddingHorizontal = arr.getDimensionPixelSize(i, 0);
-            } else if (attr == R.styleable.SmartReplyView_doubleLineButtonPaddingHorizontal) {
-                doubleLineButtonPaddingHorizontal = arr.getDimensionPixelSize(i, 0);
             } else if (attr == R.styleable.SmartReplyView_buttonStrokeWidth) {
                 strokeWidth = arr.getDimensionPixelSize(i, 0);
             }
@@ -137,10 +123,6 @@
 
         mStrokeWidth = strokeWidth;
         mSpacing = spacing;
-        mSingleLineButtonPaddingHorizontal = singleLineButtonPaddingHorizontal;
-        mDoubleLineButtonPaddingHorizontal = doubleLineButtonPaddingHorizontal;
-        mSingleToDoubleLineButtonWidthIncrease =
-                2 * (doubleLineButtonPaddingHorizontal - singleLineButtonPaddingHorizontal);
 
         mBreakIterator = BreakIterator.getLineInstance();
 
@@ -222,6 +204,12 @@
         return new LayoutParams(params.width, params.height);
     }
 
+    private void clearLayoutLineCount(View view) {
+        if (view instanceof TextView) {
+            ((TextView) view).nullLayouts();
+        }
+    }
+
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         final int targetWidth = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.UNSPECIFIED
@@ -237,8 +225,7 @@
 
         SmartSuggestionMeasures accumulatedMeasures = new SmartSuggestionMeasures(
                 mPaddingLeft + mPaddingRight,
-                0 /* maxChildHeight */,
-                mSingleLineButtonPaddingHorizontal);
+                0 /* maxChildHeight */);
         int displayedChildCount = 0;
 
         // Set up a list of suggestions where actions come before replies. Note that the Buttons
@@ -268,8 +255,7 @@
                 continue;
             }
 
-            child.setPadding(accumulatedMeasures.mButtonPaddingHorizontal, child.getPaddingTop(),
-                    accumulatedMeasures.mButtonPaddingHorizontal, child.getPaddingBottom());
+            clearLayoutLineCount(child);
             child.measure(MEASURE_SPEC_ANY_LENGTH, heightMeasureSpec);
 
             coveredSuggestions.add(child);
@@ -299,18 +285,6 @@
             accumulatedMeasures.mMaxChildHeight =
                     Math.max(accumulatedMeasures.mMaxChildHeight, childHeight);
 
-            // Do we need to increase the number of lines in smart reply buttons to two?
-            final boolean increaseToTwoLines =
-                    (accumulatedMeasures.mButtonPaddingHorizontal
-                            == mSingleLineButtonPaddingHorizontal)
-                    && (lineCount == 2 || accumulatedMeasures.mMeasuredWidth > targetWidth);
-            if (increaseToTwoLines) {
-                accumulatedMeasures.mMeasuredWidth +=
-                        (displayedChildCount + 1) * mSingleToDoubleLineButtonWidthIncrease;
-                accumulatedMeasures.mButtonPaddingHorizontal =
-                        mDoubleLineButtonPaddingHorizontal;
-            }
-
             // If the last button doesn't fit into the remaining width, try squeezing preceding
             // smart reply buttons.
             if (accumulatedMeasures.mMeasuredWidth > targetWidth) {
@@ -372,18 +346,11 @@
         mCandidateButtonQueueForSqueezing.clear();
 
         // Finally, we need to re-measure some buttons.
-        remeasureButtonsIfNecessary(accumulatedMeasures.mButtonPaddingHorizontal,
-                                    accumulatedMeasures.mMaxChildHeight);
+        remeasureButtonsIfNecessary(accumulatedMeasures.mMaxChildHeight);
 
         int buttonHeight = Math.max(getSuggestedMinimumHeight(), mPaddingTop
                 + accumulatedMeasures.mMaxChildHeight + mPaddingBottom);
 
-        // Set the corner radius to half the button height to make the side of the buttons look like
-        // a semicircle.
-        for (View smartSuggestionButton : smartSuggestions) {
-            setCornerRadius((Button) smartSuggestionButton, ((float) buttonHeight) / 2);
-        }
-
         setMeasuredDimension(
                 resolveSize(Math.max(getSuggestedMinimumWidth(),
                                      accumulatedMeasures.mMeasuredWidth),
@@ -411,18 +378,14 @@
     private static class SmartSuggestionMeasures {
         int mMeasuredWidth = -1;
         int mMaxChildHeight = -1;
-        int mButtonPaddingHorizontal = -1;
 
-        SmartSuggestionMeasures(int measuredWidth, int maxChildHeight,
-                int buttonPaddingHorizontal) {
+        SmartSuggestionMeasures(int measuredWidth, int maxChildHeight) {
             this.mMeasuredWidth = measuredWidth;
             this.mMaxChildHeight = maxChildHeight;
-            this.mButtonPaddingHorizontal = buttonPaddingHorizontal;
         }
 
         public SmartSuggestionMeasures clone() {
-            return new SmartSuggestionMeasures(
-                    mMeasuredWidth, mMaxChildHeight, mButtonPaddingHorizontal);
+            return new SmartSuggestionMeasures(mMeasuredWidth, mMaxChildHeight);
         }
     }
 
@@ -553,17 +516,11 @@
 
     private int squeezeButtonToTextWidth(Button button, int heightMeasureSpec, int textWidth) {
         int oldWidth = button.getMeasuredWidth();
-        if (button.getPaddingLeft() != mDoubleLineButtonPaddingHorizontal) {
-            // Correct for the fact that the button was laid out with single-line horizontal
-            // padding.
-            oldWidth += mSingleToDoubleLineButtonWidthIncrease;
-        }
 
         // Re-measure the squeezed smart reply button.
-        button.setPadding(mDoubleLineButtonPaddingHorizontal, button.getPaddingTop(),
-                mDoubleLineButtonPaddingHorizontal, button.getPaddingBottom());
+        clearLayoutLineCount(button);
         final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(
-                2 * mDoubleLineButtonPaddingHorizontal + textWidth
+                button.getPaddingLeft() + button.getPaddingRight() + textWidth
                       + getLeftCompoundDrawableWidthWithPadding(button), MeasureSpec.AT_MOST);
         button.measure(widthMeasureSpec, heightMeasureSpec);
 
@@ -579,8 +536,7 @@
         }
     }
 
-    private void remeasureButtonsIfNecessary(
-            int buttonPaddingHorizontal, int maxChildHeight) {
+    private void remeasureButtonsIfNecessary(int maxChildHeight) {
         final int maxChildHeightMeasure =
                 MeasureSpec.makeMeasureSpec(maxChildHeight, MeasureSpec.EXACTLY);
 
@@ -602,24 +558,7 @@
                 newWidth = Integer.MAX_VALUE;
             }
 
-            // Re-measure reason 2: The button's horizontal padding is incorrect (because it was
-            // measured with the wrong number of lines).
-            if (child.getPaddingLeft() != buttonPaddingHorizontal) {
-                requiresNewMeasure = true;
-                if (newWidth != Integer.MAX_VALUE) {
-                    if (buttonPaddingHorizontal == mSingleLineButtonPaddingHorizontal) {
-                        // Change padding (2->1 line).
-                        newWidth -= mSingleToDoubleLineButtonWidthIncrease;
-                    } else {
-                        // Change padding (1->2 lines).
-                        newWidth += mSingleToDoubleLineButtonWidthIncrease;
-                    }
-                }
-                child.setPadding(buttonPaddingHorizontal, child.getPaddingTop(),
-                        buttonPaddingHorizontal, child.getPaddingBottom());
-            }
-
-            // Re-measure reason 3: The button's height is less than the max height of all buttons
+            // Re-measure reason 2: The button's height is less than the max height of all buttons
             // (all should have the same height).
             if (child.getMeasuredHeight() != maxChildHeight) {
                 requiresNewMeasure = true;
@@ -725,23 +664,6 @@
         button.setTextColor(mCurrentTextColor);
     }
 
-    private void setCornerRadius(Button button, float radius) {
-        Drawable drawable = button.getBackground();
-        if (drawable instanceof RippleDrawable) {
-            // Mutate in case other notifications are using this drawable.
-            drawable = drawable.mutate();
-            RippleDrawable ripple = (RippleDrawable) drawable;
-            Drawable inset = ripple.getDrawable(0);
-            if (inset instanceof InsetDrawable) {
-                Drawable background = ((InsetDrawable) inset).getDrawable();
-                if (background instanceof GradientDrawable) {
-                    GradientDrawable gradientDrawable = (GradientDrawable) background;
-                    gradientDrawable.setCornerRadius(radius);
-                }
-            }
-        }
-    }
-
     enum SmartButtonType {
         REPLY,
         ACTION
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java
index 644addf..5e9bae9 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/DotIndicatorDecoration.java
@@ -20,13 +20,10 @@
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Rect;
-import android.text.TextPaint;
 import android.util.MathUtils;
 import android.view.View;
-import android.widget.TextView;
 
 import androidx.annotation.ColorInt;
-import androidx.core.content.ContextCompat;
 import androidx.core.graphics.ColorUtils;
 import androidx.recyclerview.widget.RecyclerView;
 
@@ -39,12 +36,10 @@
     @ColorInt private final int mUnselectedColor;
     @ColorInt private final int mSelectedColor;
     private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-    private final TextPaint mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
     private WalletCardCarousel mCardCarousel;
 
     DotIndicatorDecoration(Context context) {
         super();
-
         mUnselectedRadius =
                 context.getResources().getDimensionPixelSize(
                         R.dimen.card_carousel_dot_unselected_radius);
@@ -53,13 +48,8 @@
                         R.dimen.card_carousel_dot_selected_radius);
         mDotMargin = context.getResources().getDimensionPixelSize(R.dimen.card_carousel_dot_margin);
 
-        TextView textView = new TextView(context);
-        mTextPaint.set(textView.getPaint());
-        // Text color is not copied from text appearance.
-        mTextPaint.setColor(ContextCompat.getColor(context, R.color.GM2_blue_600));
-
-        mUnselectedColor = ContextCompat.getColor(context, R.color.GM2_grey_300);
-        mSelectedColor = ContextCompat.getColor(context, R.color.GM2_blue_600);
+        mUnselectedColor = context.getColor(com.android.internal.R.color.system_neutral1_300);
+        mSelectedColor = context.getColor(com.android.internal.R.color.system_neutral1_0);
     }
 
     @Override
@@ -107,9 +97,9 @@
             int i = isLayoutLtr() ? itemsDrawn : itemCount - itemsDrawn - 1;
 
             if (isSelectedItem(i)) {
-                drawSelectedDot(canvas, interpolatedProgress, i);
+                drawSelectedDot(canvas, interpolatedProgress);
             } else if (isNextItemInScrollingDirection(i)) {
-                drawFadingUnselectedDot(canvas, interpolatedProgress, i);
+                drawFadingUnselectedDot(canvas, interpolatedProgress);
             } else {
                 drawUnselectedDot(canvas);
             }
@@ -121,7 +111,7 @@
         this.mCardCarousel = null; // No need to hold a reference.
     }
 
-    private void drawSelectedDot(Canvas canvas, float progress, int position) {
+    private void drawSelectedDot(Canvas canvas, float progress) {
         // Divide progress by 2 because the other half of the animation is done by
         // drawFadingUnselectedDot.
         mPaint.setColor(
@@ -132,13 +122,13 @@
         canvas.translate(radius * 2, 0);
     }
 
-    private void drawFadingUnselectedDot(Canvas canvas, float progress, int position) {
+    private void drawFadingUnselectedDot(Canvas canvas, float progress) {
         // Divide progress by 2 because the first half of the animation is done by drawSelectedDot.
         int blendedColor =
                 ColorUtils.blendARGB(
                         mUnselectedColor, mSelectedColor, progress / 2);
         mPaint.setColor(getTransitionAdjustedColor(blendedColor));
-        float radius = MathUtils.lerp(mSelectedRadius, mUnselectedRadius, progress / 2);
+        float radius = MathUtils.lerp(mUnselectedRadius, mSelectedColor, progress / 2);
         canvas.drawCircle(radius, 0, radius, mPaint);
         canvas.translate(radius * 2, 0);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
index ac8b16a..66bd48b 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
@@ -17,6 +17,7 @@
 package com.android.systemui.wallet.ui;
 
 import android.graphics.Color;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.Handler;
 import android.service.quickaccesswallet.QuickAccessWalletClient;
@@ -95,7 +96,7 @@
         }
         setTitle("");
         getActionBar().setDisplayHomeAsUpEnabled(true);
-        getActionBar().setHomeAsUpIndicator(R.drawable.ic_close);
+        getActionBar().setHomeAsUpIndicator(getHomeIndicatorDrawable());
         getActionBar().setHomeActionContentDescription(R.string.accessibility_desc_close);
         WalletView walletView = requireViewById(R.id.wallet_view);
         mWalletScreenController = new WalletScreenController(
@@ -175,4 +176,10 @@
         mWalletScreenController.onDismissed();
         super.onDestroy();
     }
+
+    private Drawable getHomeIndicatorDrawable() {
+        Drawable drawable = getDrawable(R.drawable.ic_close);
+        drawable.setTint(getColor(com.android.internal.R.color.system_neutral1_300));
+        return drawable;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
index ec62981..b57d937 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
@@ -234,7 +234,9 @@
         mWalletView.show();
         mWalletView.hideErrorMessage();
         int iconSizePx =
-                mContext.getResources().getDimensionPixelSize(R.dimen.wallet_view_header_icon_size);
+                mContext
+                        .getResources()
+                        .getDimensionPixelSize(R.dimen.wallet_screen_header_icon_size);
         GetWalletCardsRequest request =
                 new GetWalletCardsRequest(cardWidthPx, cardHeightPx, iconSizePx, MAX_CARDS);
         mWalletClient.getWalletCards(mExecutor, request, this);
@@ -340,7 +342,11 @@
 
         @Override
         public CharSequence getLabel() {
-            return mWalletCard.getCardLabel();
+            CharSequence label = mWalletCard.getCardLabel();
+            if (label == null) {
+                return "";
+            }
+            return label;
         }
 
         @Override
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 c547bb3..e42ce6a 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
@@ -37,6 +37,7 @@
 import android.widget.TextView;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.Utils;
 import com.android.systemui.R;
 
 import java.util.List;
@@ -100,7 +101,7 @@
     public void onCardScroll(WalletCardViewInfo centerCard, WalletCardViewInfo nextCard,
             float percentDistanceFromCenter) {
         CharSequence centerCardText = getLabelText(centerCard);
-        Drawable centerCardIcon = centerCard.getIcon();
+        Drawable centerCardIcon = getHeaderIcon(mContext, centerCard);
         if (!TextUtils.equals(mCenterCardText, centerCardText)) {
             mCenterCardText = centerCardText;
             mCardLabel.setText(centerCardText);
@@ -133,7 +134,8 @@
         mCardCarouselContainer.setVisibility(VISIBLE);
         mErrorView.setVisibility(GONE);
         mEmptyStateView.setVisibility(GONE);
-        renderHeaderIconAndActionButton(data.get(selectedIndex), isDeviceLocked);
+        mIcon.setImageDrawable(getHeaderIcon(mContext, data.get(selectedIndex)));
+        renderActionButton(data.get(selectedIndex), isDeviceLocked);
         if (shouldAnimate) {
             animateViewsShown(mIcon, mCardLabel, mActionButton);
         }
@@ -220,10 +222,15 @@
         return mCardLabel;
     }
 
-    private void renderHeaderIconAndActionButton(WalletCardViewInfo walletCard, boolean isLocked) {
-        mIcon.setImageDrawable(walletCard.getIcon());
-        mIcon.setVisibility(VISIBLE);
-        renderActionButton(walletCard, isLocked);
+    @Nullable
+    private static Drawable getHeaderIcon(Context context, WalletCardViewInfo walletCard) {
+        Drawable icon = walletCard.getIcon();
+        if (icon != null) {
+            icon.setTint(
+                    Utils.getColorAttrDefaultColor(
+                            context, com.android.internal.R.attr.colorAccentPrimary));
+        }
+        return icon;
     }
 
     private void renderActionButton(WalletCardViewInfo walletCard, boolean isDeviceLocked) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
index 1d9eaae..77286b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
@@ -37,7 +37,8 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.statusbar.CommandQueue;
 
 import org.junit.Before;
@@ -67,9 +68,12 @@
     @Mock
     private ModeSwitchesController mModeSwitchesController;
     @Mock
-    private NavigationModeController mNavigationModeController;
+    private SysUiState mSysUiState;
     @Mock
     private IRemoteMagnificationAnimationCallback mAnimationCallback;
+    @Mock
+    private OverviewProxyService mOverviewProxyService;
+
     private IWindowMagnificationConnection mIWindowMagnificationConnection;
     private WindowMagnification mWindowMagnification;
 
@@ -83,8 +87,8 @@
         }).when(mAccessibilityManager).setWindowMagnificationConnection(
                 any(IWindowMagnificationConnection.class));
         mWindowMagnification = new WindowMagnification(getContext(),
-                getContext().getMainThreadHandler(), mCommandQueue, mModeSwitchesController,
-                mNavigationModeController);
+                getContext().getMainThreadHandler(), mCommandQueue,
+                mModeSwitchesController, mSysUiState, mOverviewProxyService);
         mWindowMagnification.mAnimationControllerSupplier = new FakeAnimationControllerSupplier(
                 mContext.getSystemService(DisplayManager.class));
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
index 4e4c33a..045fb57f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -41,6 +41,7 @@
 
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.model.SysUiState;
 
 import org.junit.After;
 import org.junit.Before;
@@ -83,6 +84,8 @@
     IRemoteMagnificationAnimationCallback mAnimationCallback;
     @Mock
     IRemoteMagnificationAnimationCallback mAnimationCallback2;
+    @Mock
+    SysUiState mSysUiState;
     private SpyWindowMagnificationController mController;
     private WindowMagnificationController mSpyController;
     private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
@@ -98,7 +101,7 @@
         mWaitIntermediateAnimationPeriod = ANIMATION_DURATION_MS / 2;
         mController = new SpyWindowMagnificationController(mContext, mHandler,
                 mSfVsyncFrameProvider, null, new SurfaceControl.Transaction(),
-                mWindowMagnifierCallback);
+                mWindowMagnifierCallback, mSysUiState);
         mSpyController = mController.getSpyController();
         mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
                 mContext, mController, newValueAnimator());
@@ -394,6 +397,13 @@
         verify(mSpyController).onConfigurationChanged(100);
     }
 
+    @Test
+    public void updateSysUiStateFlag_passThrough() {
+        mWindowMagnificationAnimationController.updateSysUiStateFlag();
+
+        verify(mSpyController).updateSysUIStateFlag();
+    }
+
     private void verifyFinalSpec(float expectedScale, float expectedCenterX,
             float expectedCenterY) {
         assertEquals(expectedScale, mController.getScale(), 0f);
@@ -440,9 +450,9 @@
         SpyWindowMagnificationController(Context context, Handler handler,
                 SfVsyncFrameCallbackProvider sfVsyncFrameProvider,
                 MirrorWindowControl mirrorWindowControl, SurfaceControl.Transaction transaction,
-                WindowMagnifierCallback callback) {
+                WindowMagnifierCallback callback, SysUiState sysUiState) {
             super(context, handler, sfVsyncFrameProvider, mirrorWindowControl, transaction,
-                    callback);
+                    callback, sysUiState);
             mSpyController = Mockito.mock(WindowMagnificationController.class);
         }
 
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 7d617db..b8734df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -17,12 +17,15 @@
 package com.android.systemui.accessibility;
 
 import static android.view.Choreographer.FrameCallback;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+import static android.view.WindowInsets.Type.systemGestures;
 import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
+
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.hasItems;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
@@ -33,6 +36,9 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -40,49 +46,60 @@
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.res.Resources;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.graphics.Region;
 import android.os.Handler;
 import android.os.SystemClock;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableResources;
+import android.text.TextUtils;
 import android.view.Display;
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowInsets;
 import android.view.WindowManager;
+import android.view.WindowMetrics;
 import android.view.accessibility.AccessibilityNodeInfo;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
+import androidx.test.filters.LargeTest;
 
 import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.util.leak.ReferenceTestUtils;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Answers;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
-@SmallTest
+@LargeTest
 @RunWith(AndroidTestingRunner.class)
 public class WindowMagnificationControllerTest extends SysuiTestCase {
 
+    private static final int LAYOUT_CHANGE_TIMEOUT_MS = 5000;
     @Mock
-    Handler mHandler;
+    private Handler mHandler;
     @Mock
-    SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
+    private SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
     @Mock
-    MirrorWindowControl mMirrorWindowControl;
+    private MirrorWindowControl mMirrorWindowControl;
     @Mock
-    WindowMagnifierCallback mWindowMagnifierCallback;
-    @Mock
-    SurfaceControl.Transaction mTransaction;
-    @Mock
+    private WindowMagnifierCallback mWindowMagnifierCallback;
+    @Mock (answer = Answers.RETURNS_DEEP_STUBS)
+    private SurfaceControl.Transaction mTransaction;
     private WindowManager mWindowManager;
+    private SysUiState mSysUiState = new SysUiState();
     private Resources mResources;
     private WindowMagnificationController mWindowMagnificationController;
     private Instrumentation mInstrumentation;
@@ -93,37 +110,30 @@
         MockitoAnnotations.initMocks(this);
         mContext = Mockito.spy(getContext());
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
-        WindowManager wm = mContext.getSystemService(WindowManager.class);
-        doAnswer(invocation ->
-                wm.getMaximumWindowMetrics()
-        ).when(mWindowManager).getMaximumWindowMetrics();
-        doAnswer(invocation ->
-                wm.getCurrentWindowMetrics()
-        ).when(mWindowManager).getCurrentWindowMetrics();
+        final WindowManager wm = mContext.getSystemService(WindowManager.class);
+        mWindowManager = spy(new TestableWindowManager(wm));
+
         mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
         doAnswer(invocation -> {
-            mMirrorView = invocation.getArgument(0);
-            WindowManager.LayoutParams lp = invocation.getArgument(1);
-            mMirrorView.setLayoutParams(lp);
-            return null;
-        }).when(mWindowManager).addView(any(View.class), any(WindowManager.LayoutParams.class));
-        doAnswer(invocation -> {
-            mMirrorView = null;
-            return null;
-        }).when(mWindowManager).removeView(any(View.class));
-        doAnswer(invocation -> {
             FrameCallback callback = invocation.getArgument(0);
             callback.doFrame(0);
             return null;
         }).when(mSfVsyncFrameProvider).postFrameCallback(
                 any(FrameCallback.class));
-        when(mTransaction.remove(any())).thenReturn(mTransaction);
-        when(mTransaction.setGeometry(any(), any(), any(),
-                anyInt())).thenReturn(mTransaction);
+        doAnswer(invocation -> {
+            final Runnable runnable = invocation.getArgument(0);
+            runnable.run();
+            return null;
+        }).when(mHandler).post(
+                any(Runnable.class));
+
+        mSysUiState.addCallback(Mockito.mock(SysUiState.SysUiStateCallback.class));
+
         mResources = getContext().getOrCreateTestableResources().getResources();
         mWindowMagnificationController = new WindowMagnificationController(mContext,
                 mHandler, mSfVsyncFrameProvider,
-                mMirrorWindowControl, mTransaction, mWindowMagnifierCallback);
+                mMirrorWindowControl, mTransaction, mWindowMagnifierCallback, mSysUiState);
+
         verify(mMirrorWindowControl).setWindowDelegate(
                 any(MirrorWindowControl.MirrorWindowDelegate.class));
     }
@@ -135,12 +145,21 @@
     }
 
     @Test
-    public void enableWindowMagnification_showControl() {
+    public void enableWindowMagnification_showControlAndNotifyBoundsChanged() {
         mInstrumentation.runOnMainSync(() -> {
             mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
                     Float.NaN);
         });
+
         verify(mMirrorWindowControl).showControl();
+        ArgumentCaptor<Rect> boundsCaptor = ArgumentCaptor.forClass(Rect.class);
+        verify(mWindowMagnifierCallback,
+                timeout(LAYOUT_CHANGE_TIMEOUT_MS)).onWindowMagnifierBoundsChanged(
+                eq(mContext.getDisplayId()), boundsCaptor.capture());
+        final Rect actualBounds = new Rect();
+        mMirrorView.getBoundsOnScreen(actualBounds);
+        assertEquals(actualBounds, boundsCaptor.getValue());
+
     }
 
     @Test
@@ -158,6 +177,25 @@
     }
 
     @Test
+    public void deleteWindowMagnification_enableAtTheBottom_overlapFlagIsFalse() {
+        final WindowManager wm = mContext.getSystemService(WindowManager.class);
+        final Rect bounds = wm.getCurrentWindowMetrics().getBounds();
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+                    bounds.bottom);
+        });
+        ReferenceTestUtils.waitForCondition(this::hasMagnificationOverlapFlag);
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.deleteWindowMagnification();
+        });
+
+        verify(mMirrorWindowControl).destroyControl();
+        assertFalse(hasMagnificationOverlapFlag());
+    }
+
+    @Test
     public void moveMagnifier_schedulesFrame() {
         mInstrumentation.runOnMainSync(() -> {
             mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
@@ -210,7 +248,8 @@
         });
 
         assertEquals(Surface.ROTATION_90, mWindowMagnificationController.mRotation);
-        verify(mWindowManager).updateViewLayout(any(), any());
+        // The first invocation is called when the surface is created.
+        verify(mWindowManager, times(2)).updateViewLayout(any(), any());
     }
 
     @Test
@@ -318,17 +357,6 @@
     }
 
     @Test
-    public void onNavigationModeChanged_updateMirrorViewLayout() {
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
-                    Float.NaN);
-            mWindowMagnificationController.onNavigationModeChanged(NAV_BAR_MODE_GESTURAL);
-        });
-
-        verify(mWindowManager).updateViewLayout(eq(mMirrorView), any());
-    }
-
-    @Test
     public void enableWindowMagnification_hasA11yWindowTitle() {
         mInstrumentation.runOnMainSync(() -> {
             mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
@@ -353,16 +381,12 @@
         final TestableResources testableResources = getContext().getOrCreateTestableResources();
         testableResources.addOverride(com.android.internal.R.string.android_system_label,
                 newA11yWindowTitle);
-        when(mContext.getResources()).thenReturn(testableResources.getResources());
 
         mInstrumentation.runOnMainSync(() -> {
             mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_LOCALE);
         });
 
-        ArgumentCaptor<WindowManager.LayoutParams> paramsArgumentCaptor = ArgumentCaptor.forClass(
-                WindowManager.LayoutParams.class);
-        verify(mWindowManager).updateViewLayout(eq(mMirrorView), paramsArgumentCaptor.capture());
-        assertEquals(newA11yWindowTitle, paramsArgumentCaptor.getValue().accessibilityTitle);
+        assertTrue(TextUtils.equals(newA11yWindowTitle, getAccessibilityWindowTitle()));
     }
 
     @Test
@@ -386,4 +410,95 @@
         }
         fail("mMirrorView scale is not changed");
     }
+
+    @Test
+    public void moveWindowMagnificationToTheBottom_enabled_overlapFlagIsTrue() {
+        final WindowManager wm = mContext.getSystemService(WindowManager.class);
+        final Rect bounds = wm.getCurrentWindowMetrics().getBounds();
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+                    Float.NaN);
+        });
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.moveWindowMagnifier(0, bounds.height());
+        });
+
+        ReferenceTestUtils.waitForCondition(() -> hasMagnificationOverlapFlag());
+    }
+
+    private CharSequence getAccessibilityWindowTitle() {
+        if (mMirrorView == null) {
+            return  null;
+        }
+        WindowManager.LayoutParams layoutParams =
+                (WindowManager.LayoutParams) mMirrorView.getLayoutParams();
+        return layoutParams.accessibilityTitle;
+    }
+
+    private boolean hasMagnificationOverlapFlag() {
+        return (mSysUiState.getFlags() & SYSUI_STATE_MAGNIFICATION_OVERLAP) != 0;
+    }
+
+    private class TestableWindowManager implements WindowManager {
+
+        private final WindowManager mWindowManager;
+
+        TestableWindowManager(WindowManager windowManager) {
+            mWindowManager = windowManager;
+        }
+
+        @Override
+        public Display getDefaultDisplay() {
+            return mWindowManager.getDefaultDisplay();
+        }
+
+        @Override
+        public void removeViewImmediate(View view) {
+            mWindowManager.removeViewImmediate(view);
+        }
+
+        @Override
+        public void requestAppKeyboardShortcuts(KeyboardShortcutsReceiver receiver, int deviceId) {
+            mWindowManager.requestAppKeyboardShortcuts(receiver, deviceId);
+        }
+
+        @Override
+        public Region getCurrentImeTouchRegion() {
+            return mWindowManager.getCurrentImeTouchRegion();
+        }
+
+        @Override
+        public void addView(View view, ViewGroup.LayoutParams params) {
+            mMirrorView = view;
+            mWindowManager.addView(view, params);
+        }
+
+        @Override
+        public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
+            mWindowManager.updateViewLayout(view, params);
+        }
+
+        @Override
+        public void removeView(View view) {
+            mMirrorView = null;
+            mWindowManager.removeView(view);
+        }
+
+        @Override
+        public WindowMetrics getCurrentWindowMetrics() {
+            final Insets systemGesturesInsets = Insets.of(0, 0, 0, 10);
+            final WindowInsets insets = new WindowInsets.Builder()
+                    .setInsets(systemGestures(), systemGesturesInsets)
+                    .build();
+            final WindowMetrics windowMetrics = new WindowMetrics(
+                    mWindowManager.getCurrentWindowMetrics().getBounds(), insets);
+            return windowMetrics;
+        }
+
+        @Override
+        public WindowMetrics getMaximumWindowMetrics() {
+            return mWindowManager.getMaximumWindowMetrics();
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
index 7833114..6ef7cc3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
@@ -16,15 +16,21 @@
 
 package com.android.systemui.accessibility;
 
+import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
+
 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.doAnswer;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
 import android.os.RemoteException;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -36,12 +42,14 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.statusbar.CommandQueue;
 
 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;
 
@@ -50,17 +58,21 @@
 @TestableLooper.RunWithLooper
 public class WindowMagnificationTest extends SysuiTestCase {
 
+    private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
     @Mock
     private AccessibilityManager mAccessibilityManager;
     @Mock
     private ModeSwitchesController mModeSwitchesController;
     @Mock
-    private NavigationModeController mNavigationModeController;
+    private SysUiState mSysUiState;
     @Mock
     private IWindowMagnificationConnectionCallback mConnectionCallback;
+    @Mock
+    private OverviewProxyService mOverviewProxyService;
+
     private CommandQueue mCommandQueue;
     private WindowMagnification mWindowMagnification;
-
+    private OverviewProxyListener mOverviewProxyListener;
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -72,11 +84,18 @@
         }).when(mAccessibilityManager).setWindowMagnificationConnection(
                 any(IWindowMagnificationConnection.class));
 
+        when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState);
+
         mCommandQueue = new CommandQueue(getContext());
         mWindowMagnification = new WindowMagnification(getContext(),
                 getContext().getMainThreadHandler(), mCommandQueue, mModeSwitchesController,
-                mNavigationModeController);
+                mSysUiState, mOverviewProxyService);
         mWindowMagnification.start();
+
+        final ArgumentCaptor<OverviewProxyListener> listenerArgumentCaptor =
+                ArgumentCaptor.forClass(OverviewProxyListener.class);
+        verify(mOverviewProxyService).addCallback(listenerArgumentCaptor.capture());
+        mOverviewProxyListener = listenerArgumentCaptor.getValue();
     }
 
     @Test
@@ -99,10 +118,9 @@
         mCommandQueue.requestWindowMagnificationConnection(true);
         waitForIdleSync();
 
-        mWindowMagnification.onWindowMagnifierBoundsChanged(Display.DEFAULT_DISPLAY, testBounds);
+        mWindowMagnification.onWindowMagnifierBoundsChanged(TEST_DISPLAY, testBounds);
 
-        verify(mConnectionCallback).onWindowMagnifierBoundsChanged(Display.DEFAULT_DISPLAY,
-                testBounds);
+        verify(mConnectionCallback).onWindowMagnifierBoundsChanged(TEST_DISPLAY, testBounds);
     }
 
     @Test
@@ -111,10 +129,9 @@
         mCommandQueue.requestWindowMagnificationConnection(true);
         waitForIdleSync();
 
-        mWindowMagnification.onPerformScaleAction(Display.DEFAULT_DISPLAY, newScale);
+        mWindowMagnification.onPerformScaleAction(TEST_DISPLAY, newScale);
 
-        verify(mConnectionCallback).onPerformScaleAction(eq(Display.DEFAULT_DISPLAY),
-                eq(newScale));
+        verify(mConnectionCallback).onPerformScaleAction(TEST_DISPLAY, newScale);
     }
 
     @Test
@@ -122,9 +139,9 @@
         mCommandQueue.requestWindowMagnificationConnection(true);
         waitForIdleSync();
 
-        mWindowMagnification.onAccessibilityActionPerformed(Display.DEFAULT_DISPLAY);
+        mWindowMagnification.onAccessibilityActionPerformed(TEST_DISPLAY);
 
-        verify(mConnectionCallback).onAccessibilityActionPerformed(eq(Display.DEFAULT_DISPLAY));
+        verify(mConnectionCallback).onAccessibilityActionPerformed(TEST_DISPLAY);
     }
 
     @Test
@@ -135,4 +152,42 @@
 
         verify(mModeSwitchesController).onConfigurationChanged(anyInt());
     }
+
+    @Test
+    public void overviewProxyIsConnected_noController_resetFlag() {
+        mOverviewProxyListener.onConnectionChanged(true);
+
+        verify(mSysUiState).setFlag(SYSUI_STATE_MAGNIFICATION_OVERLAP, false);
+        verify(mSysUiState).commitUpdate(mContext.getDisplayId());
+    }
+
+    @Test
+    public void overviewProxyIsConnected_controllerIsAvailable_updateSysUiStateFlag() {
+        final WindowMagnificationAnimationController mController = mock(
+                WindowMagnificationAnimationController.class);
+        mWindowMagnification.mAnimationControllerSupplier = new FakeAnimationControllerSupplier(
+                mContext.getSystemService(DisplayManager.class), mController);
+        mWindowMagnification.mAnimationControllerSupplier.get(TEST_DISPLAY);
+
+        mOverviewProxyListener.onConnectionChanged(true);
+
+        verify(mController).updateSysUiStateFlag();
+    }
+
+    private static class FakeAnimationControllerSupplier extends
+            DisplayIdIndexSupplier<WindowMagnificationAnimationController> {
+
+        private final WindowMagnificationAnimationController mController;
+
+        FakeAnimationControllerSupplier(DisplayManager displayManager,
+                WindowMagnificationAnimationController controller) {
+            super(displayManager);
+            mController = controller;
+        }
+
+        @Override
+        protected WindowMagnificationAnimationController createInstance(Display display) {
+            return mController;
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java
new file mode 100644
index 0000000..46c930f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AnnotationLinkSpanTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.accessibility.floatingmenu;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+
+import android.testing.AndroidTestingRunner;
+import android.text.SpannableStringBuilder;
+import android.view.View;
+
+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;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/** Tests for {@link AnnotationLinkSpan}. */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class AnnotationLinkSpanTest extends SysuiTestCase {
+
+    private AnnotationLinkSpan.LinkInfo mLinkInfo;
+
+    @Before
+    public void setUp() {
+        mLinkInfo = new AnnotationLinkSpan.LinkInfo(
+                AnnotationLinkSpan.LinkInfo.DEFAULT_ANNOTATION,
+                mock(View.OnClickListener.class));
+    }
+
+    @Test
+    public void linkifyText_textAttachedWithSpan() {
+        final CharSequence text = getContext().getText(
+                R.string.accessibility_floating_button_migration_tooltip);
+        final SpannableStringBuilder builder =
+                (SpannableStringBuilder) AnnotationLinkSpan.linkify(text, mLinkInfo);
+        final int AnnotationLinkSpanNum =
+                builder.getSpans(/* queryStart= */ 0, builder.length(),
+                        AnnotationLinkSpan.class).length;
+
+        assertThat(AnnotationLinkSpanNum).isEqualTo(1);
+    }
+
+    @Test
+    public void linkifyText_withoutAnnotationTag_textWithoutSpan() {
+        final CharSequence text = "text without any annotation tag";
+        final SpannableStringBuilder builder =
+                (SpannableStringBuilder) AnnotationLinkSpan.linkify(text, mLinkInfo);
+        final int AnnotationLinkSpanNum =
+                builder.getSpans(/* queryStart= */ 0, builder.length(),
+                        AnnotationLinkSpan.class).length;
+
+        assertThat(AnnotationLinkSpanNum).isEqualTo(0);
+    }
+
+    @Test
+    public void linkifyText_twoLinkInfoWithSameAnnotation_listenerInvoked() {
+        final AtomicBoolean isClicked = new AtomicBoolean(false);
+        final CharSequence text = getContext().getText(
+                R.string.accessibility_floating_button_migration_tooltip);
+        final View.OnClickListener firstListener = v -> isClicked.set(true);
+        final AnnotationLinkSpan.LinkInfo firstLinkInfo = new AnnotationLinkSpan.LinkInfo(
+                AnnotationLinkSpan.LinkInfo.DEFAULT_ANNOTATION, firstListener);
+
+        final SpannableStringBuilder builder =
+                (SpannableStringBuilder) AnnotationLinkSpan.linkify(text, firstLinkInfo, mLinkInfo);
+        final AnnotationLinkSpan[] firstAnnotationLinkSpan =
+                builder.getSpans(/* queryStart= */ 0, builder.length(),
+                        AnnotationLinkSpan.class);
+        firstAnnotationLinkSpan[0].onClick(mock(View.class));
+
+        assertThat(isClicked.get()).isTrue();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipViewTest.java
new file mode 100644
index 0000000..6db5761
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/BaseTooltipViewTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.accessibility.floatingmenu;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.accessibility.MotionEventHelper;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Tests for {@link BaseTooltipView}. */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class BaseTooltipViewTest extends SysuiTestCase {
+
+    @Mock
+    private WindowManager mWindowManager;
+
+    private AccessibilityFloatingMenuView mMenuView;
+    private BaseTooltipView mToolTipView;
+
+    private final MotionEventHelper mMotionEventHelper = new MotionEventHelper();
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        final WindowManager wm = mContext.getSystemService(WindowManager.class);
+        doAnswer(invocation -> wm.getMaximumWindowMetrics()).when(
+                mWindowManager).getMaximumWindowMetrics();
+        mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
+
+        mMenuView = new AccessibilityFloatingMenuView(mContext);
+        mToolTipView = new BaseTooltipView(mContext, mMenuView);
+    }
+
+    @Test
+    public void showToolTipView_success() {
+        mToolTipView.show();
+
+        verify(mWindowManager).addView(eq(mToolTipView), any(WindowManager.LayoutParams.class));
+    }
+
+    @Test
+    public void touchOutsideWhenToolTipViewShown_dismiss() {
+        final MotionEvent outsideEvent =
+                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0,
+                        /* eventTime= */1,
+                        MotionEvent.ACTION_OUTSIDE,
+                        /* x= */ 0,
+                        /* y= */ 0);
+
+        mToolTipView.show();
+        mToolTipView.dispatchTouchEvent(outsideEvent);
+
+        verify(mWindowManager).removeView(mToolTipView);
+    }
+
+    @Test
+    public void getAccessibilityActionList_matchResult() {
+        final AccessibilityNodeInfo infos = new AccessibilityNodeInfo();
+        mToolTipView.onInitializeAccessibilityNodeInfo(infos);
+
+        assertThat(infos.getActionList().size()).isEqualTo(1);
+    }
+
+    @Test
+    public void accessibilityAction_dismiss_success() {
+        final BaseTooltipView tooltipView =
+                spy(new BaseTooltipView(mContext, mMenuView));
+
+        final boolean isActionPerformed =
+                tooltipView.performAccessibilityAction(
+                        AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS.getId(),
+                        /* arguments= */ null);
+
+        assertThat(isActionPerformed).isTrue();
+        verify(tooltipView).hide();
+    }
+
+    @After
+    public void tearDown() {
+        mToolTipView.hide();
+        mMotionEventHelper.recycleEvents();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DockTooltipViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DockTooltipViewTest.java
new file mode 100644
index 0000000..41b948f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DockTooltipViewTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.accessibility.floatingmenu;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.accessibility.MotionEventHelper;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Tests for {@link DockTooltipView}. */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class DockTooltipViewTest extends SysuiTestCase {
+
+    @Mock
+    private WindowManager mWindowManager;
+
+    private AccessibilityFloatingMenuView mMenuView;
+    private DockTooltipView mDockTooltipView;
+    private final MotionEventHelper mMotionEventHelper = new MotionEventHelper();
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        final WindowManager wm = mContext.getSystemService(WindowManager.class);
+        doAnswer(invocation -> wm.getMaximumWindowMetrics()).when(
+                mWindowManager).getMaximumWindowMetrics();
+        mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
+
+        mMenuView = spy(new AccessibilityFloatingMenuView(mContext));
+        mDockTooltipView = new DockTooltipView(mContext, mMenuView);
+    }
+
+    @Test
+    public void showTooltip_success() {
+        mDockTooltipView.show();
+
+        verify(mMenuView).startTranslateXAnimation();
+        verify(mWindowManager).addView(eq(mDockTooltipView), any(WindowManager.LayoutParams.class));
+    }
+
+    @Test
+    public void hideTooltip_success() {
+        mDockTooltipView.show();
+        mDockTooltipView.hide();
+
+        verify(mMenuView).stopTranslateXAnimation();
+        verify(mWindowManager).removeView(mDockTooltipView);
+    }
+
+    @Test
+    public void touchOutsideWhenToolTipViewShown_stopAnimation() {
+        final MotionEvent outsideEvent =
+                mMotionEventHelper.obtainMotionEvent(/* downTime= */ 0,
+                        /* eventTime= */ 1,
+                        MotionEvent.ACTION_OUTSIDE,
+                        /* x= */ 0,
+                        /* y= */ 0);
+
+        mDockTooltipView.show();
+        mDockTooltipView.dispatchTouchEvent(outsideEvent);
+
+        verify(mMenuView).stopTranslateXAnimation();
+    }
+
+    @After
+    public void tearDown() {
+        mMotionEventHelper.recycleEvents();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipViewTest.java
new file mode 100644
index 0000000..c5bd2fe
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MigrationTooltipViewTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.accessibility.floatingmenu;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.text.SpannableString;
+import android.text.method.LinkMovementMethod;
+import android.widget.TextView;
+
+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;
+
+/** Tests for {@link MigrationTooltipView}. */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class MigrationTooltipViewTest extends SysuiTestCase {
+
+    private TextView mTextView;
+
+    @Before
+    public void setUp() {
+        final AccessibilityFloatingMenuView menuView = new AccessibilityFloatingMenuView(mContext);
+        final MigrationTooltipView toolTipView = new MigrationTooltipView(mContext, menuView);
+        mTextView = toolTipView.findViewById(R.id.text);
+    }
+
+    @Test
+    public void onCreate_setLinkMovementMethod() {
+        assertThat(mTextView.getMovementMethod()).isInstanceOf(LinkMovementMethod.class);
+    }
+
+    @Test
+    public void onCreate_setDescription_matchTextAndSpanNum() {
+        final CharSequence expectedTextWithoutSpan =
+                AnnotationLinkSpan.linkify(mContext.getText(
+                R.string.accessibility_floating_button_migration_tooltip)).toString();
+        final SpannableString spannableString = (SpannableString) mTextView.getText();
+        final int AnnotationLinkSpanNum =
+                spannableString.getSpans(/* queryStart= */ 0, spannableString.length(),
+                        AnnotationLinkSpan.class).length;
+
+        assertThat(AnnotationLinkSpanNum).isEqualTo(1);
+        assertThat(mTextView.getText().toString().contentEquals(expectedTextWithoutSpan)).isTrue();
+    }
+}
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 2530cfd..46c1848 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -70,6 +70,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -94,6 +95,8 @@
     @Mock
     private WindowManager mWindowManager;
     @Mock
+    private UdfpsHbmCallback mHbmCallback;
+    @Mock
     private StatusBarStateController mStatusBarStateController;
     @Mock
     private StatusBar mStatusBar;
@@ -174,7 +177,8 @@
                 mPowerManager,
                 mAccessibilityManager,
                 mScreenLifecycle,
-                mVibrator);
+                mVibrator,
+                Optional.of(mHbmCallback));
         verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
         mOverlayController = mOverlayCaptor.getValue();
         verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
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 80716f9..003368d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -39,6 +39,7 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.IWindowManager;
+import android.view.View;
 import android.view.WindowManagerPolicyConstants;
 
 import androidx.test.filters.SmallTest;
@@ -172,6 +173,44 @@
     }
 
     @Test
+    public void testShouldLogClose_backButton() {
+        mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
+        doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
+        doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
+        doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
+        String[] actions = {
+                GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
+                GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
+                GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
+                GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
+        };
+        doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
+        GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog();
+        dialog.onBackPressed();
+        mTestableLooper.processAllMessages();
+        verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_CLOSE_BACK);
+    }
+
+    @Test
+    public void testShouldLogOnTapOutside() {
+        mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
+        doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
+        doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
+        doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
+        String[] actions = {
+                GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
+                GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
+                GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
+                GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
+        };
+        doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
+        GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog();
+        View container = dialog.findViewById(com.android.systemui.R.id.global_actions_container);
+        container.callOnClick();
+        verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
+    }
+
+    @Test
     public void testShouldLogBugreportPress() throws InterruptedException {
         GlobalActionsDialog.BugReportAction bugReportAction =
                 mGlobalActionsDialogLite.makeBugReportActionForTesting();
@@ -286,4 +325,44 @@
         assertThat(mGlobalActionsDialogLite.mOverflowItems).isEmpty();
         assertThat(mGlobalActionsDialogLite.mPowerItems).isEmpty();
     }
+
+    @Test
+    public void testShouldLogLockdownPress() {
+        GlobalActionsDialogLite.LockDownAction lockDownAction =
+                mGlobalActionsDialogLite.new LockDownAction();
+        lockDownAction.onPress();
+        verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_LOCKDOWN_PRESS);
+    }
+
+    @Test
+    public void testShouldLogShutdownPress() {
+        GlobalActionsDialogLite.ShutDownAction shutDownAction =
+                mGlobalActionsDialogLite.new ShutDownAction();
+        shutDownAction.onPress();
+        verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_SHUTDOWN_PRESS);
+    }
+
+    @Test
+    public void testShouldLogShutdownLongPress() {
+        GlobalActionsDialogLite.ShutDownAction shutDownAction =
+                mGlobalActionsDialogLite.new ShutDownAction();
+        shutDownAction.onLongPress();
+        verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_SHUTDOWN_LONG_PRESS);
+    }
+
+    @Test
+    public void testShouldLogRebootPress() {
+        GlobalActionsDialogLite.RestartAction restartAction =
+                mGlobalActionsDialogLite.new RestartAction();
+        restartAction.onPress();
+        verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_REBOOT_PRESS);
+    }
+
+    @Test
+    public void testShouldLogRebootLongPress() {
+        GlobalActionsDialogLite.RestartAction restartAction =
+                mGlobalActionsDialogLite.new RestartAction();
+        restartAction.onLongPress();
+        verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_REBOOT_LONG_PRESS);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index dd3a192..e62fa91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -289,6 +289,7 @@
 
         captor.value.onLongClick(holder.player)
         verify(mediaViewController, never()).openGuts()
+        verify(mediaViewController).closeGuts(false)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
index 406f40c..a974421 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
 import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -73,11 +74,15 @@
     @Mock
     private lateinit var mediaCarouselController: MediaCarouselController
     @Mock
+    private lateinit var mediaCarouselScrollHandler: MediaCarouselScrollHandler
+    @Mock
     private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
     @Mock
     private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
     @Captor
     private lateinit var wakefullnessObserver: ArgumentCaptor<(WakefulnessLifecycle.Observer)>
+    @Captor
+    private lateinit var statusBarCallback: ArgumentCaptor<(StatusBarStateController.StateListener)>
     @JvmField
     @Rule
     val mockito = MockitoJUnit.rule()
@@ -96,10 +101,13 @@
                 wakefulnessLifecycle,
                 statusBarKeyguardViewManager)
         verify(wakefulnessLifecycle).addObserver(wakefullnessObserver.capture())
+        verify(statusBarStateController).addCallback(statusBarCallback.capture())
         setupHost(lockHost, MediaHierarchyManager.LOCATION_LOCKSCREEN)
         setupHost(qsHost, MediaHierarchyManager.LOCATION_QS)
         setupHost(qqsHost, MediaHierarchyManager.LOCATION_QQS)
         `when`(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
+        `when`(mediaCarouselController.mediaCarouselScrollHandler)
+                .thenReturn(mediaCarouselScrollHandler)
         // We'll use the viewmanager to verify a few calls below, let's reset this.
         clearInvocations(mediaCarouselController)
     }
@@ -153,4 +161,11 @@
 
         verify(mediaCarouselController).closeGuts()
     }
+
+    @Test
+    fun testCloseGutsWhenDoze() {
+        statusBarCallback.value.onDozingChanged(true)
+
+        verify(mediaCarouselController).closeGuts()
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
index 5c70a4ef2..7172307 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
@@ -17,6 +17,7 @@
 package com.android.systemui.people;
 
 import static android.app.Notification.CATEGORY_MISSED_CALL;
+import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY;
 import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY;
 import static android.app.people.ConversationStatus.ACTIVITY_GAME;
 import static android.app.people.ConversationStatus.ACTIVITY_NEW_STORY;
@@ -25,6 +26,7 @@
 import static android.app.people.PeopleSpaceTile.SHOW_CONTACTS;
 import static android.app.people.PeopleSpaceTile.SHOW_IMPORTANT_CONVERSATIONS;
 import static android.app.people.PeopleSpaceTile.SHOW_STARRED_CONTACTS;
+import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT;
 import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH;
 
 import static com.android.systemui.people.PeopleSpaceUtils.STARRED_CONTACT;
@@ -127,6 +129,9 @@
                     .build();
 
     @Mock
+    private Icon mIcon;
+
+    @Mock
     private Context mMockContext;
     @Mock
     private PackageManager mPackageManager;
@@ -169,7 +174,7 @@
 
         mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
                 getSizeInDp(R.dimen.required_width_for_large));
-        mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+        mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
                 getSizeInDp(R.dimen.required_height_for_large));
         RemoteViews largeView = getPeopleTileViewHelper(
                 PERSON_TILE_WITHOUT_NOTIFICATION, mOptions).getViews();
@@ -218,7 +223,7 @@
 
         mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
                 getSizeInDp(R.dimen.required_width_for_large));
-        mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+        mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
                 getSizeInDp(R.dimen.required_height_for_large));
         RemoteViews largeView = getPeopleTileViewHelper(
                 tileWithLastInteraction, mOptions).getViews();
@@ -278,7 +283,7 @@
 
         mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
                 getSizeInDp(R.dimen.required_width_for_large));
-        mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+        mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
                 getSizeInDp(R.dimen.required_height_for_large));
         RemoteViews largeView = getPeopleTileViewHelper(
                 tileWithAvailabilityAndNewStory, mOptions).getViews();
@@ -340,7 +345,7 @@
 
         mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
                 getSizeInDp(R.dimen.required_width_for_large));
-        mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+        mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
                 getSizeInDp(R.dimen.required_height_for_large));
         RemoteViews largeView = getPeopleTileViewHelper(
                 tileWithStatusTemplate, mOptions).getViews();
@@ -376,6 +381,8 @@
         assertEquals(name.getText(), NAME);
         assertEquals(View.GONE, result.findViewById(R.id.subtext).getVisibility());
         assertEquals(View.VISIBLE, result.findViewById(R.id.predefined_icon).getVisibility());
+        assertEquals(View.GONE, result.findViewById(R.id.scrim_layout).getVisibility());
+        assertEquals(View.GONE, result.findViewById(R.id.image).getVisibility());
         // Has availability.
         assertEquals(View.VISIBLE, result.findViewById(R.id.availability).getVisibility());
         // Has person icon.
@@ -403,7 +410,7 @@
 
         mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
                 getSizeInDp(R.dimen.required_width_for_large));
-        mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+        mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
                 getSizeInDp(R.dimen.required_height_for_large));
         RemoteViews largeView = getPeopleTileViewHelper(
                 tileWithStatusTemplate, mOptions).getViews();
@@ -413,6 +420,8 @@
         assertEquals(name.getText(), NAME);
         assertEquals(View.GONE, largeResult.findViewById(R.id.subtext).getVisibility());
         assertEquals(View.VISIBLE, largeResult.findViewById(R.id.predefined_icon).getVisibility());
+        assertEquals(View.GONE, largeResult.findViewById(R.id.scrim_layout).getVisibility());
+        assertEquals(View.GONE, largeResult.findViewById(R.id.image).getVisibility());
         // Has availability.
         assertEquals(View.VISIBLE, largeResult.findViewById(R.id.availability).getVisibility());
         // Has person icon.
@@ -426,6 +435,55 @@
     }
 
     @Test
+    public void testCreateRemoteViewsWithStatusTemplateWithImageOnMediumAndLarge() {
+        PeopleSpaceTile tileWithIconInStatusTemplate =
+                PERSON_TILE_WITHOUT_NOTIFICATION.toBuilder().setStatuses(
+                        Arrays.asList(new ConversationStatus.Builder(PERSON_TILE.getId(),
+                                ACTIVITY_ANNIVERSARY).setDescription("Anniversary").setAvailability(
+                                AVAILABILITY_AVAILABLE).setIcon(mIcon).build())).build();
+        RemoteViews views = getPeopleTileViewHelper(
+                tileWithIconInStatusTemplate, mOptions).getViews();
+        View result = views.apply(mContext, null);
+
+        assertEquals(View.GONE, result.findViewById(R.id.subtext).getVisibility());
+        assertEquals(View.VISIBLE, result.findViewById(R.id.predefined_icon).getVisibility());
+        assertEquals(View.VISIBLE, result.findViewById(R.id.scrim_layout).getVisibility());
+        assertEquals(View.GONE, result.findViewById(R.id.image).getVisibility());
+        // Has availability.
+        assertEquals(View.VISIBLE, result.findViewById(R.id.availability).getVisibility());
+        // Has person icon.
+        assertEquals(View.VISIBLE, result.findViewById(R.id.person_icon).getVisibility());
+        // Has status.
+        TextView statusContent = (TextView) result.findViewById(R.id.name);
+        assertEquals(statusContent.getText(), "Anniversary");
+        assertThat(statusContent.getMaxLines()).isEqualTo(1);
+
+        mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+                getSizeInDp(R.dimen.required_width_for_large));
+        mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
+                getSizeInDp(R.dimen.required_height_for_large));
+        RemoteViews largeView = getPeopleTileViewHelper(
+                tileWithIconInStatusTemplate, mOptions).getViews();
+        View largeResult = largeView.apply(mContext, null);
+
+        assertEquals(View.GONE, largeResult.findViewById(R.id.subtext).getVisibility());
+        assertEquals(View.GONE, largeResult.findViewById(R.id.name).getVisibility());
+        assertEquals(View.VISIBLE, largeResult.findViewById(R.id.predefined_icon).getVisibility());
+        assertEquals(View.VISIBLE, largeResult.findViewById(R.id.scrim_layout).getVisibility());
+        assertEquals(View.GONE, largeResult.findViewById(R.id.image).getVisibility());
+        // Has availability.
+        assertEquals(View.VISIBLE, largeResult.findViewById(R.id.availability).getVisibility());
+        // Has person icon.
+        View personIcon = largeResult.findViewById(R.id.person_icon);
+        assertEquals(View.VISIBLE, personIcon.getVisibility());
+        // Has status content.
+        statusContent = (TextView) largeResult.findViewById(R.id.text_content);
+        assertEquals(View.VISIBLE, statusContent.getVisibility());
+        assertEquals(statusContent.getText(), "Anniversary");
+        assertThat(statusContent.getMaxLines()).isEqualTo(2);
+    }
+
+    @Test
     public void testCreateRemoteViewsWithPackageSuspended() {
         PeopleSpaceTile tile = PERSON_TILE.toBuilder()
                 .setIsPackageSuspended(true)
@@ -434,7 +492,7 @@
                 tile, mOptions).getViews();
         View result = views.apply(mContext, null);
 
-        assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+        assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
     }
 
     @Test
@@ -446,7 +504,7 @@
                 tile, mOptions).getViews();
         View result = views.apply(mContext, null);
 
-        assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+        assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_work_profile_quiet_layout);
     }
 
     @Test
@@ -458,7 +516,7 @@
                 tileWithDndBlocking, mOptions).getViews();
         View result = views.apply(mContext, null);
 
-        assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+        assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
 
         tileWithDndBlocking = PERSON_TILE.toBuilder()
                 .setNotificationPolicyState(BLOCK_CONVERSATIONS)
@@ -468,7 +526,7 @@
                 tileWithDndBlocking, mOptions).getViews();
         result = views.apply(mContext, null);
 
-        assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+        assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
 
         tileWithDndBlocking = PERSON_TILE.toBuilder()
                 .setNotificationPolicyState(SHOW_IMPORTANT_CONVERSATIONS)
@@ -477,7 +535,7 @@
                 tileWithDndBlocking, mOptions).getViews();
         result = views.apply(mContext, null);
 
-        assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+        assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
 
         tileWithDndBlocking = PERSON_TILE.toBuilder()
                 .setNotificationPolicyState(SHOW_IMPORTANT_CONVERSATIONS)
@@ -487,7 +545,7 @@
                 tileWithDndBlocking, mOptions).getViews();
         result = views.apply(mContext, null);
 
-        assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+        assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
 
         tileWithDndBlocking = PERSON_TILE.toBuilder()
                 .setNotificationPolicyState(SHOW_STARRED_CONTACTS)
@@ -497,7 +555,7 @@
                 tileWithDndBlocking, mOptions).getViews();
         result = views.apply(mContext, null);
 
-        assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+        assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
 
         tileWithDndBlocking = PERSON_TILE.toBuilder()
                 .setNotificationPolicyState(SHOW_STARRED_CONTACTS)
@@ -507,7 +565,7 @@
                 tileWithDndBlocking, mOptions).getViews();
         result = views.apply(mContext, null);
 
-        assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+        assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
 
         tileWithDndBlocking = PERSON_TILE.toBuilder()
                 .setNotificationPolicyState(SHOW_CONTACTS)
@@ -517,7 +575,7 @@
                 tileWithDndBlocking, mOptions).getViews();
         result = views.apply(mContext, null);
 
-        assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+        assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
 
         tileWithDndBlocking = PERSON_TILE.toBuilder()
                 .setNotificationPolicyState(SHOW_CONTACTS)
@@ -527,7 +585,7 @@
                 tileWithDndBlocking, mOptions).getViews();
         result = views.apply(mContext, null);
 
-        assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+        assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
 
         tileWithDndBlocking = PERSON_TILE.toBuilder()
                 .setNotificationPolicyState(SHOW_CONTACTS)
@@ -536,7 +594,7 @@
                 tileWithDndBlocking, mOptions).getViews();
         result = views.apply(mContext, null);
 
-        assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_empty_layout);
+        assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
     }
 
     @Test
@@ -581,7 +639,7 @@
 
         mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
                 getSizeInDp(R.dimen.required_width_for_large));
-        mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+        mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
                 getSizeInDp(R.dimen.required_height_for_large));
         RemoteViews largeView = getPeopleTileViewHelper(
                 tileWithMissedCallNotification, mOptions).getViews();
@@ -617,6 +675,7 @@
         assertEquals(name.getText(), NAME);
         assertEquals(View.GONE, result.findViewById(R.id.subtext).getVisibility());
         assertEquals(View.GONE, result.findViewById(R.id.predefined_icon).getVisibility());
+        assertEquals(View.GONE, result.findViewById(R.id.scrim_layout).getVisibility());
         // Has availability.
         assertEquals(View.VISIBLE, result.findViewById(R.id.availability).getVisibility());
         // Has person icon.
@@ -649,7 +708,7 @@
 
         mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
                 getSizeInDp(R.dimen.required_width_for_large));
-        mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+        mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
                 getSizeInDp(R.dimen.required_height_for_large));
         RemoteViews largeView = getPeopleTileViewHelper(
                 tileWithStatusAndNotification, mOptions).getViews();
@@ -659,6 +718,7 @@
         assertEquals(name.getText(), NAME);
         assertEquals(View.GONE, largeResult.findViewById(R.id.subtext).getVisibility());
         assertEquals(View.GONE, largeResult.findViewById(R.id.predefined_icon).getVisibility());
+        assertEquals(View.GONE, largeResult.findViewById(R.id.scrim_layout).getVisibility());
         // Has availability.
         assertEquals(View.VISIBLE, largeResult.findViewById(R.id.availability).getVisibility());
         // Has person icon.
@@ -725,7 +785,7 @@
 
         mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
                 getSizeInDp(R.dimen.required_width_for_large));
-        mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+        mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
                 getSizeInDp(R.dimen.required_height_for_large));
         RemoteViews largeView = getPeopleTileViewHelper(
                 tileWithStatusAndNotification, mOptions).getViews();
@@ -802,7 +862,7 @@
 
         mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
                 getSizeInDp(R.dimen.required_width_for_large));
-        mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
+        mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
                 getSizeInDp(R.dimen.required_height_for_large));
         RemoteViews largeView = getPeopleTileViewHelper(
                 tileWithStatusAndNotification, mOptions).getViews();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
index 107ce28..d63c529 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
@@ -1041,7 +1041,6 @@
         Bundle newOptions = new Bundle();
         mManager.onAppWidgetOptionsChanged(SECOND_WIDGET_ID_WITH_SHORTCUT, newOptions);
 
-
         // Check that options is not modified
         verify(mAppWidgetManager, never()).updateAppWidgetOptions(
                 eq(SECOND_WIDGET_ID_WITH_SHORTCUT), any());
@@ -1478,6 +1477,8 @@
         widgetSp5.edit().clear().commit();
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
         sp.edit().clear().commit();
+        mManager.mListeners.clear();
+        mManager.mTiles.clear();
     }
 
     private void setStorageForTile(String shortcutId, String packageName, int widgetId,
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 d35597f..6f7bf3b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
@@ -21,6 +21,7 @@
 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;
 
@@ -35,6 +36,7 @@
 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;
@@ -82,6 +84,7 @@
     private QuickQSPanelController mQuickQSPanelController;
     private FakeTunerService mFakeTunerService;
     private MetricsLogger mMetricsLogger = new FakeMetricsLogger();
+    private FalsingManagerFake mFalsingManager;
 
     @Mock
     private SettingsButton mSettingsButton;
@@ -95,12 +98,15 @@
     private View mPowerMenuLiteView;
     @Mock
     private GlobalActionsDialogLite mGlobalActionsDialog;
+    @Mock
+    private UiEventLogger mUiEventLogger;
 
     private QSFooterViewController mController;
 
     @Before
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
+        mFalsingManager = new FalsingManagerFake();
 
         injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
 
@@ -121,7 +127,8 @@
         mController = new QSFooterViewController(mView, mUserManager, mUserInfoController,
                 mActivityStarter, mDeviceProvisionedController, mUserTracker, mQSPanelController,
                 mMultiUserSwitchController, mQuickQSPanelController, mFakeTunerService,
-                mMetricsLogger, new FalsingManagerFake(), false, mGlobalActionsDialog);
+                mMetricsLogger, mFalsingManager, false, mGlobalActionsDialog,
+                mUiEventLogger);
 
         mController.init();
     }
@@ -154,4 +161,27 @@
         // 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/tileimpl/QSTileViewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
index e5e2e53..126dca5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
@@ -20,6 +20,7 @@
 import android.graphics.drawable.Drawable
 import android.service.quicksettings.Tile
 import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
 import android.text.TextUtils
 import android.view.View
 import androidx.test.filters.SmallTest
@@ -36,6 +37,7 @@
 
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 class QSTileViewImplTest : SysuiTestCase() {
 
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index 7c8b413..88852f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -74,6 +74,7 @@
     private HeadsUpManager mHeadsUpManager;
     @Mock private NotificationEntryManager mNotificationEntryManager;
     @Mock private RowContentBindStage mBindStage;
+    @Mock PeopleNotificationIdentifier mPeopleNotificationIdentifier;
     @Captor private ArgumentCaptor<NotificationEntryListener> mListenerCaptor;
     private NotificationEntryListener mNotificationEntryListener;
     private final HashMap<String, NotificationEntry> mPendingEntries = new HashMap<>();
@@ -91,7 +92,7 @@
 
         mGroupManager = new NotificationGroupManagerLegacy(
                 mock(StatusBarStateController.class),
-                () -> mock(PeopleNotificationIdentifier.class),
+                () -> mPeopleNotificationIdentifier,
                 Optional.of(mock(Bubbles.class)));
         mDependency.injectTestDependency(NotificationGroupManagerLegacy.class, mGroupManager);
         mGroupManager.setHeadsUpManager(mHeadsUpManager);
@@ -107,15 +108,31 @@
         mHeadsUpManager.addListener(mGroupAlertTransferHelper);
     }
 
+    private void mockHasHeadsUpContentView(NotificationEntry entry,
+            boolean hasHeadsUpContentView) {
+        RowContentBindParams params = new RowContentBindParams();
+        if (hasHeadsUpContentView) {
+            params.requireContentViews(FLAG_CONTENT_VIEW_HEADS_UP);
+        }
+        when(mBindStage.getStageParams(eq(entry))).thenReturn(params);
+    }
+
+    private void mockHasHeadsUpContentView(NotificationEntry entry) {
+        mockHasHeadsUpContentView(entry, true);
+    }
+
+    private void mockIsPriority(NotificationEntry priorityEntry) {
+        when(mPeopleNotificationIdentifier.getPeopleNotificationType(eq(priorityEntry)))
+                .thenReturn(PeopleNotificationIdentifier.TYPE_IMPORTANT_PERSON);
+    }
+
     @Test
     public void testSuppressedSummaryHeadsUpTransfersToChild() {
         NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mHeadsUpManager.showNotification(summaryEntry);
         NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
 
-        RowContentBindParams params = new RowContentBindParams();
-        params.requireContentViews(FLAG_CONTENT_VIEW_HEADS_UP);
-        when(mBindStage.getStageParams(eq(childEntry))).thenReturn(params);
+        mockHasHeadsUpContentView(childEntry);
 
         // Summary will be suppressed because there is only one child.
         mGroupManager.onEntryAdded(summaryEntry);
@@ -180,8 +197,7 @@
         NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mHeadsUpManager.showNotification(summaryEntry);
         NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
-        RowContentBindParams params = new RowContentBindParams();
-        when(mBindStage.getStageParams(eq(childEntry))).thenReturn(params);
+        mockHasHeadsUpContentView(childEntry, false);
 
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
@@ -198,8 +214,7 @@
         NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
         mHeadsUpManager.showNotification(summaryEntry);
         NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
-        RowContentBindParams params = new RowContentBindParams();
-        when(mBindStage.getStageParams(eq(childEntry))).thenReturn(params);
+        mockHasHeadsUpContentView(childEntry, false);
 
         mGroupManager.onEntryAdded(summaryEntry);
         mGroupManager.onEntryAdded(childEntry);
@@ -250,8 +265,7 @@
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
         NotificationEntry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
-        RowContentBindParams params = new RowContentBindParams();
-        when(mBindStage.getStageParams(eq(childEntry))).thenReturn(params);
+        mockHasHeadsUpContentView(childEntry, false);
 
         mHeadsUpManager.showNotification(summaryEntry);
         // Trigger a transfer of alert state from summary to child.
@@ -270,8 +284,7 @@
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
         NotificationEntry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
-        RowContentBindParams params = new RowContentBindParams();
-        when(mBindStage.getStageParams(eq(childEntry))).thenReturn(params);
+        mockHasHeadsUpContentView(childEntry, false);
 
         mHeadsUpManager.showNotification(summaryEntry);
         // Trigger a transfer of alert state from summary to child.
@@ -294,8 +307,7 @@
                 mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
         NotificationEntry childEntry =
                 mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY, 47);
-        RowContentBindParams params = new RowContentBindParams();
-        when(mBindStage.getStageParams(eq(childEntry))).thenReturn(params);
+        mockHasHeadsUpContentView(childEntry, false);
 
         mHeadsUpManager.showNotification(summaryEntry);
         // Trigger a transfer of alert state from summary to child.
@@ -311,4 +323,160 @@
 
         assertFalse(mGroupAlertTransferHelper.isAlertTransferPending(childEntry));
     }
+
+    @Test
+    public void testOverriddenSummaryHeadsUpTransfersToPriority() {
+        // Creation order is oldest to newest, meaning the priority will be deemed newest
+        int groupAlert = Notification.GROUP_ALERT_SUMMARY;
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(groupAlert);
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification(groupAlert);
+        NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(groupAlert);
+        mockIsPriority(priorityEntry);
+
+        // summary gets heads up
+        mHeadsUpManager.showNotification(summaryEntry);
+
+        mockHasHeadsUpContentView(summaryEntry);
+        mockHasHeadsUpContentView(priorityEntry);
+        mockHasHeadsUpContentView(childEntry);
+
+        // Summary will have an alertOverride.
+        mGroupManager.onEntryAdded(summaryEntry);
+        mGroupManager.onEntryAdded(priorityEntry);
+        mGroupManager.onEntryAdded(childEntry);
+
+        // An overridden summary should transfer its alert state to the priority.
+        assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
+        assertFalse(mHeadsUpManager.isAlerting(childEntry.getKey()));
+        assertTrue(mHeadsUpManager.isAlerting(priorityEntry.getKey()));
+    }
+
+    @Test
+    public void testOverriddenSummaryHeadsUpTransferDoesNotAlertPriorityIfUninflated() {
+        // Creation order is oldest to newest, meaning the priority will be deemed newest
+        int groupAlert = Notification.GROUP_ALERT_SUMMARY;
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(groupAlert);
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification(groupAlert);
+        NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(groupAlert);
+        mockIsPriority(priorityEntry);
+
+        // summary gets heads up
+        mHeadsUpManager.showNotification(summaryEntry);
+
+        mockHasHeadsUpContentView(summaryEntry);
+        mockHasHeadsUpContentView(priorityEntry, false);
+        mockHasHeadsUpContentView(childEntry);
+
+        // Summary will have an alertOverride.
+        mGroupManager.onEntryAdded(summaryEntry);
+        mGroupManager.onEntryAdded(priorityEntry);
+        mGroupManager.onEntryAdded(childEntry);
+
+        // Alert is immediately removed from summary, but we do not show priority yet either as its
+        // content is not inflated.
+        assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
+        assertFalse(mHeadsUpManager.isAlerting(childEntry.getKey()));
+        assertFalse(mHeadsUpManager.isAlerting(priorityEntry.getKey()));
+        assertTrue(mGroupAlertTransferHelper.isAlertTransferPending(priorityEntry));
+    }
+
+    @Test
+    public void testOverriddenSummaryHeadsUpTransfersToPriorityButBackAgain() {
+        // Creation order is oldest to newest, meaning the child2 will ultimately be deemed newest
+        int groupAlert = Notification.GROUP_ALERT_SUMMARY;
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(groupAlert);
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification(groupAlert);
+        NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(groupAlert);
+        NotificationEntry childEntry2 = mGroupTestHelper.createChildNotification(groupAlert);
+        mockIsPriority(priorityEntry);
+
+        // summary gets heads up
+        mHeadsUpManager.showNotification(summaryEntry);
+
+        mockHasHeadsUpContentView(summaryEntry);
+        mockHasHeadsUpContentView(priorityEntry);
+        mockHasHeadsUpContentView(childEntry);
+        mockHasHeadsUpContentView(childEntry2);
+
+        // Summary will have an alertOverride.
+        mGroupManager.onEntryAdded(summaryEntry);
+        mGroupManager.onEntryAdded(priorityEntry);
+        mGroupManager.onEntryAdded(childEntry);
+
+        // An overridden summary should transfer its alert state to the priority.
+        assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
+        assertFalse(mHeadsUpManager.isAlerting(childEntry.getKey()));
+        assertTrue(mHeadsUpManager.isAlerting(priorityEntry.getKey()));
+
+        mGroupManager.onEntryAdded(childEntry2);
+
+        // An overridden summary should transfer its alert state to the priority.
+        assertTrue(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
+        assertFalse(mHeadsUpManager.isAlerting(childEntry.getKey()));
+        assertFalse(mHeadsUpManager.isAlerting(childEntry2.getKey()));
+        assertFalse(mHeadsUpManager.isAlerting(priorityEntry.getKey()));
+    }
+
+    @Test
+    public void testOverriddenSuppressedSummaryHeadsUpTransfersToChildThenToPriority() {
+        // Creation order is oldest to newest, meaning the priority will ultimately be deemed newest
+        int groupAlert = Notification.GROUP_ALERT_SUMMARY;
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(groupAlert);
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification(groupAlert);
+        NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(groupAlert);
+        mockIsPriority(priorityEntry);
+
+        // summary gets heads up
+        mHeadsUpManager.showNotification(summaryEntry);
+
+        mockHasHeadsUpContentView(summaryEntry);
+        mockHasHeadsUpContentView(priorityEntry);
+        mockHasHeadsUpContentView(childEntry);
+
+        // Summary will be suppressed, and the child will receive the alert
+        mGroupManager.onEntryAdded(summaryEntry);
+        mGroupManager.onEntryAdded(childEntry);
+
+        assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
+        assertTrue(mHeadsUpManager.isAlerting(childEntry.getKey()));
+
+        // Alert should be transferred "back" from the child to the priority
+        mGroupManager.onEntryAdded(priorityEntry);
+
+        assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
+        assertFalse(mHeadsUpManager.isAlerting(childEntry.getKey()));
+        assertTrue(mHeadsUpManager.isAlerting(priorityEntry.getKey()));
+    }
+
+    @Test
+    public void testOverriddenSuppressedSummaryHeadsUpTransfersToPriorityThenToChild() {
+        // Creation order is oldest to newest, meaning the child will ultimately be deemed newest
+        int groupAlert = Notification.GROUP_ALERT_SUMMARY;
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(groupAlert);
+        NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(groupAlert);
+        NotificationEntry childEntry = mGroupTestHelper.createChildNotification(groupAlert);
+        mockIsPriority(priorityEntry);
+
+        // summary gets heads up
+        mHeadsUpManager.showNotification(summaryEntry);
+
+        mockHasHeadsUpContentView(summaryEntry);
+        mockHasHeadsUpContentView(priorityEntry);
+        mockHasHeadsUpContentView(childEntry);
+
+        // Summary will have alert override of the priority
+        mGroupManager.onEntryAdded(summaryEntry);
+        mGroupManager.onEntryAdded(priorityEntry);
+
+        assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
+        assertTrue(mHeadsUpManager.isAlerting(priorityEntry.getKey()));
+
+        // Alert should be transferred "back" from the priority to the child (which is newer)
+        mGroupManager.onEntryAdded(childEntry);
+
+        assertFalse(mHeadsUpManager.isAlerting(summaryEntry.getKey()));
+        assertTrue(mHeadsUpManager.isAlerting(childEntry.getKey()));
+        assertFalse(mHeadsUpManager.isAlerting(priorityEntry.getKey()));
+    }
+
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
index 3e9fd51..0110d7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java
@@ -21,9 +21,12 @@
 import static junit.framework.Assert.assertTrue;
 
 import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.Notification;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
@@ -33,6 +36,8 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.NotificationGroup;
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.OnGroupChangeListener;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.wm.shell.bubbles.Bubbles;
@@ -58,6 +63,7 @@
     private final NotificationGroupTestHelper mGroupTestHelper =
             new NotificationGroupTestHelper(mContext);
 
+    @Mock PeopleNotificationIdentifier mPeopleNotificationIdentifier;
     @Mock HeadsUpManager mHeadsUpManager;
 
     @Before
@@ -69,7 +75,7 @@
     private void initializeGroupManager() {
         mGroupManager = new NotificationGroupManagerLegacy(
                 mock(StatusBarStateController.class),
-                () -> mock(PeopleNotificationIdentifier.class),
+                () -> mPeopleNotificationIdentifier,
                 Optional.of(mock(Bubbles.class)));
         mGroupManager.setHeadsUpManager(mHeadsUpManager);
     }
@@ -153,4 +159,72 @@
         assertEquals(childEntry, mGroupManager.getGroupSummary(childEntry));
         assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(childEntry));
     }
+
+    @Test
+    public void testAlertOverrideWithSiblings_0() {
+        helpTestAlertOverrideWithSiblings(0);
+    }
+
+    @Test
+    public void testAlertOverrideWithSiblings_1() {
+        helpTestAlertOverrideWithSiblings(1);
+    }
+
+    @Test
+    public void testAlertOverrideWithSiblings_2() {
+        helpTestAlertOverrideWithSiblings(2);
+    }
+
+    /**
+     * This tests, for a group with a priority entry and the given number of siblings, that:
+     * 1) the priority entry is identified as the alertOverride for the group
+     * 2) the onAlertOverrideChanged method is called at that time
+     * 3) when the priority entry is removed, these are reversed
+     */
+    private void helpTestAlertOverrideWithSiblings(int numSiblings) {
+        int groupAlert = Notification.GROUP_ALERT_SUMMARY;
+        // Create entries in an order so that the priority entry can be deemed the newest child.
+        NotificationEntry[] siblings = new NotificationEntry[numSiblings];
+        for (int i = 0; i < numSiblings; i++) {
+            siblings[i] = mGroupTestHelper.createChildNotification(groupAlert);
+        }
+        NotificationEntry priorityEntry = mGroupTestHelper.createChildNotification(groupAlert);
+        NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification(groupAlert);
+
+        // The priority entry is an important conversation.
+        when(mPeopleNotificationIdentifier.getPeopleNotificationType(eq(priorityEntry)))
+                .thenReturn(PeopleNotificationIdentifier.TYPE_IMPORTANT_PERSON);
+
+        // Register a listener so we can verify that the event is sent.
+        OnGroupChangeListener groupChangeListener = mock(OnGroupChangeListener.class);
+        mGroupManager.registerGroupChangeListener(groupChangeListener);
+
+        // Add all the entries.  The order here shouldn't matter.
+        mGroupManager.onEntryAdded(summaryEntry);
+        for (int i = 0; i < numSiblings; i++) {
+            mGroupManager.onEntryAdded(siblings[i]);
+        }
+        mGroupManager.onEntryAdded(priorityEntry);
+
+        // Verify that the summary group has the priority child as its alertOverride
+        NotificationGroup summaryGroup = mGroupManager.getGroupForSummary(summaryEntry.getSbn());
+        assertEquals(priorityEntry, summaryGroup.alertOverride);
+        verify(groupChangeListener).onGroupAlertOverrideChanged(summaryGroup, null, priorityEntry);
+
+        // Verify that only the priority notification is isolated from the group
+        assertEquals(priorityEntry, mGroupManager.getGroupSummary(priorityEntry));
+        assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(priorityEntry));
+        // Verify that the siblings are NOT isolated from the group
+        for (int i = 0; i < numSiblings; i++) {
+            assertEquals(summaryEntry, mGroupManager.getGroupSummary(siblings[i]));
+            assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(siblings[i]));
+        }
+
+        // Remove the priority notification to validate that it is removed as the alertOverride
+        mGroupManager.onEntryRemoved(priorityEntry);
+
+        // verify that the alertOverride is removed when the priority notification is
+        assertNull(summaryGroup.alertOverride);
+        verify(groupChangeListener).onGroupAlertOverrideChanged(summaryGroup, null, priorityEntry);
+    }
 }
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 4bac762..45761df 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
@@ -89,6 +89,7 @@
 import com.android.systemui.statusbar.StatusBarStateControllerImpl;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.events.PrivacyDotViewController;
 import com.android.systemui.statusbar.notification.ConversationNotificationManager;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -243,6 +244,8 @@
     private QuickAccessWalletClient mQuickAccessWalletClient;
     @Mock
     private KeyguardMediaController mKeyguardMediaController;
+    @Mock
+    private PrivacyDotViewController mPrivacyDotViewController;
 
     private SysuiStatusBarStateController mStatusBarStateController;
     private NotificationPanelViewController mNotificationPanelViewController;
@@ -351,6 +354,7 @@
                 mFeatureFlags,
                 mQuickAccessWalletClient,
                 mKeyguardMediaController,
+                mPrivacyDotViewController,
                 new FakeExecutor(new FakeSystemClock()));
         mNotificationPanelViewController.initDependencies(
                 mStatusBar,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index 3d07eb1..e5b7e3f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -20,7 +20,6 @@
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.fail;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.never;
@@ -100,8 +99,6 @@
 
     private Icon mActionIcon;
 
-    private int mSingleLinePaddingHorizontal;
-    private int mDoubleLinePaddingHorizontal;
     private int mSpacing;
 
     private NotificationEntry mEntry;
@@ -141,10 +138,6 @@
         mView = SmartReplyView.inflate(mContext, mConstants);
 
         final Resources res = mContext.getResources();
-        mSingleLinePaddingHorizontal = res.getDimensionPixelSize(
-                R.dimen.smart_reply_button_padding_horizontal_single_line);
-        mDoubleLinePaddingHorizontal = res.getDimensionPixelSize(
-                R.dimen.smart_reply_button_padding_horizontal_double_line);
         mSpacing = res.getDimensionPixelSize(R.dimen.smart_reply_button_spacing);
 
         mNotification = new Notification.Builder(mContext, "")
@@ -588,18 +581,6 @@
         layout.setBaselineAligned(false);
 
         final boolean isRtl = mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
-        final int paddingHorizontal;
-        switch (lineCount) {
-            case 1:
-                paddingHorizontal = mSingleLinePaddingHorizontal;
-                break;
-            case 2:
-                paddingHorizontal = mDoubleLinePaddingHorizontal;
-                break;
-            default:
-                fail("Invalid line count " + lineCount);
-                return null;
-        }
 
         // Add smart replies
         Button previous = null;
@@ -617,8 +598,6 @@
                                 true /* delayOnClickListener */))
                         .iterator()));
         for (Button current : inflatedReplies) {
-            current.setPadding(paddingHorizontal, current.getPaddingTop(), paddingHorizontal,
-                    current.getPaddingBottom());
             if (previous != null) {
                 ViewGroup.MarginLayoutParams lp =
                         (ViewGroup.MarginLayoutParams) previous.getLayoutParams();
@@ -647,8 +626,6 @@
 
         // Add smart actions
         for (Button current : inflatedSmartActions) {
-            current.setPadding(paddingHorizontal, current.getPaddingTop(), paddingHorizontal,
-                    current.getPaddingBottom());
             if (previous != null) {
                 ViewGroup.MarginLayoutParams lp =
                         (ViewGroup.MarginLayoutParams) previous.getLayoutParams();
diff --git a/packages/services/CameraExtensionsProxy/AndroidManifest.xml b/packages/services/CameraExtensionsProxy/AndroidManifest.xml
index 7416cba..e5f460e 100644
--- a/packages/services/CameraExtensionsProxy/AndroidManifest.xml
+++ b/packages/services/CameraExtensionsProxy/AndroidManifest.xml
@@ -2,8 +2,6 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.camera">
 
-    <uses-permission android:name="android.permission.CAMERA" />
-
     <application
         android:label="@string/app_name"
         android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/camera/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/camera/CameraExtensionsProxyService.java
index 0f05019..d44a417 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/camera/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/camera/CameraExtensionsProxyService.java
@@ -25,19 +25,36 @@
 import android.hardware.camera2.CameraExtensionCharacteristics;
 import android.hardware.camera2.CameraManager;
 import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.extension.CameraOutputConfig;
+import android.hardware.camera2.extension.CameraSessionConfig;
 import android.hardware.camera2.extension.CaptureBundle;
+import android.hardware.camera2.extension.CaptureFailure;
 import android.hardware.camera2.extension.CaptureStageImpl;
+import android.hardware.camera2.extension.IAdvancedExtenderImpl;
 import android.hardware.camera2.extension.ICameraExtensionsProxyService;
+import android.hardware.camera2.extension.ICaptureCallback;
 import android.hardware.camera2.extension.ICaptureProcessorImpl;
 import android.hardware.camera2.extension.IPreviewExtenderImpl;
 import android.hardware.camera2.extension.IPreviewImageProcessorImpl;
+import android.hardware.camera2.extension.IRequestCallback;
+import android.hardware.camera2.extension.IRequestProcessorImpl;
 import android.hardware.camera2.extension.IRequestUpdateProcessorImpl;
-import android.hardware.camera2.extension.ParcelImage;
 import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
+import android.hardware.camera2.extension.IImageProcessorImpl;
+import android.hardware.camera2.extension.ISessionProcessorImpl;
+import android.hardware.camera2.extension.LatencyRange;
+import android.hardware.camera2.extension.OutputConfigId;
+import android.hardware.camera2.extension.OutputSurface;
+import android.hardware.camera2.extension.ParcelCaptureResult;
+import android.hardware.camera2.extension.ParcelImage;
+import android.hardware.camera2.extension.ParcelTotalCaptureResult;
+import android.hardware.camera2.extension.Request;
 import android.hardware.camera2.extension.SizeList;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.TotalCaptureResult;
 import android.hardware.HardwareBuffer;
+import android.hardware.camera2.impl.PhysicalCaptureResultInfo;
 import android.media.Image;
 import android.media.ImageReader;
 import android.os.ConditionVariable;
@@ -45,9 +62,11 @@
 import android.os.HandlerExecutor;
 import android.os.HandlerThread;
 import android.os.IBinder;
+import android.os.RemoteException;
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.Pair;
+import android.util.Range;
 import android.util.Size;
 import android.view.Surface;
 
@@ -69,10 +88,26 @@
 import androidx.camera.extensions.impl.PreviewExtenderImpl.ProcessorType;
 import androidx.camera.extensions.impl.PreviewImageProcessorImpl;
 import androidx.camera.extensions.impl.RequestUpdateProcessorImpl;
+import androidx.camera.extensions.impl.advanced.AdvancedExtenderImpl;
+import androidx.camera.extensions.impl.advanced.AutoAdvancedExtenderImpl;
+import androidx.camera.extensions.impl.advanced.BeautyAdvancedExtenderImpl;
+import androidx.camera.extensions.impl.advanced.BokehAdvancedExtenderImpl;
+import androidx.camera.extensions.impl.advanced.Camera2OutputConfigImpl;
+import androidx.camera.extensions.impl.advanced.Camera2SessionConfigImpl;
+import androidx.camera.extensions.impl.advanced.HdrAdvancedExtenderImpl;
+import androidx.camera.extensions.impl.advanced.ImageProcessorImpl;
+import androidx.camera.extensions.impl.advanced.ImageReaderOutputConfigImpl;
+import androidx.camera.extensions.impl.advanced.MultiResolutionImageReaderOutputConfigImpl;
+import androidx.camera.extensions.impl.advanced.NightAdvancedExtenderImpl;
+import androidx.camera.extensions.impl.advanced.OutputSurfaceImpl;
+import androidx.camera.extensions.impl.advanced.RequestProcessorImpl;
+import androidx.camera.extensions.impl.advanced.SessionProcessorImpl;
+import androidx.camera.extensions.impl.advanced.SurfaceOutputConfigImpl;
 
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Map;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
@@ -83,13 +118,20 @@
 
     private static final String CAMERA_EXTENSION_VERSION_NAME =
             "androidx.camera.extensions.impl.ExtensionVersionImpl";
-    private static final String LATEST_VERSION = "1.1.0";
-    private static final String[] SUPPORTED_VERSION_PREFIXES = {"1.1.", "1.0."};
+    private static final String LATEST_VERSION = "1.2.0";
+    private static final String LEGACY_VERSION_PREFIX = "1.1";
+    private static final String ADVANCED_VERSION_PREFIX = "1.2";
+    private static final String[] SUPPORTED_VERSION_PREFIXES = {ADVANCED_VERSION_PREFIX,
+            LEGACY_VERSION_PREFIX, "1.0."};
     private static final boolean EXTENSIONS_PRESENT = checkForExtensions();
     private static final String EXTENSIONS_VERSION = EXTENSIONS_PRESENT ?
             (new ExtensionVersionImpl()).checkApiVersion(LATEST_VERSION) : null;
-    private static final boolean LATEST_VERSION_SUPPORTED =
-            EXTENSIONS_PRESENT && EXTENSIONS_VERSION.startsWith(SUPPORTED_VERSION_PREFIXES[0]);
+    private static final boolean LEGACY_VERSION_SUPPORTED =
+            EXTENSIONS_PRESENT && EXTENSIONS_VERSION.startsWith(LEGACY_VERSION_PREFIX);
+    private static final boolean ADVANCED_VERSION_SUPPORTED =
+            EXTENSIONS_PRESENT && EXTENSIONS_VERSION.startsWith(ADVANCED_VERSION_PREFIX);
+
+    private HashMap<String, CameraCharacteristics> mCharacteristicsHashMap = new HashMap<>();
 
     private static boolean checkForExtensions() {
         try {
@@ -223,7 +265,7 @@
 
         public long registerClient(Context ctx) {
             synchronized (mLock) {
-                if (LATEST_VERSION_SUPPORTED) {
+                if (LEGACY_VERSION_SUPPORTED) {
                     if (mActiveClients.isEmpty()) {
                         InitializerFuture status = new InitializerFuture();
                         InitializerImpl.init(EXTENSIONS_VERSION, ctx, new InitializeHandler(status),
@@ -257,7 +299,7 @@
         public void unregisterClient(long clientId) {
             synchronized (mLock) {
                 if (mActiveClients.remove(clientId) && mActiveClients.isEmpty() &&
-                        LATEST_VERSION_SUPPORTED) {
+                        LEGACY_VERSION_SUPPORTED) {
                     InitializerFuture status = new InitializerFuture();
                     InitializerImpl.deinit(new ReleaseHandler(status),
                             new HandlerExecutor(mHandler));
@@ -321,17 +363,39 @@
         }
     }
 
+    /**
+     * @hide
+     */
+    public static AdvancedExtenderImpl initializeAdvancedExtensionImpl(int extensionType) {
+        switch (extensionType) {
+            case CameraExtensionCharacteristics.EXTENSION_AUTOMATIC:
+                return new AutoAdvancedExtenderImpl();
+            case CameraExtensionCharacteristics.EXTENSION_BEAUTY:
+                return new BeautyAdvancedExtenderImpl();
+            case CameraExtensionCharacteristics.EXTENSION_BOKEH:
+                return new BokehAdvancedExtenderImpl();
+            case CameraExtensionCharacteristics.EXTENSION_HDR:
+                return new HdrAdvancedExtenderImpl();
+            case CameraExtensionCharacteristics.EXTENSION_NIGHT:
+                return new NightAdvancedExtenderImpl();
+            default:
+                throw new IllegalArgumentException("Unknown extension: " + extensionType);
+        }
+    }
+
     @Override
     public void onCreate() {
         super.onCreate();
         // This will setup the camera vendor tag descriptor in the service process
+        // along with all camera characteristics.
         try {
             CameraManager manager = getSystemService(CameraManager.class);
 
             String [] cameraIds = manager.getCameraIdListNoLazy();
             if (cameraIds != null) {
                 for (String cameraId : cameraIds) {
-                    CameraCharacteristics ch = manager.getCameraCharacteristics(cameraId);
+                    mCharacteristicsHashMap.put(cameraId,
+                            manager.getCameraCharacteristics(cameraId));
                 }
             }
         } catch (CameraAccessException e) {
@@ -372,6 +436,29 @@
         return ret;
     }
 
+    private static List<SizeList> initializeParcelable(
+            Map<Integer, List<android.util.Size>> sizes) {
+        if (sizes == null) {
+            return null;
+        }
+        ArrayList<SizeList> ret = new ArrayList<>(sizes.size());
+        for (Map.Entry<Integer, List<android.util.Size>> entry : sizes.entrySet()) {
+            SizeList sizeList = new SizeList();
+            sizeList.format = entry.getKey();
+            sizeList.sizes = new ArrayList<>();
+            for (android.util.Size size : entry.getValue()) {
+                android.hardware.camera2.extension.Size sz =
+                        new android.hardware.camera2.extension.Size();
+                sz.width = size.getWidth();
+                sz.height = size.getHeight();
+                sizeList.sizes.add(sz);
+            }
+            ret.add(sizeList);
+        }
+
+        return ret;
+    }
+
     private static CameraMetadataNative initializeParcelableMetadata(
             List<Pair<CaptureRequest.Key, Object>> paramList) {
         if (paramList == null) {
@@ -386,6 +473,20 @@
         return ret;
     }
 
+    private static CameraMetadataNative initializeParcelableMetadata(
+            Map<CaptureRequest.Key<?>, Object> paramMap) {
+        if (paramMap == null) {
+            return null;
+        }
+
+        CameraMetadataNative ret = new CameraMetadataNative();
+        for (Map.Entry<CaptureRequest.Key<?>, Object> param : paramMap.entrySet()) {
+            ret.set(((CaptureRequest.Key) param.getKey()), param.getValue());
+        }
+
+        return ret;
+    }
+
     private static android.hardware.camera2.extension.CaptureStageImpl initializeParcelable(
             androidx.camera.extensions.impl.CaptureStageImpl captureStage) {
         if (captureStage == null) {
@@ -400,6 +501,20 @@
         return ret;
     }
 
+    private Request initializeParcelable(RequestProcessorImpl.Request request, int requestId) {
+        Request ret = new Request();
+        ret.targetOutputConfigIds = new ArrayList<>();
+        for (int id : request.getTargetOutputConfigIds()) {
+            OutputConfigId configId = new OutputConfigId();
+            configId.id = id;
+            ret.targetOutputConfigIds.add(configId);
+        }
+        ret.templateId = request.getTemplateId();
+        ret.parameters = initializeParcelableMetadata(request.getParameters());
+        ret.requestId = requestId;
+        return ret;
+    }
+
     private class CameraExtensionsProxyServiceStub extends ICameraExtensionsProxyService.Stub {
         @Override
         public long registerClient() {
@@ -412,6 +527,23 @@
         }
 
         @Override
+        public boolean advancedExtensionsSupported() {
+            return ADVANCED_VERSION_SUPPORTED;
+        }
+
+        @Override
+        public IAdvancedExtenderImpl initializeAdvancedExtension(int extensionType) {
+            AdvancedExtenderImpl extension;
+            try {
+                extension = initializeAdvancedExtensionImpl(extensionType);
+            } catch (IllegalArgumentException e) {
+                return null;
+            }
+
+            return new AdvancedExtenderImplStub(extension);
+        }
+
+        @Override
         public IPreviewExtenderImpl initializePreviewExtension(int extensionType) {
             Pair<PreviewExtenderImpl, ImageCaptureExtenderImpl> extension;
             try {
@@ -436,6 +568,475 @@
         }
     }
 
+    private class AdvancedExtenderImplStub extends IAdvancedExtenderImpl.Stub {
+        private final AdvancedExtenderImpl mAdvancedExtender;
+
+        public AdvancedExtenderImplStub(AdvancedExtenderImpl advancedExtender) {
+            mAdvancedExtender = advancedExtender;
+        }
+
+        @Override
+        public boolean isExtensionAvailable(String cameraId) {
+            return mAdvancedExtender.isExtensionAvailable(cameraId, mCharacteristicsHashMap);
+        }
+
+        @Override
+        public void init(String cameraId) {
+            mAdvancedExtender.init(cameraId, mCharacteristicsHashMap);
+        }
+
+        @Override
+        public List<SizeList> getSupportedPreviewOutputResolutions(String cameraId) {
+            Map<Integer, List<Size>> supportedSizesMap =
+                    mAdvancedExtender.getSupportedPreviewOutputResolutions(cameraId);
+            if (supportedSizesMap != null) {
+                return initializeParcelable(supportedSizesMap);
+            }
+
+            return null;
+        }
+
+        @Override
+        public List<SizeList> getSupportedCaptureOutputResolutions(String cameraId) {
+            Map<Integer, List<Size>> supportedSizesMap =
+                    mAdvancedExtender.getSupportedCaptureOutputResolutions(cameraId);
+            if (supportedSizesMap != null) {
+                return initializeParcelable(supportedSizesMap);
+            }
+
+            return null;
+        }
+
+        @Override
+        public LatencyRange getEstimatedCaptureLatencyRange(String cameraId,
+                android.hardware.camera2.extension.Size outputSize, int format) {
+            Size sz = new Size(outputSize.width, outputSize.height);
+            Range<Long> latencyRange = mAdvancedExtender.getEstimatedCaptureLatencyRange(cameraId,
+                    sz, format);
+            if (latencyRange != null) {
+                LatencyRange ret = new LatencyRange();
+                ret.min = latencyRange.getLower();
+                ret.max = latencyRange.getUpper();
+                return ret;
+            }
+
+            return null;
+        }
+
+        @Override
+        public ISessionProcessorImpl getSessionProcessor() {
+            return new SessionProcessorImplStub(mAdvancedExtender.createSessionProcessor());
+        }
+    }
+
+    private class CaptureCallbackStub implements SessionProcessorImpl.CaptureCallback {
+        private final ICaptureCallback mCaptureCallback;
+
+        private CaptureCallbackStub(ICaptureCallback captureCallback) {
+            mCaptureCallback = captureCallback;
+        }
+
+        @Override
+        public void onCaptureStarted(int captureSequenceId, long timestamp) {
+            if (mCaptureCallback != null) {
+                try {
+                    mCaptureCallback.onCaptureStarted(captureSequenceId, timestamp);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failed to notify capture start due to remote " +
+                            "exception!");
+                }
+            }
+        }
+
+        @Override
+        public void onCaptureProcessStarted(int captureSequenceId) {
+            if (mCaptureCallback != null) {
+                try {
+                    mCaptureCallback.onCaptureProcessStarted(captureSequenceId);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failed to notify capture process start due to remote " +
+                            "exception!");
+                }
+            }
+        }
+
+        @Override
+        public void onCaptureFailed(int captureSequenceId) {
+            if (mCaptureCallback != null) {
+                try {
+                    mCaptureCallback.onCaptureFailed(captureSequenceId);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failed to notify capture failure due to remote " +
+                            "exception!");
+                }
+            }
+        }
+
+        @Override
+        public void onCaptureSequenceCompleted(int captureSequenceId) {
+            if (mCaptureCallback != null) {
+                try {
+                    mCaptureCallback.onCaptureSequenceCompleted(captureSequenceId);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failed to notify capture sequence end due to remote " +
+                            "exception!");
+                }
+            }
+        }
+
+        @Override
+        public void onCaptureSequenceAborted(int captureSequenceId) {
+            if (mCaptureCallback != null) {
+                try {
+                    mCaptureCallback.onCaptureSequenceAborted(captureSequenceId);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failed to notify capture sequence abort due to remote " +
+                            "exception!");
+                }
+            }
+        }
+    }
+
+    private class RequestCallbackStub extends IRequestCallback.Stub {
+        private final List<RequestProcessorImpl.Request> mRequests;
+        private final RequestProcessorImpl.Callback mCallback;
+
+        public RequestCallbackStub(List<RequestProcessorImpl.Request> requests,
+                RequestProcessorImpl.Callback callback) {
+            mCallback = callback;
+            if (mCallback != null) {
+                mRequests = requests;
+            } else {
+                Log.w(TAG, "No valid request callbacks!");
+                mRequests = new ArrayList<>();
+            }
+        }
+
+        @Override
+        public void onCaptureStarted(int requestId, long frameNumber, long timestamp) {
+            if (mCallback != null) {
+                if (mRequests.get(requestId) != null) {
+                    mCallback.onCaptureStarted(mRequests.get(requestId), frameNumber, timestamp);
+                } else {
+                    Log.e(TAG,"Request id: " + requestId + " not found!");
+                }
+            }
+        }
+
+        @Override
+        public void onCaptureProgressed(int requestId, ParcelCaptureResult partialResult) {
+            if (mCallback != null) {
+                if (mRequests.get(requestId) != null) {
+                    CaptureResult result = new CaptureResult(partialResult.cameraId,
+                            partialResult.results, partialResult.parent, partialResult.sequenceId,
+                            partialResult.frameNumber);
+                    mCallback.onCaptureProgressed(mRequests.get(requestId), result);
+                } else {
+                    Log.e(TAG,"Request id: " + requestId + " not found!");
+                }
+            }
+        }
+
+        @Override
+        public void onCaptureCompleted(int requestId, ParcelTotalCaptureResult totalCaptureResult) {
+            if (mCallback != null) {
+                if (mRequests.get(requestId) != null) {
+                    PhysicalCaptureResultInfo[] physicalResults = new PhysicalCaptureResultInfo[0];
+                    if ((totalCaptureResult.physicalResult != null) &&
+                            (!totalCaptureResult.physicalResult.isEmpty())) {
+                        int count = totalCaptureResult.physicalResult.size();
+                        physicalResults = new PhysicalCaptureResultInfo[count];
+                        physicalResults = totalCaptureResult.physicalResult.toArray(
+                                physicalResults);
+                    }
+                    ArrayList<CaptureResult> partials = new ArrayList<>(
+                            totalCaptureResult.partials.size());
+                    for (ParcelCaptureResult parcelResult : totalCaptureResult.partials) {
+                        partials.add(new CaptureResult(parcelResult.cameraId, parcelResult.results,
+                                parcelResult.parent, parcelResult.sequenceId,
+                                parcelResult.frameNumber));
+                    }
+                    TotalCaptureResult result = new TotalCaptureResult(
+                            totalCaptureResult.logicalCameraId, totalCaptureResult.results,
+                            totalCaptureResult.parent, totalCaptureResult.sequenceId,
+                            totalCaptureResult.frameNumber, partials, totalCaptureResult.sessionId,
+                            physicalResults);
+                    mCallback.onCaptureCompleted(mRequests.get(requestId), result);
+                } else {
+                    Log.e(TAG,"Request id: " + requestId + " not found!");
+                }
+            }
+        }
+
+        @Override
+        public void onCaptureFailed(int requestId, CaptureFailure captureFailure) {
+            if (mCallback != null) {
+                if (mRequests.get(requestId) != null) {
+                    android.hardware.camera2.CaptureFailure failure =
+                            new android.hardware.camera2.CaptureFailure(captureFailure.request,
+                                    captureFailure.reason, captureFailure.dropped,
+                                    captureFailure.sequenceId, captureFailure.frameNumber,
+                                    captureFailure.errorPhysicalCameraId);
+                    mCallback.onCaptureFailed(mRequests.get(requestId), failure);
+                } else {
+                    Log.e(TAG,"Request id: " + requestId + " not found!");
+                }
+            }
+        }
+
+        @Override
+        public void onCaptureBufferLost(int requestId, long frameNumber, int outputStreamId) {
+            if (mCallback != null) {
+                if (mRequests.get(requestId) != null) {
+                    mCallback.onCaptureBufferLost(mRequests.get(requestId), frameNumber,
+                            outputStreamId);
+                } else {
+                    Log.e(TAG,"Request id: " + requestId + " not found!");
+                }
+            }
+        }
+
+        @Override
+        public void onCaptureSequenceCompleted(int sequenceId, long frameNumber) {
+            if (mCallback != null) {
+                mCallback.onCaptureSequenceCompleted(sequenceId, frameNumber);
+            }
+        }
+
+        @Override
+        public void onCaptureSequenceAborted(int sequenceId) {
+            if (mCallback != null) {
+                mCallback.onCaptureSequenceAborted(sequenceId);
+            }
+        }
+    }
+
+    private class ImageProcessorImplStub extends IImageProcessorImpl.Stub {
+        private final ImageProcessorImpl mImageProcessor;
+
+        public ImageProcessorImplStub(ImageProcessorImpl imageProcessor) {
+            mImageProcessor = imageProcessor;
+        }
+
+        @Override
+        public void onNextImageAvailable(OutputConfigId outputConfigId, ParcelImage img) {
+            if (mImageProcessor != null) {
+                mImageProcessor.onNextImageAvailable(outputConfigId.id, img.timestamp,
+                        new ImageReferenceImpl(img));
+            }
+        }
+    }
+
+    private class RequestProcessorStub implements RequestProcessorImpl {
+        private final IRequestProcessorImpl mRequestProcessor;
+
+        public RequestProcessorStub(IRequestProcessorImpl requestProcessor) {
+            mRequestProcessor = requestProcessor;
+        }
+
+        @Override
+        public void setImageProcessor(int outputConfigId,
+                ImageProcessorImpl imageProcessor) {
+            OutputConfigId  configId = new OutputConfigId();
+            configId.id = outputConfigId;
+            try {
+                mRequestProcessor.setImageProcessor(configId,
+                        new ImageProcessorImplStub(imageProcessor));
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to set image processor due to remote exception!");
+            }
+        }
+
+        @Override
+        public boolean 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) {
+            ArrayList<android.hardware.camera2.extension.Request> captureRequests =
+                    new ArrayList<>();
+            int requestId = 0;
+            for (Request request : requests) {
+                captureRequests.add(initializeParcelable(request, requestId));
+                requestId++;
+            }
+
+            try {
+                return mRequestProcessor.submitBurst(captureRequests,
+                        new RequestCallbackStub(requests, callback));
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to submit request due to remote exception!");
+            }
+            return false;
+        }
+
+        @Override
+        public boolean setRepeating(Request request, Callback callback) {
+            try {
+                ArrayList<Request> requests = new ArrayList<>();
+                requests.add(request);
+                return mRequestProcessor.setRepeating(initializeParcelable(request, 0),
+                        new RequestCallbackStub(requests, callback));
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to submit repeating request due to remote exception!");
+            }
+
+            return false;
+        }
+
+        @Override
+        public void abortCaptures() {
+            try {
+                mRequestProcessor.abortCaptures();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to abort requests due to remote exception!");
+            }
+        }
+
+        @Override
+        public void stopRepeating() {
+            try {
+                mRequestProcessor.stopRepeating();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to stop repeating request due to remote exception!");
+            }
+        }
+    }
+
+    private class SessionProcessorImplStub extends ISessionProcessorImpl.Stub {
+        private final SessionProcessorImpl mSessionProcessor;
+
+        public SessionProcessorImplStub(SessionProcessorImpl sessionProcessor) {
+            mSessionProcessor = sessionProcessor;
+        }
+
+        @Override
+        public CameraSessionConfig initSession(String cameraId, OutputSurface previewSurface,
+                OutputSurface burstSurface) {
+            OutputSurfaceImplStub outputPreviewSurfaceImpl =
+                    new OutputSurfaceImplStub(previewSurface);
+            OutputSurfaceImplStub outputBurstSurfaceImpl =
+                    new OutputSurfaceImplStub(burstSurface);
+
+            Camera2SessionConfigImpl sessionConfig = mSessionProcessor.initSession(cameraId,
+                    mCharacteristicsHashMap, getApplicationContext(), outputPreviewSurfaceImpl,
+                    outputBurstSurfaceImpl, null /*imageAnalysisSurfaceConfig*/);
+
+            List<Camera2OutputConfigImpl> outputConfigs = sessionConfig.getOutputConfigs();
+            CameraSessionConfig ret = new CameraSessionConfig();
+            ret.outputConfigs = new ArrayList<>();
+            for (Camera2OutputConfigImpl output : outputConfigs) {
+                CameraOutputConfig entry = new CameraOutputConfig();
+                entry.outputId = new OutputConfigId();
+                entry.outputId.id = output.getId();
+                entry.physicalCameraId = output.getPhysicalCameraId();
+                entry.surfaceGroupId = output.getSurfaceGroupId();
+                if (output instanceof SurfaceOutputConfigImpl) {
+                    SurfaceOutputConfigImpl surfaceConfig = (SurfaceOutputConfigImpl) output;
+                    entry.type = CameraOutputConfig.TYPE_SURFACE;
+                    entry.surface = surfaceConfig.getSurface();
+                } else if (output instanceof ImageReaderOutputConfigImpl) {
+                    ImageReaderOutputConfigImpl imageReaderOutputConfig =
+                            (ImageReaderOutputConfigImpl) output;
+                    entry.type = CameraOutputConfig.TYPE_IMAGEREADER;
+                    entry.size = new android.hardware.camera2.extension.Size();
+                    entry.size.width = imageReaderOutputConfig.getSize().getWidth();
+                    entry.size.height = imageReaderOutputConfig.getSize().getHeight();
+                    entry.imageFormat = imageReaderOutputConfig.getImageFormat();
+                    entry.capacity = imageReaderOutputConfig.getMaxImages();
+                } else if (output instanceof MultiResolutionImageReaderOutputConfigImpl) {
+                    MultiResolutionImageReaderOutputConfigImpl multiResReaderConfig =
+                            (MultiResolutionImageReaderOutputConfigImpl) output;
+                    entry.type = CameraOutputConfig.TYPE_MULTIRES_IMAGEREADER;
+                    entry.imageFormat = multiResReaderConfig.getImageFormat();
+                    entry.capacity = multiResReaderConfig.getMaxImages();
+                } else {
+                    throw new IllegalStateException("Unknown output config type!");
+                }
+                List<Camera2OutputConfigImpl> sharedOutputs =
+                        output.getSurfaceSharingOutputConfigs();
+                if ((sharedOutputs != null) && (!sharedOutputs.isEmpty())) {
+                    entry.surfaceSharingOutputConfigs = new ArrayList<>();
+                    for (Camera2OutputConfigImpl sharedOutput : sharedOutputs) {
+                        OutputConfigId outputId = new OutputConfigId();
+                        outputId.id = sharedOutput.getId();
+                        entry.surfaceSharingOutputConfigs.add(outputId);
+                    }
+                }
+                ret.outputConfigs.add(entry);
+            }
+            ret.sessionTemplateId = sessionConfig.getSessionTemplateId();
+            ret.sessionParameter = initializeParcelableMetadata(
+                    sessionConfig.getSessionParameters());
+
+            return ret;
+        }
+
+        @Override
+        public void deInitSession() {
+            mSessionProcessor.deInitSession();
+        }
+
+        @Override
+        public void onCaptureSessionStart(IRequestProcessorImpl requestProcessor) {
+            mSessionProcessor.onCaptureSessionStart(new RequestProcessorStub(requestProcessor));
+        }
+
+        @Override
+        public void onCaptureSessionEnd() {
+            mSessionProcessor.onCaptureSessionEnd();
+        }
+
+        @Override
+        public int startRepeating(ICaptureCallback callback) {
+            return mSessionProcessor.startRepeating(new CaptureCallbackStub(callback));
+        }
+
+        @Override
+        public void stopRepeating() {
+            mSessionProcessor.stopRepeating();
+        }
+
+        @Override
+        public int startCapture(ICaptureCallback callback, int jpegRotation, int jpegQuality) {
+            HashMap<CaptureRequest.Key<?>, Object> paramMap = new HashMap<>();
+            paramMap.put(CaptureRequest.JPEG_ORIENTATION, jpegRotation);
+            paramMap.put(CaptureRequest.JPEG_QUALITY, jpegQuality);
+            mSessionProcessor.setParameters(paramMap);
+            return mSessionProcessor.startCapture(new CaptureCallbackStub(callback));
+        }
+    }
+
+    private class OutputSurfaceImplStub implements OutputSurfaceImpl {
+        private final Surface mSurface;
+        private final Size mSize;
+        private final int mImageFormat;
+
+        public OutputSurfaceImplStub(OutputSurface outputSurface) {
+            mSurface = outputSurface.surface;
+            mSize = new Size(outputSurface.size.width, outputSurface.size.height);
+            mImageFormat = outputSurface.imageFormat;
+        }
+
+        @Override
+        public Surface getSurface() {
+            return mSurface;
+        }
+
+        @Override
+        public Size getSize() {
+            return mSize;
+        }
+
+        @Override
+        public int getImageFormat() {
+            return mImageFormat;
+        }
+    }
+
     private class PreviewExtenderImplStub extends IPreviewExtenderImpl.Stub {
         private final PreviewExtenderImpl mPreviewExtender;
 
@@ -471,7 +1072,7 @@
 
         @Override
         public void init(String cameraId, CameraMetadataNative chars) {
-            if (LATEST_VERSION_SUPPORTED) {
+            if (LEGACY_VERSION_SUPPORTED) {
                 mPreviewExtender.init(cameraId, new CameraCharacteristics(chars));
             }
         }
@@ -535,7 +1136,7 @@
 
         @Override
         public List<SizeList> getSupportedResolutions() {
-            if (LATEST_VERSION_SUPPORTED) {
+            if (LEGACY_VERSION_SUPPORTED) {
                 List<Pair<Integer, android.util.Size[]>> sizes =
                         mPreviewExtender.getSupportedResolutions();
                 if ((sizes != null) && !sizes.isEmpty()) {
@@ -581,7 +1182,7 @@
 
         @Override
         public void init(String cameraId, CameraMetadataNative chars) {
-            if (LATEST_VERSION_SUPPORTED) {
+            if (LEGACY_VERSION_SUPPORTED) {
                 mImageExtender.init(cameraId, new CameraCharacteristics(chars));
             }
         }
@@ -627,7 +1228,7 @@
 
         @Override
         public List<SizeList> getSupportedResolutions() {
-            if (LATEST_VERSION_SUPPORTED) {
+            if (LEGACY_VERSION_SUPPORTED) {
                 List<Pair<Integer, android.util.Size[]>> sizes =
                         mImageExtender.getSupportedResolutions();
                 if ((sizes != null) && !sizes.isEmpty()) {
@@ -737,6 +1338,51 @@
         }
     }
 
+    private class ImageReferenceImpl extends ExtensionImage
+            implements androidx.camera.extensions.impl.advanced.ImageReferenceImpl {
+
+        private final Object mImageLock = new Object();
+        private int mReferenceCount;
+
+        private ImageReferenceImpl(ParcelImage parcelImage) {
+            super(parcelImage);
+            mReferenceCount = 1;
+        }
+
+        @Override
+        public boolean increment() {
+            synchronized (mImageLock) {
+                if (mReferenceCount <= 0) {
+                    return false;
+                }
+                mReferenceCount++;
+            }
+
+            return true;
+        }
+
+        @Override
+        public boolean decrement() {
+            synchronized (mImageLock) {
+                if (mReferenceCount <= 0) {
+                    return false;
+                }
+                mReferenceCount--;
+
+                if (mReferenceCount <= 0) {
+                    close();
+                }
+            }
+
+            return true;
+        }
+
+        @Override
+        public Image get() {
+            return this;
+        }
+    }
+
     private class ExtensionImage extends android.media.Image {
         private final android.hardware.camera2.extension.ParcelImage mParcelImage;
         private GraphicBuffer mGraphicBuffer;
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index db36e62..c3543e7 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -361,10 +361,13 @@
         try {
             executeRescueLevelInternal(context, level, failedPackage);
             EventLogTags.writeRescueSuccess(level);
-            logCriticalInfo(Log.DEBUG,
-                    "Finished rescue level " + levelToString(level));
+            String successMsg = "Finished rescue level " + levelToString(level);
+            if (!TextUtils.isEmpty(failedPackage)) {
+                successMsg += " for package " + failedPackage;
+            }
+            logCriticalInfo(Log.DEBUG, successMsg);
         } catch (Throwable t) {
-            logRescueException(level, t);
+            logRescueException(level, failedPackage, t);
         }
     }
 
@@ -427,7 +430,7 @@
                             pm.reboot(TAG);
                         }
                     } catch (Throwable t) {
-                        logRescueException(level, t);
+                        logRescueException(level, failedPackage, t);
                     }
                 };
                 thread = new Thread(runnable);
@@ -441,7 +444,7 @@
                         try {
                             RecoverySystem.rebootPromptAndWipeUserData(context, TAG);
                         } catch (Throwable t) {
-                            logRescueException(level, t);
+                            logRescueException(level, failedPackage, t);
                         }
                     }
                 };
@@ -455,11 +458,15 @@
         }
     }
 
-    private static void logRescueException(int level, Throwable t) {
+    private static void logRescueException(int level, @Nullable String failedPackageName,
+            Throwable t) {
         final String msg = ExceptionUtils.getCompleteMessage(t);
         EventLogTags.writeRescueFailure(level, msg);
-        logCriticalInfo(Log.ERROR,
-                "Failed rescue level " + levelToString(level) + ": " + msg);
+        String failureMsg = "Failed rescue level " + levelToString(level);
+        if (!TextUtils.isEmpty(failedPackageName)) {
+            failureMsg += " for package " + failedPackageName;
+        }
+        logCriticalInfo(Log.ERROR, failureMsg + ": " + msg);
     }
 
     private static int mapRescueLevelToUserImpact(int rescueLevel) {
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index 7763ad9..a05eb68 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -469,7 +469,7 @@
         @Override
         public void setIndividualSensorPrivacy(@UserIdInt int userId, int sensor, boolean enable) {
             enforceManageSensorPrivacyPermission();
-            if (!canChangeIndividualSensorPrivacy(sensor)) {
+            if (!canChangeIndividualSensorPrivacy(userId, sensor)) {
                 return;
             }
 
@@ -500,14 +500,14 @@
             mHandler.onSensorPrivacyChanged(userId, sensor, enable);
         }
 
-        private boolean canChangeIndividualSensorPrivacy(int sensor) {
+        private boolean canChangeIndividualSensorPrivacy(@UserIdInt int userId, int sensor) {
             if (sensor == MICROPHONE && mEmergencyCallHelper.isInEmergencyCall()) {
                 // During emergency call the microphone toggle managed automatically
                 Log.i(TAG, "Can't change mic toggle during an emergency call");
                 return false;
             }
 
-            if (mKeyguardManager != null && mKeyguardManager.isDeviceLocked()) {
+            if (mKeyguardManager != null && mKeyguardManager.isDeviceLocked(userId)) {
                 Log.i(TAG, "Can't change mic/cam toggle while device is locked");
                 return false;
             }
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 9835a9a..7a0a3a7 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -97,6 +97,7 @@
         "/system/bin/audioserver",
         "/system/bin/cameraserver",
         "/system/bin/drmserver",
+        "/system/bin/keystore2",
         "/system/bin/mediadrmserver",
         "/system/bin/mediaserver",
         "/system/bin/netd",
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 70538bb..83cff15 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -9335,7 +9335,7 @@
             pw.println("  mFgsStartTempAllowList:");
             final long currentTimeNow = System.currentTimeMillis();
             final long elapsedRealtimeNow = SystemClock.elapsedRealtime();
-            final Set<Integer> uids = mFgsStartTempAllowList.keySet();
+            final Set<Integer> uids = new ArraySet<>(mFgsStartTempAllowList.keySet());
             for (Integer uid : uids) {
                 final Pair<Long, FgsTempAllowListItem> entry = mFgsStartTempAllowList.get(uid);
                 if (entry == null) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index f0b116c..de2c11b 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -828,6 +828,7 @@
             } else {
                 r.receiverTime = SystemClock.uptimeMillis();
                 maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
+                maybeScheduleTempAllowlistLocked(filter.owningUid, r, r.options);
                 performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
                         new Intent(r.intent), r.resultCode, r.resultData,
                         r.resultExtras, r.ordered, r.initialSticky, r.userId);
@@ -911,9 +912,16 @@
         return false;
     }
 
-    final void scheduleTempAllowlistLocked(int uid, long duration, BroadcastRecord r,
-            @TempAllowListType int type, @ReasonCode int reasonCode,
-            @Nullable String reason) {
+    void maybeScheduleTempAllowlistLocked(int uid, BroadcastRecord r,
+            @Nullable BroadcastOptions brOptions) {
+        if (brOptions == null || brOptions.getTemporaryAppAllowlistDuration() <= 0) {
+            return;
+        }
+        long duration = brOptions.getTemporaryAppAllowlistDuration();
+        final @TempAllowListType int type = brOptions.getTemporaryAppAllowlistType();
+        final @ReasonCode int reasonCode = brOptions.getTemporaryAppAllowlistReasonCode();
+        final String reason = brOptions.getTemporaryAppAllowlistReason();
+
         if (duration > Integer.MAX_VALUE) {
             duration = Integer.MAX_VALUE;
         }
@@ -1344,13 +1352,6 @@
                     // r is guaranteed ordered at this point, so we know finishReceiverLocked()
                     // will get a callback and handle the activity start token lifecycle.
                 }
-                if (brOptions != null && brOptions.getTemporaryAppAllowlistDuration() > 0) {
-                    scheduleTempAllowlistLocked(filter.owningUid,
-                            brOptions.getTemporaryAppAllowlistDuration(), r,
-                            brOptions.getTemporaryAppAllowlistType(),
-                            brOptions.getTemporaryAppAllowlistReasonCode(),
-                            brOptions.getTemporaryAppAllowlistReason());
-                }
             }
             return;
         }
@@ -1646,16 +1647,9 @@
                     + info.activityInfo + ", callingUid = " + r.callingUid + ", uid = "
                     + receiverUid);
         }
-
         final boolean isActivityCapable =
                 (brOptions != null && brOptions.getTemporaryAppAllowlistDuration() > 0);
-        if (isActivityCapable) {
-            scheduleTempAllowlistLocked(receiverUid,
-                    brOptions.getTemporaryAppAllowlistDuration(), r,
-                    brOptions.getTemporaryAppAllowlistType(),
-                    brOptions.getTemporaryAppAllowlistReasonCode(),
-                    brOptions.getTemporaryAppAllowlistReason());
-        }
+        maybeScheduleTempAllowlistLocked(receiverUid, r, brOptions);
 
         // Report that a component is used for explicit broadcasts.
         if (!r.intent.isExcludingStopped() && r.curComponent != null
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index cc750ce..709139e 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -838,7 +838,7 @@
                 KEY_FREEZER_DEBOUNCE_TIMEOUT, DEFAULT_FREEZER_DEBOUNCE_TIMEOUT);
 
         if (mFreezerDebounceTimeout < 0) {
-            mFullDeltaRssThrottleKb = DEFAULT_FREEZER_DEBOUNCE_TIMEOUT;
+            mFreezerDebounceTimeout = DEFAULT_FREEZER_DEBOUNCE_TIMEOUT;
         }
     }
 
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 40db086..01bbb03 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -394,6 +394,10 @@
         mProcessGroupHandler = new Handler(adjusterThread.getLooper(), msg -> {
             final int pid = msg.arg1;
             final int group = msg.arg2;
+            if (pid == ActivityManagerService.MY_PID) {
+                // Skip setting the process group for system_server, keep it as default.
+                return true;
+            }
             if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setProcessGroup "
                         + msg.obj + " to " + group);
diff --git a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
index a33e7e5..408c7cb 100644
--- a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
+++ b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
@@ -171,7 +171,7 @@
         return mFreezeExempt;
     }
 
-    @GuardedBy("mPreLock")
+    @GuardedBy("mProcLock")
     void setFreezeExempt(boolean exempt) {
         mFreezeExempt = exempt;
     }
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index 78ff67a..05c4431 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -296,9 +296,7 @@
                         FrameworkStatsLog.USER_LEVEL_HIBERNATION_STATE_CHANGED,
                         stateSnapshot.packageName,
                         userIdSnapshot,
-                        stateSnapshot.hibernated,
-                        // TODO(b/187224817): This isn't the expected value right now.
-                        stateSnapshot.lastUnhibernatedMs);
+                        stateSnapshot.hibernated);
             });
             List<UserLevelState> states = new ArrayList<>(mUserStates.get(userId).values());
             mUserDiskStores.get(userId).scheduleWriteHibernationStates(states);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 098ce7c..a2722cb 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -6535,6 +6535,10 @@
                     if (index == -1) {
                         continue;
                     }
+                    if (mPublicStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED
+                            && mCameraSoundForced) {
+                        index = mIndexMax;
+                    }
                     if (DEBUG_VOL) {
                         Log.v(TAG, "readSettings: found stored index " + getValidIndex(index)
                                  + " for group " + mAudioVolumeGroup.name() + ", device: " + name
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 677ea5d..6482a2e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
@@ -46,6 +46,7 @@
      * Interface that ClientMonitor holders should use to receive callbacks.
      */
     public interface Callback {
+
         /**
          * Invoked when the ClientMonitor operation has been started (e.g. reached the head of
          * the queue and becomes the current operation).
@@ -222,6 +223,7 @@
                 + this.getClass().getSimpleName()
                 + ", " + getProtoEnum()
                 + ", " + getOwnerString()
-                + ", " + getCookie() + "}";
+                + ", " + getCookie()
+                + ", " + getTargetUserId() + "}";
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
index 62a9769..f1c786b49 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
@@ -132,11 +132,13 @@
         }
     }
 
-    public void onChallengeGenerated(int sensorId, long challenge) throws RemoteException {
+    /** Called when a challenged has been generated. */
+    public void onChallengeGenerated(int sensorId, int userId, long challenge)
+            throws RemoteException {
         if (mFaceServiceReceiver != null) {
-            mFaceServiceReceiver.onChallengeGenerated(sensorId, challenge);
+            mFaceServiceReceiver.onChallengeGenerated(sensorId, userId, challenge);
         } else if (mFingerprintServiceReceiver != null) {
-            mFingerprintServiceReceiver.onChallengeGenerated(sensorId, challenge);
+            mFingerprintServiceReceiver.onChallengeGenerated(sensorId, userId, challenge);
         }
     }
 
@@ -153,18 +155,6 @@
         }
     }
 
-    public void onChallengeInterrupted(int sensorId) throws RemoteException {
-        if (mFaceServiceReceiver != null) {
-            mFaceServiceReceiver.onChallengeInterrupted(sensorId);
-        }
-    }
-
-    public void onChallengeInterruptFinished(int sensorId) throws RemoteException {
-        if (mFaceServiceReceiver != null) {
-            mFaceServiceReceiver.onChallengeInterruptFinished(sensorId);
-        }
-    }
-
     // Fingerprint-specific callbacks for FingerprintManager only
 
     public void onUdfpsPointerDown(int sensorId) throws RemoteException {
diff --git a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
index 1fcad62..3d74f36 100644
--- a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
@@ -40,7 +40,7 @@
     @Override
     public void unableToStart() {
         try {
-            getListener().onChallengeGenerated(getSensorId(), 0L);
+            getListener().onChallengeGenerated(getSensorId(), getTargetUserId(), 0L);
         } catch (RemoteException e) {
             Slog.e(TAG, "Unable to send error", e);
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
index 8726923..57c1c74 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
@@ -110,17 +110,7 @@
         }
 
         @Override
-        public void onChallengeGenerated(int sensorId, long challenge) {
-
-        }
-
-        @Override
-        public void onChallengeInterrupted(int sensorId) {
-
-        }
-
-        @Override
-        public void onChallengeInterruptFinished(int sensorId) {
+        public void onChallengeGenerated(int sensorId, int userId, long challenge) {
 
         }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
index 904c399..d76036b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java
@@ -52,7 +52,7 @@
 
     void onChallengeGenerated(int sensorId, int userId, long challenge) {
         try {
-            getListener().onChallengeGenerated(sensorId, challenge);
+            getListener().onChallengeGenerated(sensorId, userId, challenge);
             mCallback.onClientFinished(this, true /* success */);
         } catch (RemoteException e) {
             Slog.e(TAG, "Unable to send challenge", e);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
index f806767..d0580c7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
@@ -99,17 +99,7 @@
         }
 
         @Override
-        public void onChallengeGenerated(int sensorId, long challenge) {
-
-        }
-
-        @Override
-        public void onChallengeInterrupted(int sensorId) {
-
-        }
-
-        @Override
-        public void onChallengeInterruptFinished(int sensorId) {
+        public void onChallengeGenerated(int sensorId, int userId, long challenge) {
 
         }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index f908fba..a5bb0f4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -80,6 +80,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.time.Clock;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -93,7 +94,13 @@
 public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
 
     private static final String TAG = "Face10";
+
     private static final int ENROLL_TIMEOUT_SEC = 75;
+    private static final int GENERATE_CHALLENGE_REUSE_INTERVAL_MILLIS = 60 * 1000;
+    private static final int GENERATE_CHALLENGE_COUNTER_TTL_MILLIS =
+            FaceGenerateChallengeClient.CHALLENGE_TIMEOUT_SEC * 1000;
+    @VisibleForTesting
+    public static Clock sSystemClock = Clock.systemUTC();
 
     private boolean mTestHalEnabled;
 
@@ -102,19 +109,15 @@
     @NonNull private final BiometricScheduler mScheduler;
     @NonNull private final Handler mHandler;
     @NonNull private final HalClientMonitor.LazyDaemon<IBiometricsFace> mLazyDaemon;
-    @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher;
     @NonNull private final LockoutHalImpl mLockoutTracker;
     @NonNull private final UsageStats mUsageStats;
     @NonNull private final Map<Integer, Long> mAuthenticatorIds;
     @Nullable private IBiometricsFace mDaemon;
     @NonNull private final HalResultController mHalResultController;
-    // If a challenge is generated, keep track of its owner. Since IBiometricsFace@1.0 only
-    // supports a single in-flight challenge, we must notify the interrupted owner that its
-    // challenge is no longer valid. The interrupted owner will be notified when the interrupter
-    // has finished.
-    @Nullable private FaceGenerateChallengeClient mCurrentChallengeOwner;
     private int mCurrentUserId = UserHandle.USER_NULL;
     private final int mSensorId;
+    private final List<Long> mGeneratedChallengeCount = new ArrayList<>();
+    private FaceGenerateChallengeClient mGeneratedChallengeCache = null;
 
     private final UserSwitchObserver mUserSwitchObserver = new SynchronousUserSwitchObserver() {
         @Override
@@ -335,7 +338,6 @@
         mAuthenticatorIds = new HashMap<>();
         mLazyDaemon = Face10.this::getDaemon;
         mLockoutTracker = new LockoutHalImpl();
-        mLockoutResetDispatcher = lockoutResetDispatcher;
         mHalResultController = new HalResultController(sensorProps.sensorId, context, mHandler,
                 mScheduler, mLockoutTracker, lockoutResetDispatcher);
         mHalResultController.setCallback(() -> {
@@ -480,56 +482,56 @@
         return getDaemon() != null;
     }
 
+    private boolean isGeneratedChallengeCacheValid() {
+        return mGeneratedChallengeCache != null
+                && sSystemClock.millis() - mGeneratedChallengeCache.getCreatedAt()
+                < GENERATE_CHALLENGE_REUSE_INTERVAL_MILLIS;
+    }
+
+    private void incrementChallengeCount() {
+        mGeneratedChallengeCount.add(0, sSystemClock.millis());
+    }
+
+    private int decrementChallengeCount() {
+        final long now = sSystemClock.millis();
+        // ignore values that are old in case generate/revoke calls are not matched
+        // this doesn't ensure revoke if calls are mismatched but it keeps the list from growing
+        mGeneratedChallengeCount.removeIf(x -> now - x > GENERATE_CHALLENGE_COUNTER_TTL_MILLIS);
+        if (!mGeneratedChallengeCount.isEmpty()) {
+            mGeneratedChallengeCount.remove(0);
+        }
+        return mGeneratedChallengeCount.size();
+    }
+
     /**
-     * {@link IBiometricsFace} only supports a single in-flight challenge. In cases where two
-     * callers both need challenges (e.g. resetLockout right before enrollment), we need to ensure
-     * that either:
-     * 1) generateChallenge/operation/revokeChallenge is complete before the next generateChallenge
-     *    is processed by the scheduler, or
-     * 2) the generateChallenge callback provides a mechanism for notifying the caller that its
-     *    challenge has been invalidated by a subsequent caller, as well as a mechanism for
-     *    notifying the previous caller that the interrupting operation is complete (e.g. the
-     *    interrupting client's challenge has been revoked, so that the interrupted client can
-     *    start retry logic if necessary). See
-     *    {@link
-     *android.hardware.face.FaceManager.GenerateChallengeCallback#onChallengeInterruptFinished(int)}
-     * The only case of conflicting challenges is currently resetLockout --> enroll. So, the second
-     * option seems better as it prioritizes the new operation, which is user-facing.
+     * {@link IBiometricsFace} only supports a single in-flight challenge but there are cases where
+     * two callers both need challenges (e.g. resetLockout right before enrollment).
      */
     @Override
     public void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token,
             @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
         mHandler.post(() -> {
-            if (mCurrentChallengeOwner != null) {
-                final ClientMonitorCallbackConverter listener =
-                        mCurrentChallengeOwner.getListener();
-                Slog.w(TAG, "Current challenge owner: " + mCurrentChallengeOwner
-                        + ", listener: " + listener
-                        + ", interrupted by: " + opPackageName);
-                if (listener != null) {
-                    try {
-                        listener.onChallengeInterrupted(mSensorId);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "Unable to notify challenge interrupted", e);
-                    }
-                }
+            incrementChallengeCount();
+
+            if (isGeneratedChallengeCacheValid()) {
+                Slog.d(TAG, "Current challenge is cached and will be reused");
+                mGeneratedChallengeCache.reuseResult(receiver);
+                return;
             }
 
             scheduleUpdateActiveUserWithoutHandler(userId);
 
             final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext,
                     mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
-                    opPackageName, mSensorId, mCurrentChallengeOwner);
+                    opPackageName, mSensorId, sSystemClock.millis());
+            mGeneratedChallengeCache = client;
             mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
                 @Override
                 public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
                     if (client != clientMonitor) {
                         Slog.e(TAG, "scheduleGenerateChallenge onClientStarted, mismatched client."
                                 + " Expecting: " + client + ", received: " + clientMonitor);
-                        return;
                     }
-                    Slog.d(TAG, "Current challenge owner: " + client);
-                    mCurrentChallengeOwner = client;
                 }
             });
         });
@@ -539,14 +541,16 @@
     public void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token,
             @NonNull String opPackageName, long challenge) {
         mHandler.post(() -> {
-            if (mCurrentChallengeOwner != null
-                    && !mCurrentChallengeOwner.getOwnerString().contentEquals(opPackageName)) {
-                Slog.e(TAG, "scheduleRevokeChallenge, package: " + opPackageName
-                        + " attempting to revoke challenge owned by: "
-                        + mCurrentChallengeOwner.getOwnerString());
+            final boolean shouldRevoke = decrementChallengeCount() == 0;
+            if (!shouldRevoke) {
+                Slog.w(TAG, "scheduleRevokeChallenge skipped - challenge still in use: "
+                        + mGeneratedChallengeCount);
                 return;
             }
 
+            Slog.d(TAG, "scheduleRevokeChallenge executing - no active clients");
+            mGeneratedChallengeCache = null;
+
             final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext,
                     mLazyDaemon, token, userId, opPackageName, mSensorId);
             mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@@ -556,33 +560,6 @@
                     if (client != clientMonitor) {
                         Slog.e(TAG, "scheduleRevokeChallenge, mismatched client."
                                 + "Expecting: " + client + ", received: " + clientMonitor);
-                        return;
-                    }
-
-                    if (mCurrentChallengeOwner == null) {
-                        // Can happen if revoke is incorrectly called, for example without a
-                        // preceding generateChallenge
-                        Slog.w(TAG, "Current challenge owner is null");
-                        return;
-                    }
-
-                    final FaceGenerateChallengeClient previousChallengeOwner =
-                            mCurrentChallengeOwner.getInterruptedClient();
-                    mCurrentChallengeOwner = null;
-
-                    Slog.d(TAG, "Previous challenge owner: " + previousChallengeOwner);
-                    if (previousChallengeOwner != null) {
-                        final ClientMonitorCallbackConverter listener =
-                                previousChallengeOwner.getListener();
-                        if (listener == null) {
-                            Slog.w(TAG, "Listener is null");
-                        } else {
-                            try {
-                                listener.onChallengeInterruptFinished(mSensorId);
-                            } catch (RemoteException e) {
-                                Slog.e(TAG, "Unable to notify interrupt finished", e);
-                            }
-                        }
                     }
                 }
             });
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
index 3e0064e4..f418104 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
@@ -17,16 +17,20 @@
 package com.android.server.biometrics.sensors.face.hidl;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.face.IFaceServiceReceiver;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.internal.util.Preconditions;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.GenerateChallengeClient;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Face-specific generateChallenge client supporting the
  * {@link android.hardware.biometrics.face.V1_0} HIDL interface.
@@ -34,40 +38,70 @@
 public class FaceGenerateChallengeClient extends GenerateChallengeClient<IBiometricsFace> {
 
     private static final String TAG = "FaceGenerateChallengeClient";
-    private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
+    static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
+    private static final Callback EMPTY_CALLBACK = new Callback() {
+    };
 
-    // If `this` FaceGenerateChallengeClient was invoked while an existing in-flight challenge
-    // was not revoked yet, store a reference to the interrupted client here. Notify the interrupted
-    // client when `this` challenge is revoked.
-    @Nullable private final FaceGenerateChallengeClient mInterruptedClient;
+    private final long mCreatedAt;
+    private List<IFaceServiceReceiver> mWaiting;
+    private Long mChallengeResult;
 
     FaceGenerateChallengeClient(@NonNull Context context,
             @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token,
             @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
-            int sensorId, @Nullable FaceGenerateChallengeClient interruptedClient) {
+            int sensorId, long now) {
         super(context, lazyDaemon, token, listener, userId, owner, sensorId);
-        mInterruptedClient = interruptedClient;
-    }
-
-    @Nullable
-    public FaceGenerateChallengeClient getInterruptedClient() {
-        return mInterruptedClient;
+        mCreatedAt = now;
+        mWaiting = new ArrayList<>();
     }
 
     @Override
     protected void startHalOperation() {
+        mChallengeResult = null;
         try {
-            final long challenge = getFreshDaemon().generateChallenge(CHALLENGE_TIMEOUT_SEC).value;
-            try {
-                getListener().onChallengeGenerated(getSensorId(), challenge);
-                mCallback.onClientFinished(this, true /* success */);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Remote exception", e);
-                mCallback.onClientFinished(this, false /* success */);
+            mChallengeResult = getFreshDaemon().generateChallenge(CHALLENGE_TIMEOUT_SEC).value;
+            // send the result to the original caller via mCallback and any waiting callers
+            // that called reuseResult
+            sendChallengeResult(getListener(), mCallback);
+            for (IFaceServiceReceiver receiver : mWaiting) {
+                sendChallengeResult(new ClientMonitorCallbackConverter(receiver), EMPTY_CALLBACK);
             }
         } catch (RemoteException e) {
             Slog.e(TAG, "generateChallenge failed", e);
             mCallback.onClientFinished(this, false /* success */);
+        } finally {
+            mWaiting = null;
+        }
+    }
+
+    /** @return An arbitrary time value for caching provided to the constructor. */
+    public long getCreatedAt() {
+        return mCreatedAt;
+    }
+
+    /**
+     * Reuse the result of this operation when it is available. The receiver will be notified
+     * immediately if a challenge has already been generated.
+     *
+     * @param receiver receiver to be notified of challenge result
+     */
+    public void reuseResult(@NonNull IFaceServiceReceiver receiver) {
+        if (mWaiting != null) {
+            mWaiting.add(receiver);
+        } else {
+            sendChallengeResult(new ClientMonitorCallbackConverter(receiver), EMPTY_CALLBACK);
+        }
+    }
+
+    private void sendChallengeResult(@NonNull ClientMonitorCallbackConverter receiver,
+            @NonNull Callback ownerCallback) {
+        Preconditions.checkState(mChallengeResult != null, "result not available");
+        try {
+            receiver.onChallengeGenerated(getSensorId(), getTargetUserId(), mChallengeResult);
+            ownerCallback.onClientFinished(this, true /* success */);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote exception", e);
+            ownerCallback.onClientFinished(this, false /* success */);
         }
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
index e34afc0..29f2f20 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
@@ -100,7 +100,7 @@
         }
 
         @Override
-        public void onChallengeGenerated(int sensorId, long challenge) {
+        public void onChallengeGenerated(int sensorId, int userId, long challenge) {
 
         }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
index 293b57d..6d01481 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
@@ -53,7 +53,7 @@
 
     void onChallengeGenerated(int sensorId, int userId, long challenge) {
         try {
-            getListener().onChallengeGenerated(sensorId, challenge);
+            getListener().onChallengeGenerated(sensorId, userId, challenge);
             mCallback.onClientFinished(this, true /* success */);
         } catch (RemoteException e) {
             Slog.e(TAG, "Unable to send challenge", e);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
index ad4f679..c00daff 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
@@ -101,7 +101,7 @@
         }
 
         @Override
-        public void onChallengeGenerated(int sensorId, long challenge) {
+        public void onChallengeGenerated(int sensorId, int userId, long challenge) {
 
         }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
index 3584397..db2f045 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
@@ -48,7 +48,7 @@
         try {
             final long challenge = getFreshDaemon().preEnroll();
             try {
-                getListener().onChallengeGenerated(getSensorId(), challenge);
+                getListener().onChallengeGenerated(getSensorId(), getTargetUserId(), challenge);
                 mCallback.onClientFinished(this, true /* success */);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote exception", e);
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index 28f208b..5886b1a 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -338,7 +338,8 @@
             return currentPermission;
         }
         try {
-            final PackageInfo app = mPackageManager.getPackageInfo(name, GET_PERMISSIONS);
+            final PackageInfo app = mPackageManager.getPackageInfo(name,
+                    GET_PERMISSIONS | MATCH_ANY_USER);
             final boolean isNetwork = hasNetworkPermission(app);
             final boolean hasRestrictedPermission = hasRestrictedNetworkPermission(app);
             if (isNetwork || hasRestrictedPermission) {
@@ -664,6 +665,7 @@
                     break;
                 case INetd.PERMISSION_UNINSTALLED:
                     uninstalledAppIds.add(netdPermissionsAppIds.keyAt(i));
+                    break;
                 default:
                     Log.e(TAG, "unknown permission type: " + permissions + "for uid: "
                             + netdPermissionsAppIds.keyAt(i));
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 10f6948f..dcd0eb8 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -136,18 +136,19 @@
         if (!mService.isControlEnabled()) {
             return;
         }
-        if (isActiveSource()) {
-            mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource(
-                    mAddress, mService.getPhysicalAddress()));
-        }
         boolean wasActiveSource = isActiveSource();
-        // Invalidate the internal active source record when goes to standby
+        // Invalidate the internal active source record when going to standby
         mService.setActiveSource(Constants.ADDR_INVALID, Constants.INVALID_PHYSICAL_ADDRESS,
                 "HdmiCecLocalDevicePlayback#onStandby()");
         boolean mTvSendStandbyOnSleep = mService.getHdmiCecConfig().getIntValue(
                 HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP)
                     == HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED;
-        if (initiatedByCec || !mTvSendStandbyOnSleep || !wasActiveSource) {
+        if (!wasActiveSource) {
+            return;
+        }
+        if (initiatedByCec || !mTvSendStandbyOnSleep) {
+            mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource(mAddress,
+                            mService.getPhysicalAddress()));
             return;
         }
         switch (standbyAction) {
@@ -167,6 +168,9 @@
                                         Constants.ADDR_BROADCAST));
                         break;
                     case HdmiControlManager.POWER_CONTROL_MODE_NONE:
+                        mService.sendCecCommand(
+                                HdmiCecMessageBuilder.buildInactiveSource(mAddress,
+                                        mService.getPhysicalAddress()));
                         break;
                 }
                 break;
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java b/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java
index 7226cc2..e52e32a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java
@@ -90,6 +90,8 @@
         pw.println("      Set the value of a CEC setting");
         pw.println("  setsystemaudiomode, setsam [on|off]");
         pw.println("      Sets the System Audio Mode feature on or off on TV devices");
+        pw.println("  setarc [on|off]");
+        pw.println("      Sets the ARC feature on or off on TV devices");
     }
 
     private int handleShellCommand(String cmd) throws RemoteException {
@@ -106,6 +108,8 @@
             case "setsystemaudiomode":
             case "setsam":
                 return setSystemAudioMode(pw);
+            case "setarc":
+                return setArcMode(pw);
         }
 
         getErrPrintWriter().println("Unhandled command: " + cmd);
@@ -229,6 +233,27 @@
         return mCecResult.get() == HdmiControlManager.RESULT_SUCCESS ? 0 : 1;
     }
 
+    private int setArcMode(PrintWriter pw) throws RemoteException {
+        if (1 > getRemainingArgsCount()) {
+            throw new IllegalArgumentException(
+                    "Please indicate if ARC mode should be turned \"on\" or \"off\".");
+        }
+
+        String arg = getNextArg();
+        if (arg.equals("on")) {
+            pw.println("Setting ARC mode on");
+            mBinderService.setArcMode(true);
+        } else if (arg.equals("off")) {
+            pw.println("Setting ARC mode off");
+            mBinderService.setArcMode(false);
+        } else {
+            throw new IllegalArgumentException(
+                    "Please indicate if ARC mode should be turned \"on\" or \"off\".");
+        }
+
+        return 0;
+    }
+
     private boolean receiveCallback(String command) {
         try {
             if (!mLatch.await(HdmiConfig.TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
diff --git a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
index 5abc438..2bdeab4 100644
--- a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
+++ b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
@@ -98,19 +98,7 @@
         }
 
         @Override
-        public void onChallengeInterrupted(int sensorId) {
-            Slog.w(TAG, "Challenge interrupted, sensor: " + sensorId);
-            // Consider re-attempting generateChallenge/resetLockout/revokeChallenge
-            // when onChallengeInterruptFinished is invoked
-        }
-
-        @Override
-        public void onChallengeInterruptFinished(int sensorId) {
-            Slog.w(TAG, "Challenge interrupt finished, sensor: " + sensorId);
-        }
-
-        @Override
-        public void onGenerateChallengeResult(int sensorId, long challenge) {
+        public void onGenerateChallengeResult(int sensorId, int userId, long challenge) {
             if (!sensorIds.contains(sensorId)) {
                 Slog.e(TAG, "Unknown sensorId received: " + sensorId);
                 return;
@@ -128,10 +116,6 @@
             }
 
             sensorIds.remove(sensorId);
-            // Challenge is only required for IBiometricsFace@1.0 (and not IFace AIDL). The
-            // IBiometricsFace@1.0 HAL does not require userId to revokeChallenge, so passing
-            // in 0 is OK.
-            final int userId = 0;
             faceManager.revokeChallenge(sensorId, userId, challenge);
 
             if (sensorIds.isEmpty()) {
@@ -234,18 +218,12 @@
         }
     }
 
-    /**
-     * For devices on {@link android.hardware.biometrics.face.V1_0} which only support a single
-     * in-flight challenge, we generate a single challenge to reset lockout for all profiles. This
-     * hopefully reduces/eliminates issues such as overwritten challenge, incorrectly revoked
-     * challenge, or other race conditions.
-     */
     private void processPendingLockoutsForFace(List<UserAuthInfo> pendingResetLockouts) {
         if (mFaceManager != null) {
             if (mFaceResetLockoutTask != null) {
                 // This code will need to be updated if this problem ever occurs.
-                Slog.w(TAG, "mFaceGenerateChallengeCallback not null, previous operation may be"
-                        + " stuck");
+                Slog.w(TAG,
+                        "mFaceGenerateChallengeCallback not null, previous operation may be stuck");
             }
             final List<FaceSensorPropertiesInternal> faceSensorProperties =
                     mFaceManager.getSensorPropertiesInternal();
@@ -258,12 +236,13 @@
                     mSpManager, sensorIds, pendingResetLockouts);
             for (final FaceSensorPropertiesInternal prop : faceSensorProperties) {
                 if (prop.resetLockoutRequiresHardwareAuthToken) {
-                    if (prop.resetLockoutRequiresChallenge) {
-                        // Generate a challenge for each sensor. The challenge does not need to be
-                        // per-user, since the HAT returned by gatekeeper contains userId.
-                        mFaceManager.generateChallenge(prop.sensorId, mFaceResetLockoutTask);
-                    } else {
-                        for (UserAuthInfo user : pendingResetLockouts) {
+                    for (UserAuthInfo user : pendingResetLockouts) {
+                        if (prop.resetLockoutRequiresChallenge) {
+                            Slog.d(TAG, "Generating challenge for sensor: " + prop.sensorId
+                                    + ", user: " + user.userId);
+                            mFaceManager.generateChallenge(prop.sensorId, user.userId,
+                                    mFaceResetLockoutTask);
+                        } else {
                             Slog.d(TAG, "Resetting face lockout for sensor: " + prop.sensorId
                                     + ", user: " + user.userId);
                             final byte[] hat = requestHatFromGatekeeperPassword(mSpManager, user,
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 805f395..8c1fd36 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1511,7 +1511,8 @@
 
                 builder.setSmallIcon(R.drawable.stat_notify_error);
 
-                final Intent snoozeIntent = buildSnoozeWarningIntent(policy.template);
+                final Intent snoozeIntent = buildSnoozeWarningIntent(policy.template,
+                        mContext.getPackageName());
                 builder.setDeleteIntent(PendingIntent.getBroadcast(
                         mContext, 0, snoozeIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
 
@@ -1597,7 +1598,8 @@
 
                 builder.setSmallIcon(R.drawable.stat_notify_error);
 
-                final Intent snoozeIntent = buildSnoozeRapidIntent(policy.template);
+                final Intent snoozeIntent = buildSnoozeRapidIntent(policy.template,
+                        mContext.getPackageName());
                 builder.setDeleteIntent(PendingIntent.getBroadcast(
                         mContext, 0, snoozeIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
 
@@ -5478,17 +5480,19 @@
         return new Intent(ACTION_ALLOW_BACKGROUND);
     }
 
-    private static Intent buildSnoozeWarningIntent(NetworkTemplate template) {
+    private static Intent buildSnoozeWarningIntent(NetworkTemplate template, String targetPackage) {
         final Intent intent = new Intent(ACTION_SNOOZE_WARNING);
         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         intent.putExtra(EXTRA_NETWORK_TEMPLATE, template);
+        intent.setPackage(targetPackage);
         return intent;
     }
 
-    private static Intent buildSnoozeRapidIntent(NetworkTemplate template) {
+    private static Intent buildSnoozeRapidIntent(NetworkTemplate template, String targetPackage) {
         final Intent intent = new Intent(ACTION_SNOOZE_RAPID);
         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         intent.putExtra(EXTRA_NETWORK_TEMPLATE, template);
+        intent.setPackage(targetPackage);
         return intent;
     }
 
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 202b315..a3daae4 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -101,6 +101,7 @@
     protected static final String ENABLED_SERVICES_SEPARATOR = ":";
     private static final String DB_VERSION_1 = "1";
     private static final String DB_VERSION_2 = "2";
+    private static final String DB_VERSION_3 = "3";
 
 
     /**
@@ -113,8 +114,9 @@
     static final String ATT_VERSION = "version";
     static final String ATT_DEFAULTS = "defaults";
     static final String ATT_USER_SET = "user_set_services";
+    static final String ATT_USER_CHANGED = "user_changed";
 
-    static final int DB_VERSION = 3;
+    static final int DB_VERSION = 4;
 
     static final int APPROVAL_BY_PACKAGE = 0;
     static final int APPROVAL_BY_COMPONENT = 1;
@@ -160,6 +162,8 @@
     @GuardedBy("mApproved")
     protected ArrayMap<Integer, ArraySet<String>> mUserSetServices = new ArrayMap<>();
 
+    protected ArrayMap<Integer, Boolean> mIsUserChanged = new ArrayMap<>();
+
     // True if approved services are stored in xml, not settings.
     private boolean mUseXml;
 
@@ -338,6 +342,7 @@
             for (int i = 0; i < N; i++) {
                 final int userId = mApproved.keyAt(i);
                 final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.valueAt(i);
+                final Boolean userChanged = mIsUserChanged.get(userId);
                 if (approvedByType != null) {
                     final int M = approvedByType.size();
                     for (int j = 0; j < M; j++) {
@@ -345,16 +350,20 @@
                         final ArraySet<String> approved = approvedByType.valueAt(j);
                         if (approvedByType != null && approvedByType.size() > 0) {
                             pw.println("      " + String.join(ENABLED_SERVICES_SEPARATOR, approved)
-                                    + " (user: " + userId + " isPrimary: " + isPrimary + ")");
+                                    + " (user: " + userId + " isPrimary: " + isPrimary
+                                    + (userChanged == null ? "" : " isUserChanged: "
+                                    + userChanged) + ")");
                         }
                     }
                 }
             }
-
             pw.println("    Has user set:");
             Set<Integer> userIds = mUserSetServices.keySet();
             for (int userId : userIds) {
-                pw.println("      userId=" + userId + " value=" + mUserSetServices.get(userId));
+                if (mIsUserChanged.get(userId) == null) {
+                    pw.println("      userId=" + userId + " value="
+                            + (mUserSetServices.get(userId)));
+                }
             }
         }
 
@@ -489,13 +498,14 @@
                     continue;
                 }
                 final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.valueAt(i);
+                final Boolean isUserChanged = mIsUserChanged.get(approvedUserId);
                 if (approvedByType != null) {
                     final int M = approvedByType.size();
                     for (int j = 0; j < M; j++) {
                         final boolean isPrimary = approvedByType.keyAt(j);
                         final Set<String> approved = approvedByType.valueAt(j);
                         final Set<String> userSet = mUserSetServices.get(approvedUserId);
-                        if (approved != null || userSet != null) {
+                        if (approved != null || userSet != null || isUserChanged != null) {
                             String allowedItems = approved == null
                                     ? ""
                                     : String.join(ENABLED_SERVICES_SEPARATOR, approved);
@@ -503,7 +513,9 @@
                             out.attribute(null, ATT_APPROVED_LIST, allowedItems);
                             out.attributeInt(null, ATT_USER_ID, approvedUserId);
                             out.attributeBoolean(null, ATT_IS_PRIMARY, isPrimary);
-                            if (userSet != null) {
+                            if (isUserChanged != null) {
+                                out.attributeBoolean(null, ATT_USER_CHANGED, isUserChanged);
+                            } else if (userSet != null) {
                                 String userSetItems =
                                         String.join(ENABLED_SERVICES_SEPARATOR, userSet);
                                 out.attribute(null, ATT_USER_SET, userSetItems);
@@ -618,12 +630,21 @@
                             ? userId : parser.getAttributeInt(null, ATT_USER_ID, 0);
                     final boolean isPrimary =
                             parser.getAttributeBoolean(null, ATT_IS_PRIMARY, true);
-                    final String userSet = XmlUtils.readStringAttribute(parser, ATT_USER_SET);
+
+                    final String isUserChanged = XmlUtils.readStringAttribute(parser,
+                            ATT_USER_CHANGED);
+                    String userSetComponent = null;
+                    if (isUserChanged == null) {
+                        userSetComponent = XmlUtils.readStringAttribute(parser, ATT_USER_SET);
+                    } else {
+                        mIsUserChanged.put(resolvedUserId, Boolean.valueOf(isUserChanged));
+                    }
                     readExtraAttributes(tag, parser, resolvedUserId);
                     if (allowedManagedServicePackages == null || allowedManagedServicePackages.test(
-                            getPackageName(approved), resolvedUserId, getRequiredPermission())) {
+                            getPackageName(approved), resolvedUserId, getRequiredPermission())
+                            || approved.isEmpty()) {
                         if (mUm.getUserInfo(resolvedUserId) != null) {
-                            addApprovedList(approved, resolvedUserId, isPrimary, userSet);
+                            addApprovedList(approved, resolvedUserId, isPrimary, userSetComponent);
                         }
                         mUseXml = true;
                     }
@@ -634,10 +655,16 @@
         }
         boolean isOldVersion = TextUtils.isEmpty(version)
                 || DB_VERSION_1.equals(version)
-                || DB_VERSION_2.equals(version);
+                || DB_VERSION_2.equals(version)
+                || DB_VERSION_3.equals(version);
+        boolean needUpgradeUserset = DB_VERSION_3.equals(version);
         if (isOldVersion) {
             upgradeDefaultsXmlVersion();
         }
+        if (needUpgradeUserset) {
+            upgradeUserSet();
+        }
+
         rebindServices(false, USER_ALL);
     }
 
@@ -666,6 +693,8 @@
         }
     }
 
+    protected void upgradeUserSet() {};
+
     /**
      * Read extra attributes in the {@link #TAG_MANAGED_SERVICES} tag.
      */
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 67fd09b3..19c717d 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -106,7 +106,6 @@
 import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 
-import static com.android.internal.util.CollectionUtils.emptyIfNull;
 import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
@@ -324,7 +323,6 @@
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.Set;
@@ -832,7 +830,7 @@
                 learnNASPendingIntent).build();
 
 
-        return new Notification.Builder(getContext(), SystemNotificationChannels.ALERTS)
+        return new Notification.Builder(getContext(), SystemNotificationChannels.SYSTEM_CHANGES)
                 .setAutoCancel(false)
                 .setOngoing(true)
                 .setTicker(getContext().getResources().getString(title))
@@ -9467,6 +9465,27 @@
             return mDefaultFromConfig;
         }
 
+        @Override
+        protected void upgradeUserSet() {
+            for (int userId: mApproved.keySet()) {
+                ArraySet<String> userSetServices = mUserSetServices.get(userId);
+                mIsUserChanged.put(userId, (userSetServices != null && userSetServices.size() > 0));
+            }
+        }
+
+        @Override
+        protected void addApprovedList(String approved, int userId, boolean isPrimary,
+                String userSet) {
+            if (!TextUtils.isEmpty(approved)) {
+                String[] approvedArray = approved.split(ENABLED_SERVICES_SEPARATOR);
+                if (approvedArray.length > 1) {
+                    Slog.d(TAG, "More than one approved assistants");
+                    approved = approvedArray[0];
+                }
+            }
+            super.addApprovedList(approved, userId, isPrimary, userSet);
+        }
+
         public NotificationAssistants(Context context, Object lock, UserProfiles up,
                 IPackageManager pm) {
             super(context, lock, up, pm);
@@ -9641,47 +9660,12 @@
         }
 
         boolean hasUserSet(int userId) {
-            synchronized (mLock) {
-                ArraySet<String> userSetServices = mUserSetServices.get(userId);
-                if (userSetServices == null) {
-                    // Legacy case - no data means user-set, unless no assistant is set
-                    return !mApproved.isEmpty();
-                }
-                Map<Boolean, ArraySet<String>> approvedByType = emptyIfNull(mApproved.get(userId));
-                return userSetServices.containsAll(emptyIfNull(approvedByType.get(true)))
-                        && userSetServices.containsAll(emptyIfNull(approvedByType.get(false)));
-            }
+            Boolean userSet = mIsUserChanged.get(userId);
+            return (userSet != null && userSet);
         }
 
         void setUserSet(int userId, boolean set) {
-            synchronized (mLock) {
-                ArraySet<String> userSetServices = new ArraySet<>();
-                if (set) {
-                    ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(userId);
-                    if (approvedByType != null) {
-                        for (int i = 0; i < approvedByType.size(); i++) {
-                            userSetServices.addAll(approvedByType.valueAt(i));
-                        }
-                    }
-                }
-                mUserSetServices.put(userId, userSetServices);
-            }
-        }
-
-        @Override
-        protected void readExtraAttributes(String tag, TypedXmlPullParser parser, int userId)
-                throws IOException {
-            // TODO: this logic looks broken, since it's trying to convert a
-            // list into a boolean; for now we preserve the old parsing behavior
-            // to avoid a performance regression, but someone should investigate
-            final String value = parser.getAttributeValue(null, ATT_USER_SET);
-            final boolean userSet;
-            if (TextUtils.isEmpty(value)) {
-                userSet = false;
-            } else {
-                userSet = Boolean.parseBoolean(value);
-            }
-            setUserSet(userId, userSet);
+            mIsUserChanged.put(userId, set);
         }
 
         private void notifyCapabilitiesChanged(final ManagedServiceInfo info) {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 7f18c4b..20f35f2 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -2254,24 +2254,6 @@
     }
 
     @Override
-    public AndroidFuture updateShortcutVisibility(String callingPkg, String packageName,
-            byte[] certificate, boolean visible, int userId) {
-        final AndroidFuture<Void> ret = new AndroidFuture<>();
-        injectPostToHandlerIfAppSearch(() -> {
-            try {
-                synchronized (mLock) {
-                    getPackageShortcutsForPublisherLocked(callingPkg, userId)
-                            .updateVisibility(packageName, certificate, visible);
-                }
-                ret.complete(null);
-            } catch (Exception e) {
-                ret.completeExceptionally(e);
-            }
-        });
-        return ret;
-    }
-
-    @Override
     public AndroidFuture requestPinShortcut(String packageName, ShortcutInfo shortcut,
             IntentSender resultIntent, int userId) {
         final AndroidFuture<Boolean> ret = new AndroidFuture<>();
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 90a3c58..f86eb60 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -94,6 +94,9 @@
           "include-filter": "com.android.server.pm.test.OverlayActorVisibilityTest"
         }
       ]
+    },
+    {
+      "name": "CtsPackageManagerBootTestCases"
     }
   ],
   "postsubmit": [
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 206f67e..8800a9e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1562,6 +1562,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.
+            return false;
+        }
         final int rotation = rotationForActivityInDifferentOrientation(r);
         if (rotation == ROTATION_UNDEFINED) {
             // The display rotation won't be changed by current top activity. The client side
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index fb481b4..a4c8db6 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -38,6 +38,7 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 import android.view.SurfaceControl;
@@ -75,6 +76,9 @@
     private static final int REPORT_CONFIGS = CONTROLLABLE_CONFIGS;
     private static final int REPORT_WINDOW_CONFIGS = CONTROLLABLE_WINDOW_CONFIGS;
 
+    private static final boolean DEBUG_ENABLE_REVEAL_ANIMATION =
+            SystemProperties.getBoolean("persist.debug.enable_reveal_animation", false);
+
     // The set of modes that are currently supports
     // TODO: Remove once the task organizer can support all modes
     @VisibleForTesting
@@ -183,7 +187,7 @@
             SurfaceControl windowAnimationLeash = null;
             Rect mainFrame = null;
             final boolean playShiftUpAnimation = !task.inMultiWindowMode();
-            if (prepareAnimation && playShiftUpAnimation) {
+            if (prepareAnimation && playShiftUpAnimation && DEBUG_ENABLE_REVEAL_ANIMATION) {
                 final ActivityRecord topActivity = task.topActivityWithStartingWindow();
                 if (topActivity != null) {
                     final WindowState mainWindow =
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 51b270c..24699d9 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -983,7 +983,8 @@
     bool enableReadTimeouts = ifs.readTimeoutsRequested();
 
     std::lock_guard l(mMountOperationLock);
-    auto status = mVold->setIncFsMountOptions(control, enableReadLogs, enableReadTimeouts);
+    auto status = mVold->setIncFsMountOptions(control, enableReadLogs, enableReadTimeouts,
+                                              ifs.metricsKey);
     if (status.isOk()) {
         // Store states.
         ifs.setReadLogsEnabled(enableReadLogs);
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index 68a28b2..ce3d514 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -56,8 +56,10 @@
     }
     binder::Status setIncFsMountOptions(
             const ::android::os::incremental::IncrementalFileSystemControlParcel& control,
-            bool enableReadLogs, bool enableReadTimeouts) const final {
-        return mInterface->setIncFsMountOptions(control, enableReadLogs, enableReadTimeouts);
+            bool enableReadLogs, bool enableReadTimeouts,
+            const std::string& sysfsName) const final {
+        return mInterface->setIncFsMountOptions(control, enableReadLogs, enableReadTimeouts,
+                                                sysfsName);
     }
 
 private:
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index c0ef7ba..39e2ee3 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -58,7 +58,7 @@
                                      const std::string& targetDir) const = 0;
     virtual binder::Status setIncFsMountOptions(
             const os::incremental::IncrementalFileSystemControlParcel& control, bool enableReadLogs,
-            bool enableReadTimeouts) const = 0;
+            bool enableReadTimeouts, const std::string& sysfsName) const = 0;
 };
 
 class DataLoaderManagerWrapper {
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 6c9310b..fae654e 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -56,10 +56,10 @@
     MOCK_CONST_METHOD1(unmountIncFs, binder::Status(const std::string& dir));
     MOCK_CONST_METHOD2(bindMount,
                        binder::Status(const std::string& sourceDir, const std::string& argetDir));
-    MOCK_CONST_METHOD3(
+    MOCK_CONST_METHOD4(
             setIncFsMountOptions,
             binder::Status(const ::android::os::incremental::IncrementalFileSystemControlParcel&,
-                           bool, bool));
+                           bool, bool, const std::string&));
 
     void mountIncFsFails() {
         ON_CALL(*this, mountIncFs(_, _, _, _, _))
@@ -83,12 +83,12 @@
         ON_CALL(*this, bindMount(_, _)).WillByDefault(Return(binder::Status::ok()));
     }
     void setIncFsMountOptionsFails() const {
-        ON_CALL(*this, setIncFsMountOptions(_, _, _))
+        ON_CALL(*this, setIncFsMountOptions(_, _, _, _))
                 .WillByDefault(Return(
                         binder::Status::fromExceptionCode(1, String8("failed to set options"))));
     }
     void setIncFsMountOptionsSuccess() {
-        ON_CALL(*this, setIncFsMountOptions(_, _, _))
+        ON_CALL(*this, setIncFsMountOptions(_, _, _, _))
                 .WillByDefault(Invoke(this, &MockVoldService::setIncFsMountOptionsOk));
     }
     binder::Status getInvalidControlParcel(const std::string& imagePath,
@@ -108,7 +108,7 @@
     }
     binder::Status setIncFsMountOptionsOk(
             const ::android::os::incremental::IncrementalFileSystemControlParcel& control,
-            bool enableReadLogs, bool enableReadTimeouts) {
+            bool enableReadLogs, bool enableReadTimeouts, const std::string& sysfsName) {
         mReadLogsEnabled = enableReadLogs;
         mReadTimeoutsEnabled = enableReadTimeouts;
         return binder::Status::ok();
@@ -1451,9 +1451,9 @@
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // on startLoading
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _, _)).Times(1);
     // We are calling setIncFsMountOptions(true).
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(1);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _, _)).Times(1);
     // After setIncFsMountOptions succeeded expecting to start watching.
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
     // Not expecting callback removal.
@@ -1475,8 +1475,8 @@
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // Enabling and then disabling readlogs.
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(1);
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(2);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _, _)).Times(1);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _, _)).Times(2);
     // After setIncFsMountOptions succeeded expecting to start watching.
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
     // Not expecting callback removal.
@@ -1503,8 +1503,8 @@
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // Enabling and then disabling readlogs.
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(2);
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(2);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _, _)).Times(2);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _, _)).Times(2);
     // After setIncFsMountOptions succeeded expecting to start watching.
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
     // Not expecting callback removal.
@@ -1544,8 +1544,8 @@
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // Enabling and then disabling readlogs.
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(3);
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _, _)).Times(3);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _, _)).Times(1);
     // After setIncFsMountOptions succeeded expecting to start watching.
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
     // Not expecting callback removal.
@@ -1585,8 +1585,8 @@
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(2);
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // Enabling and then disabling readlogs.
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(5);
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(3);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _, _)).Times(5);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _, _)).Times(3);
     // After setIncFsMountOptions succeeded expecting to start watching.
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
     // Not expecting callback removal.
@@ -1660,9 +1660,9 @@
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // We are calling setIncFsMountOptions(true).
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(1);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _, _)).Times(1);
     // setIncFsMountOptions(false) is called on the callback.
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(2);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _, _)).Times(2);
     // After setIncFsMountOptions succeeded expecting to start watching.
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
     // After callback is called, disable read logs and remove callback.
@@ -1685,8 +1685,8 @@
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // checkPermission fails, no calls to set opitions,  start or stop WatchingMode.
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(0);
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _, _)).Times(0);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _, _)).Times(1);
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0);
     EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
     TemporaryDir tempDir;
@@ -1705,8 +1705,8 @@
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // checkPermission fails, no calls to set opitions,  start or stop WatchingMode.
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(0);
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _, _)).Times(0);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _, _)).Times(1);
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0);
     EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
     TemporaryDir tempDir;
@@ -1726,8 +1726,8 @@
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // We are calling setIncFsMountOptions.
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(1);
-    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _, _)).Times(1);
+    EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _, _)).Times(1);
     // setIncFsMountOptions fails, no calls to start or stop WatchingMode.
     EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0);
     EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
diff --git a/services/net/TEST_MAPPING b/services/net/TEST_MAPPING
index 7025dd1..7eca1f9 100644
--- a/services/net/TEST_MAPPING
+++ b/services/net/TEST_MAPPING
@@ -2,6 +2,9 @@
   "imports": [
     {
       "path": "frameworks/base/core/java/android/net"
+    },
+    {
+      "path": "packages/modules/Wifi/framework"
     }
   ]
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
index 8fe1139..d3feb12 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
@@ -75,10 +75,14 @@
                                         .setPropertyName(propertyKeyString)
                                         .addSnippetMatches(
                                                 SnippetMatchProto.newBuilder()
-                                                        .setExactMatchPosition(29)
-                                                        .setExactMatchBytes(3)
-                                                        .setWindowPosition(26)
-                                                        .setWindowBytes(6)))
+                                                        .setExactMatchBytePosition(29)
+                                                        .setExactMatchByteLength(3)
+                                                        .setExactMatchUtf16Position(29)
+                                                        .setExactMatchUtf16Length(3)
+                                                        .setWindowBytePosition(26)
+                                                        .setWindowByteLength(6)
+                                                        .setWindowUtf16Position(26)
+                                                        .setWindowUtf16Length(6)))
                         .build();
         SearchResultProto searchResultProto =
                 SearchResultProto.newBuilder()
@@ -168,19 +172,27 @@
                                         .setPropertyName("senderName")
                                         .addSnippetMatches(
                                                 SnippetMatchProto.newBuilder()
-                                                        .setExactMatchPosition(0)
-                                                        .setExactMatchBytes(4)
-                                                        .setWindowPosition(0)
-                                                        .setWindowBytes(9)))
+                                                        .setExactMatchBytePosition(0)
+                                                        .setExactMatchByteLength(4)
+                                                        .setExactMatchUtf16Position(0)
+                                                        .setExactMatchUtf16Length(4)
+                                                        .setWindowBytePosition(0)
+                                                        .setWindowByteLength(9)
+                                                        .setWindowUtf16Position(0)
+                                                        .setWindowUtf16Length(9)))
                         .addEntries(
                                 SnippetProto.EntryProto.newBuilder()
                                         .setPropertyName("senderEmail")
                                         .addSnippetMatches(
                                                 SnippetMatchProto.newBuilder()
-                                                        .setExactMatchPosition(0)
-                                                        .setExactMatchBytes(20)
-                                                        .setWindowPosition(0)
-                                                        .setWindowBytes(20)))
+                                                        .setExactMatchBytePosition(0)
+                                                        .setExactMatchByteLength(20)
+                                                        .setExactMatchUtf16Position(0)
+                                                        .setExactMatchUtf16Length(20)
+                                                        .setWindowBytePosition(0)
+                                                        .setWindowByteLength(20)
+                                                        .setWindowUtf16Position(0)
+                                                        .setWindowUtf16Length(20)))
                         .build();
         SearchResultProto searchResultProto =
                 SearchResultProto.newBuilder()
@@ -251,19 +263,27 @@
                                         .setPropertyName("sender.name")
                                         .addSnippetMatches(
                                                 SnippetMatchProto.newBuilder()
-                                                        .setExactMatchPosition(0)
-                                                        .setExactMatchBytes(4)
-                                                        .setWindowPosition(0)
-                                                        .setWindowBytes(9)))
+                                                        .setExactMatchBytePosition(0)
+                                                        .setExactMatchByteLength(4)
+                                                        .setExactMatchUtf16Position(0)
+                                                        .setExactMatchUtf16Length(4)
+                                                        .setWindowBytePosition(0)
+                                                        .setWindowByteLength(9)
+                                                        .setWindowUtf16Position(0)
+                                                        .setWindowUtf16Length(9)))
                         .addEntries(
                                 SnippetProto.EntryProto.newBuilder()
                                         .setPropertyName("sender.email[1]")
                                         .addSnippetMatches(
                                                 SnippetMatchProto.newBuilder()
-                                                        .setExactMatchPosition(0)
-                                                        .setExactMatchBytes(21)
-                                                        .setWindowPosition(0)
-                                                        .setWindowBytes(21)))
+                                                        .setExactMatchBytePosition(0)
+                                                        .setExactMatchByteLength(21)
+                                                        .setExactMatchUtf16Position(0)
+                                                        .setExactMatchUtf16Length(21)
+                                                        .setWindowBytePosition(0)
+                                                        .setWindowByteLength(21)
+                                                        .setWindowUtf16Position(0)
+                                                        .setWindowUtf16Length(21)))
                         .build();
         SearchResultProto searchResultProto =
                 SearchResultProto.newBuilder()
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java b/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java
index 5de8a7a..217430c 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java
@@ -89,7 +89,7 @@
                 CallStats.CALL_TYPE_INITIALIZE).mSamplingRatio).isEqualTo(
                 TEST_DEFAULT_SAMPLING_RATIO);
         assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
-                CallStats.CALL_TYPE_QUERY).mSamplingRatio).isEqualTo(
+                CallStats.CALL_TYPE_SEARCH).mSamplingRatio).isEqualTo(
                 TEST_DEFAULT_SAMPLING_RATIO);
         assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
                 CallStats.CALL_TYPE_FLUSH).mSamplingRatio).isEqualTo(
@@ -103,7 +103,7 @@
         int querySamplingRatio = 2;
         final SparseIntArray samplingRatios = new SparseIntArray();
         samplingRatios.put(CallStats.CALL_TYPE_PUT_DOCUMENT, putDocumentSamplingRatio);
-        samplingRatios.put(CallStats.CALL_TYPE_QUERY, querySamplingRatio);
+        samplingRatios.put(CallStats.CALL_TYPE_SEARCH, querySamplingRatio);
         PlatformLogger logger = new PlatformLogger(
                 ApplicationProvider.getApplicationContext(),
                 UserHandle.USER_NULL,
@@ -127,7 +127,7 @@
                 CallStats.CALL_TYPE_PUT_DOCUMENT).mSamplingRatio).isEqualTo(
                 putDocumentSamplingRatio);
         assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME,
-                CallStats.CALL_TYPE_QUERY).mSamplingRatio).isEqualTo(
+                CallStats.CALL_TYPE_SEARCH).mSamplingRatio).isEqualTo(
                 querySamplingRatio);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
index 0b59be6..39c51d5 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/Face10Test.java
@@ -18,6 +18,10 @@
 
 import static junit.framework.Assert.assertEquals;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.isA;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -26,6 +30,7 @@
 import android.hardware.biometrics.SensorProperties;
 import android.hardware.face.FaceSensorProperties;
 import android.hardware.face.FaceSensorPropertiesInternal;
+import android.hardware.face.IFaceServiceReceiver;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.UserManager;
@@ -42,8 +47,12 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.time.Clock;
+import java.time.Instant;
+import java.time.ZoneId;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.IntStream;
 
 @Presubmit
 @SmallTest
@@ -51,6 +60,7 @@
 
     private static final String TAG = "Face10Test";
     private static final int SENSOR_ID = 1;
+    private static final int USER_ID = 20;
 
     @Mock
     private Context mContext;
@@ -86,10 +96,18 @@
                 FaceSensorProperties.TYPE_UNKNOWN, supportsFaceDetection, supportsSelfIllumination,
                 resetLockoutRequiresChallenge);
 
+        Face10.sSystemClock = Clock.fixed(Instant.ofEpochMilli(100), ZoneId.of("PST"));
         mFace10 = new Face10(mContext, sensorProps, mLockoutResetDispatcher, mScheduler);
         mBinder = new Binder();
     }
 
+    private void tick(long seconds) {
+        waitForIdle();
+        Face10.sSystemClock = Clock.fixed(Instant.ofEpochSecond(
+                Face10.sSystemClock.instant().getEpochSecond() + seconds),
+                ZoneId.of("PST"));
+    }
+
     @Test
     public void getAuthenticatorId_doesNotCrashWhenIdNotFound() {
         assertEquals(0, mFace10.getAuthenticatorId(0 /* sensorId */, 111 /* userId */));
@@ -104,6 +122,59 @@
     }
 
     @Test
+    public void scheduleGenerateChallenge_cachesResult() {
+        final IFaceServiceReceiver[] mocks = IntStream.range(0, 3)
+                .mapToObj(i -> mock(IFaceServiceReceiver.class))
+                .toArray(IFaceServiceReceiver[]::new);
+        for (IFaceServiceReceiver mock : mocks) {
+            mFace10.scheduleGenerateChallenge(SENSOR_ID, USER_ID, mBinder, mock, TAG);
+            tick(10);
+        }
+        tick(120);
+        mFace10.scheduleGenerateChallenge(
+                SENSOR_ID, USER_ID, mBinder, mock(IFaceServiceReceiver.class), TAG);
+        waitForIdle();
+
+        verify(mScheduler, times(2))
+                .scheduleClientMonitor(isA(FaceGenerateChallengeClient.class), any());
+    }
+
+    @Test
+    public void scheduleRevokeChallenge_waitsUntilEmpty() {
+        final long challenge = 22;
+        final IFaceServiceReceiver[] mocks = IntStream.range(0, 3)
+                .mapToObj(i -> mock(IFaceServiceReceiver.class))
+                .toArray(IFaceServiceReceiver[]::new);
+        for (IFaceServiceReceiver mock : mocks) {
+            mFace10.scheduleGenerateChallenge(SENSOR_ID, USER_ID, mBinder, mock, TAG);
+            tick(10);
+        }
+        for (IFaceServiceReceiver mock : mocks) {
+            mFace10.scheduleRevokeChallenge(SENSOR_ID, USER_ID, mBinder, TAG, challenge);
+            tick(10);
+        }
+        waitForIdle();
+
+        verify(mScheduler).scheduleClientMonitor(isA(FaceRevokeChallengeClient.class), any());
+    }
+
+    @Test
+    public void scheduleRevokeChallenge_doesNotWaitForever() {
+        mFace10.scheduleGenerateChallenge(
+                SENSOR_ID, USER_ID, mBinder, mock(IFaceServiceReceiver.class), TAG);
+        mFace10.scheduleGenerateChallenge(
+                SENSOR_ID, USER_ID, mBinder, mock(IFaceServiceReceiver.class), TAG);
+        tick(10000);
+        mFace10.scheduleGenerateChallenge(
+                SENSOR_ID, USER_ID, mBinder, mock(IFaceServiceReceiver.class), TAG);
+        mFace10.scheduleRevokeChallenge(
+                SENSOR_ID, USER_ID, mBinder, TAG, 8 /* challenge */);
+        waitForIdle();
+
+        verify(mScheduler).scheduleClientMonitor(isA(FaceRevokeChallengeClient.class), any());
+    }
+
+    @Test
     public void halServiceDied_resetsScheduler() {
         // It's difficult to test the linkToDeath --> serviceDied path, so let's just invoke
         // serviceDied directly.
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java
new file mode 100644
index 0000000..55dc035
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.biometrics.sensors.face.hidl;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.biometrics.face.V1_0.OptionalUint64;
+import android.hardware.face.IFaceServiceReceiver;
+import android.os.Binder;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@Presubmit
+@SmallTest
+public class FaceGenerateChallengeClientTest {
+
+    private static final String TAG = "FaceGenerateChallengeClientTest";
+    private static final int USER_ID = 2;
+    private static final int SENSOR_ID = 4;
+    private static final long START_TIME = 5000;
+    private static final long CHALLENGE = 200;
+
+    private final Context mContext = ApplicationProvider.getApplicationContext();
+
+    @Mock
+    private IBiometricsFace mIBiometricsFace;
+    @Mock
+    private IFaceServiceReceiver mClientReceiver;
+    @Mock
+    private IFaceServiceReceiver mOtherReceiver;
+    @Mock
+    private BaseClientMonitor.Callback mMonitorCallback;
+
+    private FaceGenerateChallengeClient mClient;
+
+    @Before
+    public void setup() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        final OptionalUint64 challenge = new OptionalUint64();
+        challenge.value = CHALLENGE;
+        when(mIBiometricsFace.generateChallenge(anyInt())).thenReturn(challenge);
+
+        mClient = new FaceGenerateChallengeClient(mContext, () -> mIBiometricsFace, new Binder(),
+                new ClientMonitorCallbackConverter(mClientReceiver), USER_ID,
+                TAG, SENSOR_ID, START_TIME);
+    }
+
+    @Test
+    public void getCreatedAt() {
+        assertEquals(START_TIME, mClient.getCreatedAt());
+    }
+
+    @Test
+    public void reuseResult_whenNotReady() throws Exception {
+        mClient.reuseResult(mOtherReceiver);
+        verify(mOtherReceiver, never()).onChallengeGenerated(anyInt(), anyInt(), anyInt());
+    }
+
+    @Test
+    public void reuseResult_whenReady() throws Exception {
+        mClient.start(mMonitorCallback);
+        mClient.reuseResult(mOtherReceiver);
+        verify(mOtherReceiver).onChallengeGenerated(eq(SENSOR_ID), eq(USER_ID), eq(CHALLENGE));
+    }
+
+    @Test
+    public void reuseResult_whenReallyReady() throws Exception {
+        mClient.reuseResult(mOtherReceiver);
+        mClient.start(mMonitorCallback);
+        verify(mOtherReceiver).onChallengeGenerated(eq(SENSOR_ID), eq(USER_ID), eq(CHALLENGE));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
index c08857c..6cc8d471 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
@@ -68,10 +68,12 @@
 
         mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
 
-        PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
-                mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
-        when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
-        when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+        when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+                new PowerManager(mContextSpy, mIPowerManagerMock,
+                mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
+        when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+                new PowerManager(mContextSpy, mIPowerManagerMock,
+                mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
         when(mIPowerManagerMock.isInteractive()).thenReturn(true);
 
         mHdmiControlService = new HdmiControlService(mContextSpy) {
@@ -97,7 +99,8 @@
 
             @Override
             protected PowerManager getPowerManager() {
-                return powerManager;
+                return new PowerManager(mContextSpy, mIPowerManagerMock,
+                        mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
             }
 
             @Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
index ee9de07..97bd066 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
@@ -70,10 +70,12 @@
 
         mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
 
-        PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
-                mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
-        when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
-        when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+        when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+                new PowerManager(mContextSpy, mIPowerManagerMock,
+                mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
+        when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+                new PowerManager(mContextSpy, mIPowerManagerMock,
+                mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
         when(mIPowerManagerMock.isInteractive()).thenReturn(true);
 
         HdmiControlService hdmiControlService =
@@ -89,7 +91,8 @@
 
                     @Override
                     protected PowerManager getPowerManager() {
-                        return powerManager;
+                        return new PowerManager(mContextSpy, mIPowerManagerMock,
+                                mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
                     }
 
                     @Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
index d5df071..29c9b40 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
@@ -71,10 +71,12 @@
 
         mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
 
-        PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
-                mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
-        when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
-        when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+        when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+                new PowerManager(mContextSpy, mIPowerManagerMock,
+                mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
+        when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+                new PowerManager(mContextSpy, mIPowerManagerMock,
+                mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
         when(mIPowerManagerMock.isInteractive()).thenReturn(true);
 
         HdmiControlService hdmiControlService =
@@ -85,7 +87,8 @@
 
                     @Override
                     protected PowerManager getPowerManager() {
-                        return powerManager;
+                        return new PowerManager(mContextSpy, mIPowerManagerMock,
+                                mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
                     }
 
                     @Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
index 4f97c26..650ffe9 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
@@ -80,10 +80,12 @@
 
         mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
 
-        PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
-                mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
-        when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
-        when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+        when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+                new PowerManager(mContextSpy, mIPowerManagerMock,
+                mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
+        when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+                new PowerManager(mContextSpy, mIPowerManagerMock,
+                mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
         when(mIPowerManagerMock.isInteractive()).thenReturn(true);
 
         mHdmiControlService = new HdmiControlService(mContextSpy) {
@@ -109,7 +111,8 @@
 
             @Override
             protected PowerManager getPowerManager() {
-                return powerManager;
+                return new PowerManager(mContextSpy, mIPowerManagerMock,
+                        mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
             }
 
             @Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java
index 678f8b2..fa5cb67 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionTest.java
@@ -103,8 +103,6 @@
 
         Context context = InstrumentationRegistry.getTargetContext();
         mMyLooper = mTestLooper.getLooper();
-        PowerManager powerManager = new PowerManager(context, mIPowerManagerMock,
-                mIThermalServiceMock, new Handler(mMyLooper));
 
         mHdmiControlService =
                 new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
@@ -129,7 +127,8 @@
 
                     @Override
                     protected PowerManager getPowerManager() {
-                        return powerManager;
+                        return new PowerManager(context, mIPowerManagerMock,
+                                mIThermalServiceMock, new Handler(mMyLooper));
                     }
                 };
 
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
index 45409c8..29f62b5 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
@@ -30,6 +30,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.content.ContextWrapper;
@@ -96,9 +97,10 @@
         mContextSpy = spy(new ContextWrapper(
                 InstrumentationRegistry.getInstrumentation().getTargetContext()));
 
-        PowerManager powerManager = new PowerManager(
-                mContextSpy, mIPowerManagerMock, mIThermalServiceMock, new Handler(mLooper));
-        doReturn(powerManager).when(mContextSpy).getSystemService(Context.POWER_SERVICE);
+
+        when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+                new PowerManager(mContextSpy, mIPowerManagerMock,
+                        mIThermalServiceMock, new Handler(mLooper)));
         doReturn(true).when(mIPowerManagerMock).isInteractive();
 
         mHdmiControlServiceSpy = spy(new HdmiControlService(mContextSpy));
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 38a44c6..7911c40 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -94,8 +94,6 @@
 
         Context context = InstrumentationRegistry.getTargetContext();
         mMyLooper = mTestLooper.getLooper();
-        PowerManager powerManager = new PowerManager(context, mIPowerManagerMock,
-                mIThermalServiceMock, new Handler(mMyLooper));
 
         mHdmiControlService =
             new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
@@ -183,7 +181,8 @@
 
                 @Override
                 protected PowerManager getPowerManager() {
-                    return powerManager;
+                    return new PowerManager(context, mIPowerManagerMock,
+                            mIThermalServiceMock, new Handler(mMyLooper));
                 }
             };
 
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 1ac0150..5fbf8de 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -92,8 +92,6 @@
 
         Context context = InstrumentationRegistry.getTargetContext();
         mMyLooper = mTestLooper.getLooper();
-        PowerManager powerManager = new PowerManager(context, mIPowerManagerMock,
-                mIThermalServiceMock, new Handler(mMyLooper));
 
         mHdmiControlService =
                 new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
@@ -140,7 +138,8 @@
 
                     @Override
                     protected PowerManager getPowerManager() {
-                        return powerManager;
+                        return new PowerManager(context, mIPowerManagerMock,
+                                mIThermalServiceMock, new Handler(mMyLooper));
                     }
                 };
 
@@ -168,6 +167,8 @@
         mPlaybackLogicalAddress = mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress();
         mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
         mNativeWrapper.clearResultMessages();
+        mHdmiCecLocalDevicePlayback.mPlaybackDeviceActionOnRoutingControl =
+                HdmiProperties.playback_device_action_on_routing_control_values.NONE;
     }
 
     @Test
@@ -698,9 +699,12 @@
                 mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
         HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
                 mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+        HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+                mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
 
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
     }
 
     @Test
@@ -720,9 +724,12 @@
                 mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
         HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
                 mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+        HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+                mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
 
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
     }
 
     @Test
@@ -742,9 +749,12 @@
                 mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
         HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
                 mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+        HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+                mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
 
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
     }
 
     @Test
@@ -764,9 +774,12 @@
                 mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
         HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
                 mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+        HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+                mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
 
         assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessageToTv);
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
     }
 
     @Test
@@ -786,9 +799,12 @@
                 mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
         HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
                 mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+        HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+                mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
 
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
         assertThat(mNativeWrapper.getResultMessages()).contains(standbyMessageBroadcast);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(inactiveSource);
     }
 
     @Test
@@ -808,9 +824,37 @@
                 mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
         HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
                 mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+        HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+                mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
 
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
         assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+        assertThat(mNativeWrapper.getResultMessages()).contains(inactiveSource);
+    }
+
+    @Test
+    public void handleOnStandby_CecMessageReceived() {
+        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+                HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+                HdmiControlManager.POWER_CONTROL_MODE_TV);
+        mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+                mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+        mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+                HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED);
+        mHdmiCecLocalDevicePlayback.onStandby(true, HdmiControlService.STANDBY_SCREEN_OFF);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage standbyMessageToTv = HdmiCecMessageBuilder.buildStandby(
+                mHdmiCecLocalDevicePlayback.mAddress, ADDR_TV);
+        HdmiCecMessage standbyMessageBroadcast = HdmiCecMessageBuilder.buildStandby(
+                mHdmiCecLocalDevicePlayback.mAddress, ADDR_BROADCAST);
+        HdmiCecMessage inactiveSource = HdmiCecMessageBuilder.buildInactiveSource(
+                mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
+
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageToTv);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(standbyMessageBroadcast);
+        assertThat(mNativeWrapper.getResultMessages()).contains(inactiveSource);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index 8ee983f..59711a6 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -86,8 +86,6 @@
 
         Context context = InstrumentationRegistry.getTargetContext();
         mMyLooper = mTestLooper.getLooper();
-        PowerManager powerManager = new PowerManager(context, mIPowerManagerMock,
-                mIThermalServiceMock, new Handler(mMyLooper));
 
         mHdmiControlService =
                 new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
@@ -118,7 +116,8 @@
 
                     @Override
                     protected PowerManager getPowerManager() {
-                        return powerManager;
+                        return new PowerManager(context, mIPowerManagerMock,
+                                mIThermalServiceMock, new Handler(mMyLooper));
                     }
 
                     @Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
index 1c7ff42..572ffd9 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
@@ -75,10 +75,12 @@
 
         Context contextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
         Looper myLooper = mTestLooper.getLooper();
-        PowerManager powerManager = new PowerManager(contextSpy, mIPowerManagerMock,
-                mIThermalServiceMock, new Handler(myLooper));
-        when(contextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
-        when(contextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+        when(contextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+                new PowerManager(contextSpy, mIPowerManagerMock,
+                mIThermalServiceMock, new Handler(myLooper)));
+        when(contextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+                new PowerManager(contextSpy, mIPowerManagerMock,
+                mIThermalServiceMock, new Handler(myLooper)));
         when(mIPowerManagerMock.isInteractive()).thenReturn(true);
 
         mHdmiControlService = new HdmiControlService(contextSpy) {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index b1d77d0..bcf30a2 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -192,10 +192,12 @@
 
         mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
 
-        PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
-                mIThermalServiceMock, null);
-        when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
-        when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+        when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+                new PowerManager(mContextSpy, mIPowerManagerMock,
+                mIThermalServiceMock, null));
+        when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+                new PowerManager(mContextSpy, mIPowerManagerMock,
+                mIThermalServiceMock, null));
         when(mIPowerManagerMock.isInteractive()).thenReturn(true);
 
         HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
index 826438f..4cd17e8 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
@@ -91,10 +91,12 @@
 
         setHdmiControlEnabled(hdmiControlEnabled);
 
-        PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
-                mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
-        when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
-        when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+        when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+                new PowerManager(mContextSpy, mIPowerManagerMock,
+                mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
+        when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+                new PowerManager(mContextSpy, mIPowerManagerMock,
+                mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
         when(mIPowerManagerMock.isInteractive()).thenReturn(true);
 
         mHdmiControlService = new HdmiControlService(mContextSpy) {
@@ -120,7 +122,8 @@
 
             @Override
             protected PowerManager getPowerManager() {
-                return powerManager;
+                return new PowerManager(mContextSpy, mIPowerManagerMock,
+                        mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
             }
 
             @Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
index 53b4b49..a9880c0 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
@@ -78,10 +78,12 @@
 
         mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
 
-        PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
-                mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
-        when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
-        when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+        when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+                new PowerManager(mContextSpy, mIPowerManagerMock,
+                mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
+        when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+                new PowerManager(mContextSpy, mIPowerManagerMock,
+                mIThermalServiceMock, new Handler(mTestLooper.getLooper())));
         when(mIPowerManagerMock.isInteractive()).thenReturn(true);
 
         mHdmiControlService = new HdmiControlService(mContextSpy,
@@ -108,7 +110,8 @@
 
             @Override
             protected PowerManager getPowerManager() {
-                return powerManager;
+                return new PowerManager(mContextSpy, mIPowerManagerMock,
+                        mIThermalServiceMock, new Handler(mTestLooper.getLooper()));
             }
 
             @Override
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
index 865eb7a..2cf4ef1 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
@@ -78,10 +78,12 @@
         mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
 
         Looper myLooper = mTestLooper.getLooper();
-        PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
-                mIThermalServiceMock, new Handler(myLooper));
-        when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
-        when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
+        when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenAnswer(i ->
+                new PowerManager(mContextSpy, mIPowerManagerMock,
+                mIThermalServiceMock, new Handler(myLooper)));
+        when(mContextSpy.getSystemService(PowerManager.class)).thenAnswer(i ->
+                new PowerManager(mContextSpy, mIPowerManagerMock,
+                mIThermalServiceMock, new Handler(myLooper)));
         when(mIPowerManagerMock.isInteractive()).thenReturn(true);
 
         mHdmiControlService = new HdmiControlService(mContextSpy) {
@@ -107,7 +109,8 @@
 
             @Override
             protected PowerManager getPowerManager() {
-                return powerManager;
+                return new PowerManager(mContextSpy, mIPowerManagerMock,
+                        mIThermalServiceMock, new Handler(myLooper));
             }
 
             @Override
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
index 478aa41..9598a00 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
@@ -15,20 +15,11 @@
  */
 package com.android.server.pm;
 
-import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI;
-
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
 
 import android.app.PendingIntent;
-import android.app.appsearch.PackageIdentifier;
-import android.content.pm.AppSearchShortcutInfo;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.provider.DeviceConfig;
-
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
-
-import java.util.Random;
 
 /**
  * Tests for {@link android.app.appsearch.AppSearchManager} and relevant APIs in ShortcutManager.
@@ -37,26 +28,6 @@
  */
 public class ShortcutManagerTest12 extends BaseShortcutManagerTest {
 
-    public void testUpdateShortcutVisibility_updatesShortcutSchema() {
-        if (!DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.DARK_LAUNCH_REMOTE_PREDICTION_SERVICE_ENABLED,
-                false)) {
-            // no-op if app-search integration is disabled.
-            return;
-        }
-        final byte[] cert = new byte[20];
-        new Random().nextBytes(cert);
-
-        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
-            mManager.updateShortcutVisibility(CALLING_PACKAGE_2, cert, true);
-            assertTrue(mMockAppSearchManager.mSchemasPackageAccessible.containsKey(
-                    AppSearchShortcutInfo.SCHEMA_TYPE));
-            assertTrue(mMockAppSearchManager.mSchemasPackageAccessible.get(
-                    AppSearchShortcutInfo.SCHEMA_TYPE).get(0).equals(
-                            new PackageIdentifier(CALLING_PACKAGE_2, cert)));
-        });
-    }
-
     public void testGetShortcutIntents_ReturnsMutablePendingIntents() throws RemoteException {
         setDefaultLauncher(USER_0, LAUNCHER_1);
 
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/OWNERS b/services/tests/servicestests/src/com/android/server/power/batterysaver/OWNERS
new file mode 100644
index 0000000..08276f5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/power/batterysaver/OWNERS
\ No newline at end of file
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index c502ed5..825e53e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -100,6 +100,7 @@
     private String mVersionString;
     private final Set<ComponentName> mDefaults = new ArraySet();
     private ManagedServices mService;
+    private String mUserSetString;
 
     private static final String SETTING = "setting";
     private static final String SECONDARY_SETTING = "secondary_setting";
@@ -138,7 +139,7 @@
         profileIds.add(13);
         when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
 
-        mVersionString = "2";
+        mVersionString = "4";
         mExpectedPrimary = new ArrayMap<>();
         mExpectedSecondary = new ArrayMap<>();
         mExpectedPrimaryPackages = new ArrayMap<>();
@@ -1363,6 +1364,7 @@
     public void loadDefaults_noVersionWithDefaults() throws Exception {
         resetComponentsAndPackages();
         mDefaults.add(new ComponentName("default", "class"));
+        mVersionString = null;
         loadXml(mService);
         assertEquals(mService.getDefaultComponents(), mDefaults);
     }
@@ -1421,12 +1423,34 @@
         resetComponentsAndPackages();
         mDefaults.add(new ComponentName("default", "class"));
         mDefaultsString = "xml/class";
-        mVersionString = String.valueOf(mService.DB_VERSION);
         loadXml(mService);
         assertEquals(mService.getDefaultComponents(),
                 new ArraySet(Arrays.asList(new ComponentName("xml", "class"))));
     }
 
+    @Test
+    public void upgradeUserSet_versionThree() throws Exception {
+        resetComponentsAndPackages();
+
+        List<UserInfo> users = new ArrayList<>();
+        users.add(new UserInfo(98, "98", 0));
+        users.add(new UserInfo(99, "99", 0));
+        for (UserInfo user : users) {
+            when(mUm.getUserInfo(eq(user.id))).thenReturn(user);
+        }
+
+        mDefaultsString = "xml/class";
+        mVersionString = "3";
+        mUserSetString = "xml/class";
+        loadXml(mService);
+
+        //Test services without overriding upgradeUserSet() remain unchanged
+        assertEquals(new ArraySet(Arrays.asList(mUserSetString)),
+                mService.mUserSetServices.get(98));
+        assertEquals(new ArraySet(Arrays.asList(mUserSetString)),
+                mService.mUserSetServices.get(99));
+        assertEquals(new ArrayMap(), mService.mIsUserChanged);
+    }
 
     private void resetComponentsAndPackages() {
         ArrayMap<Integer, ArrayMap<Integer, String>> empty = new ArrayMap(1);
@@ -1468,11 +1492,17 @@
         xml.append("<" + ManagedServices.TAG_MANAGED_SERVICES + " "
                         + ManagedServices.ATT_USER_ID + "=\"99\" "
                         + ManagedServices.ATT_IS_PRIMARY + "=\"true\" "
-                        + ManagedServices.ATT_APPROVED_LIST + "=\"990\" />\n");
+                        + ManagedServices.ATT_APPROVED_LIST + "=\"990\" "
+                        + (mUserSetString != null ? ManagedServices.ATT_USER_SET + "=\""
+                        + mUserSetString + "\" " : "")
+                        + "/>\n");
         xml.append("<" + ManagedServices.TAG_MANAGED_SERVICES + " "
                 + ManagedServices.ATT_USER_ID + "=\"98\" "
                 + ManagedServices.ATT_IS_PRIMARY + "=\"false\" "
-                + ManagedServices.ATT_APPROVED_LIST + "=\"981\" />\n");
+                + ManagedServices.ATT_APPROVED_LIST + "=\"981\" "
+                + (mUserSetString != null ? ManagedServices.ATT_USER_SET + "=\""
+                + mUserSetString + "\" " : "")
+                + " />\n");
         xml.append("</" + xmlTag + ">");
 
         return xml.toString();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index 6722fff..054a401 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -16,6 +16,7 @@
 package com.android.server.notification;
 
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 
 import static org.junit.Assert.assertNull;
@@ -41,11 +42,13 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.testing.TestableContext;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.IntArray;
 import android.util.TypedXmlPullParser;
 import android.util.Xml;
 
+import com.android.internal.util.function.TriPredicate;
 import com.android.server.UiServiceTestCase;
 import com.android.server.notification.NotificationManagerService.NotificationAssistants;
 
@@ -132,6 +135,68 @@
     }
 
     @Test
+    public void testReadXml_userDisabled() throws Exception {
+        String xml = "<enabled_assistants version=\"4\" defaults=\"b/b\">"
+                + "<service_listing approved=\"\" user=\"0\" primary=\"true\""
+                + "user_changed=\"true\"/>"
+                + "</enabled_assistants>";
+
+        final TypedXmlPullParser parser = Xml.newFastPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(xml.toString().getBytes())), null);
+        TriPredicate<String, Integer, String> allowedManagedServicePackages =
+                mNm::canUseManagedServices;
+
+        parser.nextTag();
+        mAssistants.readXml(parser, allowedManagedServicePackages, false, UserHandle.USER_ALL);
+
+        ArrayMap<Boolean, ArraySet<String>> approved = mAssistants.mApproved.get(0);
+
+        // approved should not be null
+        assertNotNull(approved);
+        assertEquals(new ArraySet<>(), approved.get(true));
+    }
+
+    @Test
+    public void testReadXml_upgradeUserSet() throws Exception {
+        String xml = "<enabled_assistants version=\"3\" defaults=\"b/b\">"
+                + "<service_listing approved=\"\" user=\"0\" primary=\"true\""
+                + "user_set_services=\"b/b\"/>"
+                + "</enabled_assistants>";
+
+        final TypedXmlPullParser parser = Xml.newFastPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(xml.toString().getBytes())), null);
+        TriPredicate<String, Integer, String> allowedManagedServicePackages =
+                mNm::canUseManagedServices;
+
+        parser.nextTag();
+        mAssistants.readXml(parser, allowedManagedServicePackages, false, UserHandle.USER_ALL);
+
+        verify(mAssistants, times(1)).upgradeUserSet();
+        assertTrue(mAssistants.mIsUserChanged.get(0));
+    }
+
+    @Test
+    public void testReadXml_multiApproved() throws Exception {
+        String xml = "<enabled_assistants version=\"4\" defaults=\"b/b\">"
+                + "<service_listing approved=\"a/a:b/b\" user=\"0\" primary=\"true\""
+                + "user_changed=\"true\"/>"
+                + "</enabled_assistants>";
+
+        final TypedXmlPullParser parser = Xml.newFastPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(xml.toString().getBytes())), null);
+
+        parser.nextTag();
+        mAssistants.readXml(parser, null, false, UserHandle.USER_ALL);
+
+        assertEquals(1, mAssistants.getAllowedComponents(0).size());
+        assertEquals(new ArrayList(Arrays.asList(new ComponentName("a", "a"))),
+                mAssistants.getAllowedComponents(0));
+    }
+
+    @Test
     public void testXmlUpgradeExistingApprovedComponents() throws Exception {
         String xml = "<enabled_assistants version=\"2\" defaults=\"b\\b\">"
                 + "<service_listing approved=\"b/b\" user=\"10\" primary=\"true\" />"
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index f277e94..6702869 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -2777,8 +2777,9 @@
 
     private void grantUsingBackgroundNetworksPermissionForUid(
             final int uid, final String packageName) throws Exception {
-        when(mPackageManager.getPackageInfo(eq(packageName), eq(GET_PERMISSIONS)))
-                .thenReturn(buildPackageInfo(true, uid));
+        when(mPackageManager.getPackageInfo(
+                eq(packageName), eq(GET_PERMISSIONS | MATCH_ANY_USER)))
+                .thenReturn(buildPackageInfo(true /* hasSystemPermission */, uid));
         mService.mPermissionMonitor.onPackageAdded(packageName, uid);
     }
 
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index d7535a9..02a5808 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -479,13 +479,14 @@
     public void testUidFilteringDuringVpnConnectDisconnectAndUidUpdates() throws Exception {
         when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
                 Arrays.asList(new PackageInfo[] {
-                        buildPackageInfo(/* SYSTEM */ true, SYSTEM_UID1, MOCK_USER1),
-                        buildPackageInfo(/* SYSTEM */ false, MOCK_UID1, MOCK_USER1),
-                        buildPackageInfo(/* SYSTEM */ false, MOCK_UID2, MOCK_USER1),
-                        buildPackageInfo(/* SYSTEM */ false, VPN_UID, MOCK_USER1)
+                        buildPackageInfo(true /* hasSystemPermission */, SYSTEM_UID1, MOCK_USER1),
+                        buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1),
+                        buildPackageInfo(false /* hasSystemPermission */, MOCK_UID2, MOCK_USER1),
+                        buildPackageInfo(false /* hasSystemPermission */, VPN_UID, MOCK_USER1)
                 }));
-        when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1), eq(GET_PERMISSIONS))).thenReturn(
-                buildPackageInfo(false, MOCK_UID1, MOCK_USER1));
+        when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1),
+                eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
+                buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1));
         mPermissionMonitor.startMonitoring();
         // Every app on user 0 except MOCK_UID2 are under VPN.
         final Set<UidRange> vpnRange1 = new HashSet<>(Arrays.asList(new UidRange[] {
@@ -530,11 +531,12 @@
     public void testUidFilteringDuringPackageInstallAndUninstall() throws Exception {
         when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
                 Arrays.asList(new PackageInfo[] {
-                        buildPackageInfo(true, SYSTEM_UID1, MOCK_USER1),
-                        buildPackageInfo(false, VPN_UID, MOCK_USER1)
+                        buildPackageInfo(true /* hasSystemPermission */, SYSTEM_UID1, MOCK_USER1),
+                        buildPackageInfo(false /* hasSystemPermission */, VPN_UID, MOCK_USER1)
                 }));
-        when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1), eq(GET_PERMISSIONS))).thenReturn(
-                        buildPackageInfo(false, MOCK_UID1, MOCK_USER1));
+        when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1),
+                eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
+                buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1));
 
         mPermissionMonitor.startMonitoring();
         final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(MOCK_USER1));
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 61ba09b..f2c6b15 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -135,7 +135,8 @@
     template <typename Predicate>
     void Filter(Predicate&& func) {
       children_.erase(std::remove_if(children_.begin(), children_.end(),
-                                     [&](const auto& e) { return func(e.get()); }));
+                                     [&](const auto& e) { return func(e.get()); }),
+                      children_.end());
     }
 
     /** Retrieves the list of children of the element. */
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index 2ec01cd..72eaa35 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -255,7 +255,7 @@
         break;
 
       case android::RES_TABLE_STAGED_ALIAS_TYPE:
-        if (!ParseOverlayable(parser.chunk())) {
+        if (!ParseStagedAliases(parser.chunk())) {
           return false;
         }
         break;
@@ -518,7 +518,7 @@
       return false;
     }
 
-    // Set the staged if of the finalized resource.
+    // Set the staged id of the finalized resource.
     const auto& resource_name = iter->second;
     const StagedId staged_id_def{.id = staged_id};
     if (!table_->AddResource(NewResourceBuilder(resource_name)
diff --git a/tools/codegen/src/com/android/codegen/Utils.kt b/tools/codegen/src/com/android/codegen/Utils.kt
index 7cfa784..9ceb204 100644
--- a/tools/codegen/src/com/android/codegen/Utils.kt
+++ b/tools/codegen/src/com/android/codegen/Utils.kt
@@ -43,8 +43,8 @@
  * cccc dd
  */
 fun Iterable<Pair<String, String>>.columnize(separator: String = " | "): String {
-    val col1w = map { (a, _) -> a.length }.max()!!
-    val col2w = map { (_, b) -> b.length }.max()!!
+    val col1w = map { (a, _) -> a.length }.maxOrNull()!!
+    val col2w = map { (_, b) -> b.length }.maxOrNull()!!
     return map { it.first.padEnd(col1w) + separator + it.second.padEnd(col2w) }.joinToString("\n")
 }