Merge "Remove netlink-client dependency from net-module-wifi." into sc-dev
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index 4ef91b5..b5e3662 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -90,6 +90,7 @@
             @NonNull Consumer<AppSearchResult<AppSearchSession>> callback) {
         try {
             mService.initialize(
+                    mPackageName,
                     mUserHandle,
                     /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(),
                     new IAppSearchResultCallback.Stub() {
@@ -136,8 +137,6 @@
      * @param callback Callback to receive errors resulting from setting the schema. If the
      *                 operation succeeds, the callback will be invoked with {@code null}.
      */
-    // TODO(b/169883602): Change @code references to @link when setPlatformSurfaceable APIs are
-    //  exposed.
     public void setSchema(
             @NonNull SetSchemaRequest request,
             @NonNull Executor workExecutor,
@@ -152,7 +151,7 @@
         for (AppSearchSchema schema : request.getSchemas()) {
             schemaBundles.add(schema.getBundle());
         }
-        Map<String, List<Bundle>> schemasPackageAccessibleBundles =
+        Map<String, List<Bundle>> schemasVisibleToPackagesBundles =
                 new ArrayMap<>(request.getSchemasVisibleToPackagesInternal().size());
         for (Map.Entry<String, Set<PackageIdentifier>> entry :
                 request.getSchemasVisibleToPackagesInternal().entrySet()) {
@@ -160,7 +159,7 @@
             for (PackageIdentifier packageIdentifier : entry.getValue()) {
                 packageIdentifierBundles.add(packageIdentifier.getBundle());
             }
-            schemasPackageAccessibleBundles.put(entry.getKey(), packageIdentifierBundles);
+            schemasVisibleToPackagesBundles.put(entry.getKey(), packageIdentifierBundles);
         }
 
         // No need to trigger migration if user never set migrator
@@ -168,14 +167,14 @@
             setSchemaNoMigrations(
                     request,
                     schemaBundles,
-                    schemasPackageAccessibleBundles,
+                    schemasVisibleToPackagesBundles,
                     callbackExecutor,
                     callback);
         } else {
             setSchemaWithMigrations(
                     request,
                     schemaBundles,
-                    schemasPackageAccessibleBundles,
+                    schemasVisibleToPackagesBundles,
                     workExecutor,
                     callbackExecutor,
                     callback);
@@ -687,7 +686,9 @@
         if (mIsMutated && !mIsClosed) {
             try {
                 mService.persistToDisk(
-                        mUserHandle, /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime());
+                        mPackageName,
+                        mUserHandle,
+                        /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime());
                 mIsClosed = true;
             } catch (RemoteException e) {
                 Log.e(TAG, "Unable to close the AppSearchSession", e);
@@ -704,7 +705,7 @@
     private void setSchemaNoMigrations(
             @NonNull SetSchemaRequest request,
             @NonNull List<Bundle> schemaBundles,
-            @NonNull Map<String, List<Bundle>> schemasPackageAccessibleBundles,
+            @NonNull Map<String, List<Bundle>> schemasVisibleToPackagesBundles,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull Consumer<AppSearchResult<SetSchemaResponse>> callback) {
         try {
@@ -713,7 +714,7 @@
                     mDatabaseName,
                     schemaBundles,
                     new ArrayList<>(request.getSchemasNotDisplayedBySystem()),
-                    schemasPackageAccessibleBundles,
+                    schemasVisibleToPackagesBundles,
                     request.isForceOverride(),
                     request.getVersion(),
                     mUserHandle,
@@ -761,7 +762,7 @@
     private void setSchemaWithMigrations(
             @NonNull SetSchemaRequest request,
             @NonNull List<Bundle> schemaBundles,
-            @NonNull Map<String, List<Bundle>> schemasPackageAccessibleBundles,
+            @NonNull Map<String, List<Bundle>> schemasVisibleToPackagesBundles,
             @NonNull Executor workExecutor,
             @NonNull @CallbackExecutor Executor callbackExecutor,
             @NonNull Consumer<AppSearchResult<SetSchemaResponse>> callback) {
@@ -787,7 +788,7 @@
 
                 // No need to trigger migration if no migrator is active.
                 if (activeMigrators.isEmpty()) {
-                    setSchemaNoMigrations(request, schemaBundles, schemasPackageAccessibleBundles,
+                    setSchemaNoMigrations(request, schemaBundles, schemasVisibleToPackagesBundles,
                             callbackExecutor, callback);
                     return;
                 }
@@ -801,7 +802,7 @@
                         mDatabaseName,
                         schemaBundles,
                         new ArrayList<>(request.getSchemasNotDisplayedBySystem()),
-                        schemasPackageAccessibleBundles,
+                        schemasVisibleToPackagesBundles,
                         /*forceOverride=*/ false,
                         request.getVersion(),
                         mUserHandle,
@@ -853,7 +854,7 @@
                                 mDatabaseName,
                                 schemaBundles,
                                 new ArrayList<>(request.getSchemasNotDisplayedBySystem()),
-                                schemasPackageAccessibleBundles,
+                                schemasVisibleToPackagesBundles,
                                 /*forceOverride=*/ true,
                                 request.getVersion(),
                                 mUserHandle,
diff --git a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
index 247eb08..130e442 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
@@ -73,6 +73,7 @@
             @NonNull Consumer<AppSearchResult<GlobalSearchSession>> callback) {
         try {
             mService.initialize(
+                    mPackageName,
                     mUserHandle,
                     /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(),
                     new IAppSearchResultCallback.Stub() {
@@ -187,7 +188,9 @@
         if (mIsMutated && !mIsClosed) {
             try {
                 mService.persistToDisk(
-                        mUserHandle, /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime());
+                        mPackageName,
+                        mUserHandle,
+                        /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime());
                 mIsClosed = true;
             } catch (RemoteException e) {
                 Log.e(TAG, "Unable to close the GlobalSearchSession", e);
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
index eb5d22e..6dfa01f 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
@@ -124,7 +124,8 @@
                             wrapCallback(executor, callback));
                 }
             } else {
-                mService.getNextPage(mNextPageToken, mUserHandle, wrapCallback(executor, callback));
+                mService.getNextPage(mPackageName, mNextPageToken, mUserHandle,
+                        wrapCallback(executor, callback));
             }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -135,7 +136,7 @@
     public void close() {
         if (!mIsClosed) {
             try {
-                mService.invalidateNextPageToken(mNextPageToken, mUserHandle);
+                mService.invalidateNextPageToken(mPackageName, mNextPageToken, mUserHandle);
                 mIsClosed = true;
             } catch (RemoteException e) {
                 Log.e(TAG, "Unable to close the SearchResults", e);
diff --git a/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchManager.aidl
index 0b26e14..a2f545f 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchManager.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchManager.aidl
@@ -32,7 +32,7 @@
      * @param schemaBundles List of {@link AppSearchSchema} bundles.
      * @param schemasNotDisplayedBySystem Schema types that should not be surfaced on platform
      *     surfaces.
-     * @param schemasPackageAccessibleBundles Schema types that are visible to the specified
+     * @param schemasVisibleToPackagesBundles Schema types that are visible to the specified
      *     packages. The value List contains PackageIdentifier Bundles.
      * @param forceOverride Whether to apply the new schema even if it is incompatible. All
      *     incompatible documents will be deleted.
@@ -48,7 +48,7 @@
         in String databaseName,
         in List<Bundle> schemaBundles,
         in List<String> schemasNotDisplayedBySystem,
-        in Map<String, List<Bundle>> schemasPackageAccessibleBundles,
+        in Map<String, List<Bundle>> schemasVisibleToPackagesBundles,
         boolean forceOverride,
         in int schemaVersion,
         in UserHandle userHandle,
@@ -181,21 +181,30 @@
      * Fetches the next page of results of a previously executed query. Results can be empty if
      * next-page token is invalid or all pages have been returned.
      *
+     * @param packageName The name of the package to persist to disk for.
      * @param nextPageToken The token of pre-loaded results of previously executed query.
      * @param userHandle Handle of the calling user
      * @param callback {@link AppSearchResult}&lt;{@link Bundle}&gt; of performing this
      *                  operation.
      */
-    void getNextPage(in long nextPageToken, in UserHandle userHandle, in IAppSearchResultCallback callback);
+    void getNextPage(
+        in String packageName,
+        in long nextPageToken,
+        in UserHandle userHandle,
+        in IAppSearchResultCallback callback);
 
     /**
      * Invalidates the next-page token so that no more results of the related query can be returned.
      *
+     * @param packageName The name of the package to persist to disk for.
      * @param nextPageToken The token of pre-loaded results of previously executed query to be
      *                      Invalidated.
      * @param userHandle Handle of the calling user
      */
-    void invalidateNextPageToken(in long nextPageToken, in UserHandle userHandle);
+    void invalidateNextPageToken(
+        in String packageName,
+        in long nextPageToken,
+        in UserHandle userHandle);
 
     /**
     * Searches a document based on a given specifications.
@@ -336,20 +345,26 @@
     /**
      * Persists all update/delete requests to the disk.
      *
+     * @param packageName The name of the package to persist to disk for.
      * @param userHandle Handle of the calling user
      * @param binderCallStartTimeMillis start timestamp of binder call in Millis
      */
-    void persistToDisk(in UserHandle userHandle, in long binderCallStartTimeMillis);
+    void persistToDisk(
+        in String packageName,
+        in UserHandle userHandle,
+        in long binderCallStartTimeMillis);
 
     /**
      * Creates and initializes AppSearchImpl for the calling app.
      *
+     * @param packageName The name of the package to initialize for.
      * @param userHandle Handle of the calling user
      * @param binderCallStartTimeMillis start timestamp of binder call in Millis
      * @param callback {@link IAppSearchResultCallback#onResult} will be called with an
      *     {@link AppSearchResult}&lt;{@link Void}&gt;.
      */
     void initialize(
+        in String packageName,
         in UserHandle userHandle,
         in long binderCallStartTimeMillis,
         in IAppSearchResultCallback callback);
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 272e12d..d493a1c 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java
@@ -96,17 +96,6 @@
         return Collections.unmodifiableMap(mAll);
     }
 
-    /**
-     * Asserts that this {@link AppSearchBatchResult} has no failures.
-     *
-     * @hide
-     */
-    public void checkSuccess() {
-        if (!isSuccess()) {
-            throw new IllegalStateException("AppSearchBatchResult has failures: " + this);
-        }
-    }
-
     @Override
     @NonNull
     public String toString() {
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchResult.java
index c57cf2e..b1cb132 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchResult.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchResult.java
@@ -239,6 +239,8 @@
             resultCode = AppSearchResult.RESULT_INVALID_ARGUMENT;
         } else if (t instanceof IOException) {
             resultCode = AppSearchResult.RESULT_IO_ERROR;
+        } else if (t instanceof SecurityException) {
+            resultCode = AppSearchResult.RESULT_SECURITY_ERROR;
         } else {
             resultCode = AppSearchResult.RESULT_UNKNOWN_ERROR;
         }
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
index 237e624..0ee5e65 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 import android.app.appsearch.exceptions.IllegalSchemaException;
 import android.app.appsearch.util.BundleUtil;
+import android.app.appsearch.util.IndentingStringBuilder;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Bundle;
 import android.util.ArraySet;
@@ -30,6 +31,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
@@ -67,8 +69,45 @@
     }
 
     @Override
+    @NonNull
     public String toString() {
-        return mBundle.toString();
+        IndentingStringBuilder stringBuilder = new IndentingStringBuilder();
+        appendAppSearchSchemaString(stringBuilder);
+        return stringBuilder.toString();
+    }
+
+    /**
+     * Appends a debugging string for the {@link AppSearchSchema} instance to the given string
+     * builder.
+     *
+     * @param builder the builder to append to.
+     */
+    private void appendAppSearchSchemaString(@NonNull IndentingStringBuilder builder) {
+        Objects.requireNonNull(builder);
+
+        builder.append("{\n");
+        builder.increaseIndentLevel();
+        builder.append("schemaType: \"").append(getSchemaType()).append("\",\n");
+        builder.append("properties: [\n");
+
+        AppSearchSchema.PropertyConfig[] sortedProperties =
+                getProperties().toArray(new AppSearchSchema.PropertyConfig[0]);
+        Arrays.sort(sortedProperties, (o1, o2) -> o1.getName().compareTo(o2.getName()));
+
+        for (int i = 0; i < sortedProperties.length; i++) {
+            AppSearchSchema.PropertyConfig propertyConfig = sortedProperties[i];
+            builder.increaseIndentLevel();
+            propertyConfig.appendPropertyConfigString(builder);
+            if (i != sortedProperties.length - 1) {
+                builder.append(",\n");
+            }
+            builder.decreaseIndentLevel();
+        }
+
+        builder.append("\n");
+        builder.append("]\n");
+        builder.decreaseIndentLevel();
+        builder.append("}");
     }
 
     /** Returns the name of this schema type, e.g. Email. */
@@ -255,7 +294,68 @@
         @Override
         @NonNull
         public String toString() {
-            return mBundle.toString();
+            IndentingStringBuilder stringBuilder = new IndentingStringBuilder();
+            appendPropertyConfigString(stringBuilder);
+            return stringBuilder.toString();
+        }
+
+        /**
+         * Appends a debug string for the {@link AppSearchSchema.PropertyConfig} instance to the
+         * given string builder.
+         *
+         * @param builder the builder to append to.
+         */
+        void appendPropertyConfigString(@NonNull IndentingStringBuilder builder) {
+            Objects.requireNonNull(builder);
+
+            builder.append("{\n");
+            builder.increaseIndentLevel();
+            builder.append("name: \"").append(getName()).append("\",\n");
+
+            if (this instanceof AppSearchSchema.StringPropertyConfig) {
+                ((StringPropertyConfig) this).appendStringPropertyConfigFields(builder);
+            } else if (this instanceof AppSearchSchema.DocumentPropertyConfig) {
+                ((DocumentPropertyConfig) this).appendDocumentPropertyConfigFields(builder);
+            }
+
+            switch (getCardinality()) {
+                case AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED:
+                    builder.append("cardinality: CARDINALITY_REPEATED,\n");
+                    break;
+                case AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL:
+                    builder.append("cardinality: CARDINALITY_OPTIONAL,\n");
+                    break;
+                case AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED:
+                    builder.append("cardinality: CARDINALITY_REQUIRED,\n");
+                    break;
+                default:
+                    builder.append("cardinality: CARDINALITY_UNKNOWN,\n");
+            }
+
+            switch (getDataType()) {
+                case AppSearchSchema.PropertyConfig.DATA_TYPE_STRING:
+                    builder.append("dataType: DATA_TYPE_STRING,\n");
+                    break;
+                case AppSearchSchema.PropertyConfig.DATA_TYPE_LONG:
+                    builder.append("dataType: DATA_TYPE_LONG,\n");
+                    break;
+                case AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE:
+                    builder.append("dataType: DATA_TYPE_DOUBLE,\n");
+                    break;
+                case AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN:
+                    builder.append("dataType: DATA_TYPE_BOOLEAN,\n");
+                    break;
+                case AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES:
+                    builder.append("dataType: DATA_TYPE_BYTES,\n");
+                    break;
+                case AppSearchSchema.PropertyConfig.DATA_TYPE_DOCUMENT:
+                    builder.append("dataType: DATA_TYPE_DOCUMENT,\n");
+                    break;
+                default:
+                    builder.append("dataType: DATA_TYPE_UNKNOWN,\n");
+            }
+            builder.decreaseIndentLevel();
+            builder.append("}");
         }
 
         /** Returns the name of this property. */
@@ -506,6 +606,41 @@
                 return new StringPropertyConfig(bundle);
             }
         }
+
+        /**
+         * Appends a debug string for the {@link StringPropertyConfig} instance to the given string
+         * builder.
+         *
+         * <p>This appends fields specific to a {@link StringPropertyConfig} instance.
+         *
+         * @param builder the builder to append to.
+         */
+        void appendStringPropertyConfigFields(@NonNull IndentingStringBuilder builder) {
+            switch (getIndexingType()) {
+                case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE:
+                    builder.append("indexingType: INDEXING_TYPE_NONE,\n");
+                    break;
+                case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS:
+                    builder.append("indexingType: INDEXING_TYPE_EXACT_TERMS,\n");
+                    break;
+                case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES:
+                    builder.append("indexingType: INDEXING_TYPE_PREFIXES,\n");
+                    break;
+                default:
+                    builder.append("indexingType: INDEXING_TYPE_UNKNOWN,\n");
+            }
+
+            switch (getTokenizerType()) {
+                case AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE:
+                    builder.append("tokenizerType: TOKENIZER_TYPE_NONE,\n");
+                    break;
+                case AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN:
+                    builder.append("tokenizerType: TOKENIZER_TYPE_PLAIN,\n");
+                    break;
+                default:
+                    builder.append("tokenizerType: TOKENIZER_TYPE_UNKNOWN,\n");
+            }
+        }
     }
 
     /**
@@ -858,5 +993,21 @@
                 return new DocumentPropertyConfig(bundle);
             }
         }
+
+        /**
+         * Appends a debug string for the {@link DocumentPropertyConfig} instance to the given
+         * string builder.
+         *
+         * <p>This appends fields specific to a {@link DocumentPropertyConfig} instance.
+         *
+         * @param builder the builder to append to.
+         */
+        void appendDocumentPropertyConfigFields(@NonNull IndentingStringBuilder builder) {
+            builder.append("shouldIndexNestedProperties: ")
+                    .append(shouldIndexNestedProperties())
+                    .append(",\n");
+
+            builder.append("schemaType: \"").append(getSchemaType()).append("\",\n");
+        }
     }
 }
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 bcd341e..c905f95 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
@@ -22,6 +22,7 @@
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.app.appsearch.util.BundleUtil;
+import android.app.appsearch.util.IndentingStringBuilder;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Bundle;
 import android.os.Parcelable;
@@ -50,9 +51,6 @@
 public class GenericDocument {
     private static final String TAG = "AppSearchGenericDocumen";
 
-    /** The maximum number of elements in a repeatable field. */
-    private static final int MAX_REPEATED_PROPERTY_LENGTH = 100;
-
     /** The maximum {@link String#length} of a {@link String} field. */
     private static final int MAX_STRING_LENGTH = 20_000;
 
@@ -861,6 +859,7 @@
      *
      * @hide
      */
+    // TODO(b/171882200): Expose this API in Android T
     @NonNull
     public GenericDocument.Builder<GenericDocument.Builder<?>> toBuilder() {
         Bundle clonedBundle = BundleUtil.deepCopy(mBundle);
@@ -890,121 +889,101 @@
     @Override
     @NonNull
     public String toString() {
-        StringBuilder stringBuilder = new StringBuilder();
-        appendGenericDocumentString(this, /*indentLevel=*/ 0, stringBuilder);
+        IndentingStringBuilder stringBuilder = new IndentingStringBuilder();
+        appendGenericDocumentString(stringBuilder);
         return stringBuilder.toString();
     }
 
-    private static void appendGenericDocumentString(
-            @NonNull GenericDocument document, int indentLevel, @NonNull StringBuilder builder) {
-        Objects.requireNonNull(document);
+    /**
+     * Appends a debug string for the {@link GenericDocument} instance to the given string builder.
+     *
+     * @param builder the builder to append to.
+     */
+    void appendGenericDocumentString(@NonNull IndentingStringBuilder builder) {
         Objects.requireNonNull(builder);
 
-        builder.append(getIndent(indentLevel)).append("{\n");
+        builder.append("{\n");
+        builder.increaseIndentLevel();
 
-        String indent1 = getIndent(indentLevel + 1);
-
-        builder.append(indent1)
-                .append("namespace: \"")
-                .append(document.getNamespace())
-                .append("\",\n");
-
-        builder.append(indent1).append("id: \"").append(document.getId()).append("\",\n");
-
-        builder.append(indent1).append("score: ").append(document.getScore()).append(",\n");
-
-        builder.append(indent1)
-                .append("schemaType: \"")
-                .append(document.getSchemaType())
-                .append("\",\n");
-
-        builder.append(indent1)
-                .append("creationTimestampMillis: ")
-                .append(document.getCreationTimestampMillis())
+        builder.append("namespace: \"").append(getNamespace()).append("\",\n");
+        builder.append("id: \"").append(getId()).append("\",\n");
+        builder.append("score: ").append(getScore()).append(",\n");
+        builder.append("schemaType: \"").append(getSchemaType()).append("\",\n");
+        builder.append("creationTimestampMillis: ")
+                .append(getCreationTimestampMillis())
                 .append(",\n");
+        builder.append("timeToLiveMillis: ").append(getTtlMillis()).append(",\n");
 
-        builder.append(indent1)
-                .append("timeToLiveMillis: ")
-                .append(document.getTtlMillis())
-                .append(",\n");
+        builder.append("properties: {\n");
 
-        builder.append(indent1).append("properties: {\n");
-
-        String[] sortedProperties = document.getPropertyNames().toArray(new String[0]);
+        String[] sortedProperties = 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(sortedProperties[i])
-                    .append("\"")
-                    .append(": ");
-            appendPropertyString(property, indentLevel + 2, builder);
+            Object property = getProperty(sortedProperties[i]);
+            builder.increaseIndentLevel();
+            appendPropertyString(sortedProperties[i], property, builder);
             if (i != sortedProperties.length - 1) {
                 builder.append(",\n");
             }
+            builder.decreaseIndentLevel();
         }
 
         builder.append("\n");
-        builder.append(indent1).append("}");
+        builder.append("}");
 
+        builder.decreaseIndentLevel();
         builder.append("\n");
-        builder.append(getIndent(indentLevel)).append("}");
+        builder.append("}");
     }
 
     /**
-     * Appends a string for the given property to the given builder.
+     * Appends a debug string for the given document property to the given string builder.
      *
+     * @param propertyName name of property to create string for.
      * @param property property object to create string for.
-     * @param indentLevel base indent level for property.
      * @param builder the builder to append to.
      */
-    private static void appendPropertyString(
-            @NonNull Object property, int indentLevel, @NonNull StringBuilder builder) {
+    private void appendPropertyString(
+            @NonNull String propertyName,
+            @NonNull Object property,
+            @NonNull IndentingStringBuilder builder) {
+        Objects.requireNonNull(propertyName);
         Objects.requireNonNull(property);
         Objects.requireNonNull(builder);
 
-        builder.append("[");
+        builder.append("\"").append(propertyName).append("\": [");
         if (property instanceof GenericDocument[]) {
             GenericDocument[] documentValues = (GenericDocument[]) property;
             for (int i = 0; i < documentValues.length; ++i) {
                 builder.append("\n");
-                appendGenericDocumentString(documentValues[i], indentLevel + 1, builder);
+                builder.increaseIndentLevel();
+                documentValues[i].appendGenericDocumentString(builder);
                 if (i != documentValues.length - 1) {
-                    builder.append(", ");
+                    builder.append(",");
                 }
                 builder.append("\n");
+                builder.decreaseIndentLevel();
             }
-            builder.append(getIndent(indentLevel));
+            builder.append("]");
         } else {
             int propertyArrLength = Array.getLength(property);
             for (int i = 0; i < propertyArrLength; i++) {
                 Object propertyElement = Array.get(property, i);
                 if (propertyElement instanceof String) {
-                    builder.append("\"").append(propertyElement).append("\"");
+                    builder.append("\"").append((String) propertyElement).append("\"");
                 } else if (propertyElement instanceof byte[]) {
                     builder.append(Arrays.toString((byte[]) propertyElement));
                 } else {
-                    builder.append(propertyElement);
+                    builder.append(propertyElement.toString());
                 }
                 if (i != propertyArrLength - 1) {
                     builder.append(", ");
+                } else {
+                    builder.append("]");
                 }
             }
         }
-
-        builder.append("]");
-    }
-
-    /** Appends a string for given indent level to the given builder. */
-    @NonNull
-    private static String getIndent(int indentLevel) {
-        StringBuilder builder = new StringBuilder();
-        for (int i = 0; i < indentLevel; ++i) {
-            builder.append("  ");
-        }
-        return builder.toString();
     }
 
     /**
@@ -1187,8 +1166,8 @@
          * @param name the name associated with the {@code values}. Must match the name for this
          *     property as given in {@link AppSearchSchema.PropertyConfig#getName}.
          * @param values the {@code String} values of the property.
-         * @throws IllegalArgumentException if no values are provided, if provided values exceed
-         *     maximum repeated property length, or if a passed in {@code String} is {@code null}.
+         * @throws IllegalArgumentException if no values are provided, or if a passed in {@code
+         *     String} is {@code null}.
          */
         @NonNull
         public BuilderType setPropertyString(@NonNull String name, @NonNull String... values) {
@@ -1206,7 +1185,6 @@
          * @param name the name associated with the {@code values}. Must match the name for this
          *     property as given in {@link AppSearchSchema.PropertyConfig#getName}.
          * @param values the {@code boolean} values of the property.
-         * @throws IllegalArgumentException if values exceed maximum repeated property length.
          */
         @NonNull
         public BuilderType setPropertyBoolean(@NonNull String name, @NonNull boolean... values) {
@@ -1223,7 +1201,6 @@
          * @param name the name associated with the {@code values}. Must match the name for this
          *     property as given in {@link AppSearchSchema.PropertyConfig#getName}.
          * @param values the {@code long} values of the property.
-         * @throws IllegalArgumentException if values exceed maximum repeated property length.
          */
         @NonNull
         public BuilderType setPropertyLong(@NonNull String name, @NonNull long... values) {
@@ -1240,7 +1217,6 @@
          * @param name the name associated with the {@code values}. Must match the name for this
          *     property as given in {@link AppSearchSchema.PropertyConfig#getName}.
          * @param values the {@code double} values of the property.
-         * @throws IllegalArgumentException if values exceed maximum repeated property length.
          */
         @NonNull
         public BuilderType setPropertyDouble(@NonNull String name, @NonNull double... values) {
@@ -1257,8 +1233,8 @@
          * @param name the name associated with the {@code values}. Must match the name for this
          *     property as given in {@link AppSearchSchema.PropertyConfig#getName}.
          * @param values the {@code byte[]} of the property.
-         * @throws IllegalArgumentException if no values are provided, if provided values exceed
-         *     maximum repeated property length, or if a passed in {@code byte[]} is {@code null}.
+         * @throws IllegalArgumentException if no values are provided, or if a passed in {@code
+         *     byte[]} is {@code null}.
          */
         @NonNull
         public BuilderType setPropertyBytes(@NonNull String name, @NonNull byte[]... values) {
@@ -1276,8 +1252,7 @@
          * @param name the name associated with the {@code values}. Must match the name for this
          *     property as given in {@link AppSearchSchema.PropertyConfig#getName}.
          * @param values the {@link GenericDocument} values of the property.
-         * @throws IllegalArgumentException if no values are provided, if provided values exceed if
-         *     provided values exceed maximum repeated property length, or if a passed in {@link
+         * @throws IllegalArgumentException if no values are provided, or if a passed in {@link
          *     GenericDocument} is {@code null}.
          */
         @NonNull
@@ -1308,7 +1283,6 @@
 
         private void putInPropertyBundle(@NonNull String name, @NonNull String[] values)
                 throws IllegalArgumentException {
-            validateRepeatedPropertyLength(name, values.length);
             for (int i = 0; i < values.length; i++) {
                 if (values[i] == null) {
                     throw new IllegalArgumentException("The String at " + i + " is null.");
@@ -1327,17 +1301,14 @@
         }
 
         private void putInPropertyBundle(@NonNull String name, @NonNull boolean[] values) {
-            validateRepeatedPropertyLength(name, values.length);
             mProperties.putBooleanArray(name, values);
         }
 
         private void putInPropertyBundle(@NonNull String name, @NonNull double[] values) {
-            validateRepeatedPropertyLength(name, values.length);
             mProperties.putDoubleArray(name, values);
         }
 
         private void putInPropertyBundle(@NonNull String name, @NonNull long[] values) {
-            validateRepeatedPropertyLength(name, values.length);
             mProperties.putLongArray(name, values);
         }
 
@@ -1348,7 +1319,6 @@
          * into ArrayList<Bundle>, and each elements will contain a one dimension byte[].
          */
         private void putInPropertyBundle(@NonNull String name, @NonNull byte[][] values) {
-            validateRepeatedPropertyLength(name, values.length);
             ArrayList<Bundle> bundles = new ArrayList<>(values.length);
             for (int i = 0; i < values.length; i++) {
                 if (values[i] == null) {
@@ -1362,7 +1332,6 @@
         }
 
         private void putInPropertyBundle(@NonNull String name, @NonNull GenericDocument[] values) {
-            validateRepeatedPropertyLength(name, values.length);
             Parcelable[] documentBundles = new Parcelable[values.length];
             for (int i = 0; i < values.length; i++) {
                 if (values[i] == null) {
@@ -1373,18 +1342,6 @@
             mProperties.putParcelableArray(name, documentBundles);
         }
 
-        private static void validateRepeatedPropertyLength(@NonNull String name, int length) {
-            if (length > MAX_REPEATED_PROPERTY_LENGTH) {
-                throw new IllegalArgumentException(
-                        "Repeated property \""
-                                + name
-                                + "\" has length "
-                                + length
-                                + ", which exceeds the limit of "
-                                + MAX_REPEATED_PROPERTY_LENGTH);
-            }
-        }
-
         /** Builds the {@link GenericDocument} object. */
         @NonNull
         public GenericDocument build() {
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
index 0af3e7a..b72ca9a 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
@@ -169,13 +169,14 @@
 
     /** Builder for {@link SetSchemaRequest} objects. */
     public static final class Builder {
+        private static final int DEFAULT_VERSION = 1;
         private ArraySet<AppSearchSchema> mSchemas = new ArraySet<>();
         private ArraySet<String> mSchemasNotDisplayedBySystem = new ArraySet<>();
         private ArrayMap<String, Set<PackageIdentifier>> mSchemasVisibleToPackages =
                 new ArrayMap<>();
         private ArrayMap<String, Migrator> mMigrators = new ArrayMap<>();
         private boolean mForceOverride = false;
-        private int mVersion = 1;
+        private int mVersion = DEFAULT_VERSION;
         private boolean mBuilt = false;
 
         /**
@@ -384,6 +385,9 @@
          * <p>The version number can stay the same, increase, or decrease relative to the current
          * version number that is already stored in the {@link AppSearchSession} database.
          *
+         * <p>The version of an empty database will always be 0. You cannot set version to the
+         * {@link SetSchemaRequest}, if it doesn't contains any {@link AppSearchSchema}.
+         *
          * @param version A positive integer representing the version of the entire set of schemas
          *     represents the version of the whole schema in the {@link AppSearchSession} database,
          *     default version is 1.
@@ -423,7 +427,10 @@
                 throw new IllegalArgumentException(
                         "Schema types " + referencedSchemas + " referenced, but were not added.");
             }
-
+            if (mSchemas.isEmpty() && mVersion != DEFAULT_VERSION) {
+                throw new IllegalArgumentException(
+                        "Cannot set version to the request if schema is empty.");
+            }
             mBuilt = true;
             return new SetSchemaRequest(
                     mSchemas,
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/util/IndentingStringBuilder.java b/apex/appsearch/framework/java/external/android/app/appsearch/util/IndentingStringBuilder.java
new file mode 100644
index 0000000..b494c3c
--- /dev/null
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/util/IndentingStringBuilder.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch.util;
+
+import android.annotation.NonNull;
+
+/**
+ * Utility for building indented strings.
+ *
+ * <p>This is a wrapper for {@link StringBuilder} for appending strings with indentation. The
+ * indentation level can be increased by calling {@link #increaseIndentLevel()} and decreased by
+ * calling {@link #decreaseIndentLevel()}.
+ *
+ * <p>Indentation is applied after each newline character for the given indent level.
+ *
+ * @hide
+ */
+public class IndentingStringBuilder {
+    private final StringBuilder mStringBuilder = new StringBuilder();
+
+    // Indicates whether next non-newline character should have an indent applied before it.
+    private boolean mIndentNext = false;
+    private int mIndentLevel = 0;
+
+    /** Increases the indent level by one for appended strings. */
+    @NonNull
+    public IndentingStringBuilder increaseIndentLevel() {
+        mIndentLevel++;
+        return this;
+    }
+
+    /** Decreases the indent level by one for appended strings. */
+    @NonNull
+    public IndentingStringBuilder decreaseIndentLevel() throws IllegalStateException {
+        if (mIndentLevel == 0) {
+            throw new IllegalStateException("Cannot set indent level below 0.");
+        }
+        mIndentLevel--;
+        return this;
+    }
+
+    /**
+     * Appends provided {@code String} at the current indentation level.
+     *
+     * <p>Indentation is applied after each newline character.
+     */
+    @NonNull
+    public IndentingStringBuilder append(@NonNull String str) {
+        applyIndentToString(str);
+        return this;
+    }
+
+    /**
+     * Appends provided {@code Object}, represented as a {@code String}, at the current indentation
+     * level.
+     *
+     * <p>Indentation is applied after each newline character.
+     */
+    @NonNull
+    public IndentingStringBuilder append(@NonNull Object obj) {
+        applyIndentToString(obj.toString());
+        return this;
+    }
+
+    @Override
+    @NonNull
+    public String toString() {
+        return mStringBuilder.toString();
+    }
+
+    /** Adds indent string to the {@link StringBuilder} instance for current indent level. */
+    private void applyIndent() {
+        for (int i = 0; i < mIndentLevel; i++) {
+            mStringBuilder.append("  ");
+        }
+    }
+
+    /**
+     * Applies indent, for current indent level, after each newline character.
+     *
+     * <p>Consecutive newline characters are not indented.
+     */
+    private void applyIndentToString(@NonNull String str) {
+        int index = str.indexOf("\n");
+        if (index == 0) {
+            // String begins with new line character: append newline and slide past newline.
+            mStringBuilder.append("\n");
+            mIndentNext = true;
+            if (str.length() > 1) {
+                applyIndentToString(str.substring(index + 1));
+            }
+        } else if (index >= 1) {
+            // String contains new line character: divide string between newline, append new line,
+            // and recurse on each string.
+            String beforeIndentString = str.substring(0, index);
+            applyIndentToString(beforeIndentString);
+            mStringBuilder.append("\n");
+            mIndentNext = true;
+            if (str.length() > index + 1) {
+                String afterIndentString = str.substring(index + 1);
+                applyIndentToString(afterIndentString);
+            }
+        } else {
+            // String does not contain newline character: append string.
+            if (mIndentNext) {
+                applyIndent();
+                mIndentNext = false;
+            }
+            mStringBuilder.append(str);
+        }
+    }
+}
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 400c241..481d51e 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -60,8 +60,8 @@
 import com.android.server.LocalManagerRegistry;
 import com.android.server.SystemService;
 import com.android.server.appsearch.external.localstorage.stats.CallStats;
+import com.android.server.appsearch.external.localstorage.visibilitystore.VisibilityStore;
 import com.android.server.appsearch.util.PackageUtil;
-import com.android.server.appsearch.visibilitystore.VisibilityStore;
 import com.android.server.usage.StorageStatsManagerLocal;
 import com.android.server.usage.StorageStatsManagerLocal.StorageStatsAugmenter;
 
@@ -220,9 +220,10 @@
             }
             // Only clear the package's data if AppSearch exists for this user.
             if (AppSearchUserInstanceManager.getAppSearchDir(userHandle).exists()) {
+                Context userContext = mContext.createContextAsUser(userHandle, /*flags=*/ 0);
                 AppSearchUserInstance instance =
                         mAppSearchUserInstanceManager.getOrCreateUserInstance(
-                                mContext, userHandle, AppSearchConfig.getInstance(EXECUTOR));
+                                userContext, userHandle, AppSearchConfig.getInstance(EXECUTOR));
                 //TODO(b/145759910) clear visibility setting for package.
                 instance.getAppSearchImpl().clearPackageData(packageName);
                 instance.getLogger().removeCachedUidForPackage(packageName);
@@ -243,11 +244,11 @@
             try {
                 // Only clear the package's data if AppSearch exists for this user.
                 if (AppSearchUserInstanceManager.getAppSearchDir(userHandle).exists()) {
+                    Context userContext = mContext.createContextAsUser(userHandle, /*flags=*/ 0);
                     AppSearchUserInstance instance =
                             mAppSearchUserInstanceManager.getOrCreateUserInstance(
-                                    mContext, userHandle, AppSearchConfig.getInstance(EXECUTOR));
-                    List<PackageInfo> installedPackageInfos = mContext
-                            .createContextAsUser(userHandle, /*flags=*/0)
+                                    userContext, userHandle, AppSearchConfig.getInstance(EXECUTOR));
+                    List<PackageInfo> installedPackageInfos = userContext
                             .getPackageManager()
                             .getInstalledPackages(/*flags=*/0);
                     Set<String> packagesToKeep = new ArraySet<>(installedPackageInfos.size());
@@ -327,13 +328,15 @@
                 int operationSuccessCount = 0;
                 int operationFailureCount = 0;
                 try {
+                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
                     verifyUserUnlocked(callingUser);
-                    verifyCallingPackage(callingUser, callingUid, packageName);
+                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+                    verifyNotInstantApp(userContext, packageName);
                     List<AppSearchSchema> schemas = new ArrayList<>(schemaBundles.size());
                     for (int i = 0; i < schemaBundles.size(); i++) {
                         schemas.add(new AppSearchSchema(schemaBundles.get(i)));
                     }
-                    Map<String, List<PackageIdentifier>> schemasPackageAccessible =
+                    Map<String, List<PackageIdentifier>> schemasVisibleToPackages =
                             new ArrayMap<>(schemasVisibleToPackagesBundles.size());
                     for (Map.Entry<String, List<Bundle>> entry :
                             schemasVisibleToPackagesBundles.entrySet()) {
@@ -343,7 +346,7 @@
                             packageIdentifiers.add(
                                     new PackageIdentifier(entry.getValue().get(i)));
                         }
-                        schemasPackageAccessible.put(entry.getKey(), packageIdentifiers);
+                        schemasVisibleToPackages.put(entry.getKey(), packageIdentifiers);
                     }
                     instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
                     SetSchemaResponse setSchemaResponse = instance.getAppSearchImpl().setSchema(
@@ -352,7 +355,7 @@
                             schemas,
                             instance.getVisibilityStore(),
                             schemasNotDisplayedBySystem,
-                            schemasPackageAccessible,
+                            schemasVisibleToPackages,
                             forceOverride,
                             schemaVersion);
                     ++operationSuccessCount;
@@ -401,8 +404,10 @@
             UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
             EXECUTOR.execute(() -> {
                 try {
+                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
                     verifyUserUnlocked(callingUser);
-                    verifyCallingPackage(callingUser, callingUid, packageName);
+                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+                    verifyNotInstantApp(userContext, packageName);
                     AppSearchUserInstance instance =
                             mAppSearchUserInstanceManager.getUserInstance(callingUser);
                     GetSchemaResponse response =
@@ -431,8 +436,10 @@
             UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
             EXECUTOR.execute(() -> {
                 try {
+                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
                     verifyUserUnlocked(callingUser);
-                    verifyCallingPackage(callingUser, callingUid, packageName);
+                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+                    verifyNotInstantApp(userContext, packageName);
                     AppSearchUserInstance instance =
                             mAppSearchUserInstanceManager.getUserInstance(callingUser);
                     List<String> namespaces =
@@ -468,8 +475,10 @@
                 int operationSuccessCount = 0;
                 int operationFailureCount = 0;
                 try {
+                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
                     verifyUserUnlocked(callingUser);
-                    verifyCallingPackage(callingUser, callingUid, packageName);
+                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+                    verifyNotInstantApp(userContext, packageName);
                     AppSearchBatchResult.Builder<String, Void> resultBuilder =
                             new AppSearchBatchResult.Builder<>();
                     instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
@@ -548,8 +557,10 @@
                 int operationSuccessCount = 0;
                 int operationFailureCount = 0;
                 try {
+                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
                     verifyUserUnlocked(callingUser);
-                    verifyCallingPackage(callingUser, callingUid, packageName);
+                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+                    verifyNotInstantApp(userContext, packageName);
                     AppSearchBatchResult.Builder<String, Bundle> resultBuilder =
                             new AppSearchBatchResult.Builder<>();
                     instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
@@ -627,8 +638,10 @@
                 int operationSuccessCount = 0;
                 int operationFailureCount = 0;
                 try {
+                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
                     verifyUserUnlocked(callingUser);
-                    verifyCallingPackage(callingUser, callingUid, packageName);
+                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+                    verifyNotInstantApp(userContext, packageName);
                     instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
                     SearchResultPage searchResultPage = instance.getAppSearchImpl().query(
                             packageName,
@@ -691,8 +704,10 @@
                 int operationSuccessCount = 0;
                 int operationFailureCount = 0;
                 try {
+                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
                     verifyUserUnlocked(callingUser);
-                    verifyCallingPackage(callingUser, callingUid, packageName);
+                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+                    verifyNotInstantApp(userContext, packageName);
                     instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
 
                     boolean callerHasSystemAccess =
@@ -738,9 +753,11 @@
 
         @Override
         public void getNextPage(
+                @NonNull String packageName,
                 long nextPageToken,
                 @NonNull UserHandle userHandle,
                 @NonNull IAppSearchResultCallback callback) {
+            Objects.requireNonNull(packageName);
             Objects.requireNonNull(userHandle);
             Objects.requireNonNull(callback);
 
@@ -750,7 +767,10 @@
             // opened it
             EXECUTOR.execute(() -> {
                 try {
+                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
                     verifyUserUnlocked(callingUser);
+                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+                    verifyNotInstantApp(userContext, packageName);
                     AppSearchUserInstance instance =
                             mAppSearchUserInstanceManager.getUserInstance(callingUser);
                     SearchResultPage searchResultPage =
@@ -765,14 +785,19 @@
         }
 
         @Override
-        public void invalidateNextPageToken(long nextPageToken, @NonNull UserHandle userHandle) {
+        public void invalidateNextPageToken(@NonNull String packageName, long nextPageToken,
+                @NonNull UserHandle userHandle) {
+            Objects.requireNonNull(packageName);
             Objects.requireNonNull(userHandle);
 
             int callingUid = Binder.getCallingUid();
             UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
             EXECUTOR.execute(() -> {
                 try {
+                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
                     verifyUserUnlocked(callingUser);
+                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+                    verifyNotInstantApp(userContext, packageName);
                     AppSearchUserInstance instance =
                             mAppSearchUserInstanceManager.getUserInstance(callingUser);
                     instance.getAppSearchImpl().invalidateNextPageToken(nextPageToken);
@@ -803,7 +828,10 @@
             UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
             EXECUTOR.execute(() -> {
                 try {
-                    verifyCallingPackage(callingUser, callingUid, packageName);
+                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
+                    verifyUserUnlocked(callingUser);
+                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+                    verifyNotInstantApp(userContext, packageName);
                     AppSearchUserInstance instance =
                             mAppSearchUserInstanceManager.getUserInstance(callingUser);
                     // we don't need to append the file. The file is always brand new.
@@ -849,7 +877,10 @@
             UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
             EXECUTOR.execute(() -> {
                 try {
-                    verifyCallingPackage(callingUser, callingUid, packageName);
+                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
+                    verifyUserUnlocked(callingUser);
+                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+                    verifyNotInstantApp(userContext, packageName);
                     AppSearchUserInstance instance =
                             mAppSearchUserInstanceManager.getUserInstance(callingUser);
 
@@ -908,8 +939,10 @@
             UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
             EXECUTOR.execute(() -> {
                 try {
+                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
                     verifyUserUnlocked(callingUser);
-                    verifyCallingPackage(callingUser, callingUid, packageName);
+                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+                    verifyNotInstantApp(userContext, packageName);
                     AppSearchUserInstance instance =
                             mAppSearchUserInstanceManager.getUserInstance(callingUser);
 
@@ -957,8 +990,10 @@
                 int operationSuccessCount = 0;
                 int operationFailureCount = 0;
                 try {
+                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
                     verifyUserUnlocked(callingUser);
-                    verifyCallingPackage(callingUser, callingUid, packageName);
+                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+                    verifyNotInstantApp(userContext, packageName);
                     AppSearchBatchResult.Builder<String, Void> resultBuilder =
                             new AppSearchBatchResult.Builder<>();
                     instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
@@ -1039,8 +1074,10 @@
                 int operationSuccessCount = 0;
                 int operationFailureCount = 0;
                 try {
+                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
                     verifyUserUnlocked(callingUser);
-                    verifyCallingPackage(callingUser, callingUid, packageName);
+                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+                    verifyNotInstantApp(userContext, packageName);
                     instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
                     instance.getAppSearchImpl().removeByQuery(
                             packageName,
@@ -1095,8 +1132,10 @@
             UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
             EXECUTOR.execute(() -> {
                 try {
+                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
                     verifyUserUnlocked(callingUser);
-                    verifyCallingPackage(callingUser, callingUid, packageName);
+                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+                    verifyNotInstantApp(userContext, packageName);
                     AppSearchUserInstance instance =
                             mAppSearchUserInstanceManager.getUserInstance(callingUser);
                     StorageInfo storageInfo = instance.getAppSearchImpl()
@@ -1112,8 +1151,10 @@
 
         @Override
         public void persistToDisk(
+                @NonNull String packageName,
                 @NonNull UserHandle userHandle,
                 @ElapsedRealtimeLong long binderCallStartTimeMillis) {
+            Objects.requireNonNull(packageName);
             Objects.requireNonNull(userHandle);
 
             long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
@@ -1125,7 +1166,10 @@
                 int operationSuccessCount = 0;
                 int operationFailureCount = 0;
                 try {
+                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
                     verifyUserUnlocked(callingUser);
+                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+                    verifyNotInstantApp(userContext, packageName);
                     instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
                     instance.getAppSearchImpl().persistToDisk(PersistType.Code.FULL);
                     ++operationSuccessCount;
@@ -1157,24 +1201,30 @@
 
         @Override
         public void initialize(
+                @NonNull String packageName,
                 @NonNull UserHandle userHandle,
                 @ElapsedRealtimeLong long binderCallStartTimeMillis,
                 @NonNull IAppSearchResultCallback callback) {
+            Objects.requireNonNull(packageName);
             Objects.requireNonNull(userHandle);
             Objects.requireNonNull(callback);
 
             long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
             int callingUid = Binder.getCallingUid();
             UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
+
             EXECUTOR.execute(() -> {
                 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
                 AppSearchUserInstance instance = null;
                 int operationSuccessCount = 0;
                 int operationFailureCount = 0;
                 try {
+                    Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
                     verifyUserUnlocked(callingUser);
+                    verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+                    verifyNotInstantApp(userContext, packageName);
                     instance = mAppSearchUserInstanceManager.getOrCreateUserInstance(
-                            mContext, callingUser, AppSearchConfig.getInstance(EXECUTOR));
+                            userContext, callingUser, AppSearchConfig.getInstance(EXECUTOR));
                     ++operationSuccessCount;
                     invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
                 } catch (Throwable t) {
@@ -1204,14 +1254,15 @@
         }
 
         private void verifyCallingPackage(
+                @NonNull Context userContext,
                 @NonNull UserHandle actualCallingUser,
                 int actualCallingUid,
                 @NonNull String claimedCallingPackage) {
             Objects.requireNonNull(actualCallingUser);
             Objects.requireNonNull(claimedCallingPackage);
 
-            int claimedCallingUid = PackageUtil.getPackageUidAsUser(
-                    mContext, claimedCallingPackage, actualCallingUser);
+            int claimedCallingUid = PackageUtil.getPackageUid(
+                    userContext, claimedCallingPackage);
             if (claimedCallingUid == INVALID_UID) {
                 throw new SecurityException(
                         "Specified calling package [" + claimedCallingPackage + "] not found");
@@ -1317,6 +1368,21 @@
                         + Manifest.permission.INTERACT_ACROSS_USERS_FULL);
     }
 
+    /**
+     * Helper for ensuring instant apps can't make calls to AppSearch.
+     *
+     * @param userContext Context of the user making the call.
+     * @param packageName Package name of the caller.
+     * @throws SecurityException if the caller is an instant app.
+     */
+    private void verifyNotInstantApp(@NonNull Context userContext, @NonNull String packageName) {
+        PackageManager callingPackageManager = userContext.getPackageManager();
+        if (callingPackageManager.isInstantApp(packageName)) {
+            throw new SecurityException("Caller not allowed to create AppSearch session"
+                    + "; userHandle=" + userContext.getUser() + ", callingPackage=" + packageName);
+        }
+    }
+
     // TODO(b/179160886): Cache the previous storage stats.
     private class AppSearchStorageStatsAugmenter implements StorageStatsAugmenter {
         @Override
@@ -1331,9 +1397,10 @@
 
             try {
                 verifyUserUnlocked(userHandle);
+                Context userContext = mContext.createContextAsUser(userHandle, /*flags=*/ 0);
                 AppSearchUserInstance instance =
                         mAppSearchUserInstanceManager.getOrCreateUserInstance(
-                                mContext, userHandle, AppSearchConfig.getInstance(EXECUTOR));
+                                userContext, userHandle, AppSearchConfig.getInstance(EXECUTOR));
                 stats.dataSize += instance.getAppSearchImpl()
                         .getStorageInfoForPackage(packageName).getSizeBytes();
             } catch (Throwable t) {
@@ -1359,9 +1426,10 @@
                 if (packagesForUid == null) {
                     return;
                 }
+                Context userContext = mContext.createContextAsUser(userHandle, /*flags=*/ 0);
                 AppSearchUserInstance instance =
                         mAppSearchUserInstanceManager.getOrCreateUserInstance(
-                                mContext, userHandle, AppSearchConfig.getInstance(EXECUTOR));
+                                userContext, userHandle, AppSearchConfig.getInstance(EXECUTOR));
                 for (int i = 0; i < packagesForUid.length; i++) {
                     stats.dataSize += instance.getAppSearchImpl()
                             .getStorageInfoForPackage(packagesForUid[i]).getSizeBytes();
@@ -1387,9 +1455,10 @@
                 if (packagesForUser == null) {
                     return;
                 }
+                Context userContext = mContext.createContextAsUser(userHandle, /*flags=*/ 0);
                 AppSearchUserInstance instance =
                         mAppSearchUserInstanceManager.getOrCreateUserInstance(
-                                mContext, userHandle, AppSearchConfig.getInstance(EXECUTOR));
+                                userContext, userHandle, AppSearchConfig.getInstance(EXECUTOR));
                 for (int i = 0; i < packagesForUser.size(); i++) {
                     String packageName = packagesForUser.get(i).packageName;
                     stats.dataSize += instance.getAppSearchImpl()
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstance.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstance.java
index 7e743ed..56e2af5 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstance.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstance.java
@@ -19,7 +19,7 @@
 
 import com.android.server.appsearch.external.localstorage.AppSearchImpl;
 import com.android.server.appsearch.stats.PlatformLogger;
-import com.android.server.appsearch.visibilitystore.VisibilityStore;
+import com.android.server.appsearch.visibilitystore.VisibilityStoreImpl;
 
 import java.util.Objects;
 
@@ -30,12 +30,12 @@
 public final class AppSearchUserInstance {
     private final PlatformLogger mLogger;
     private final AppSearchImpl mAppSearchImpl;
-    private final VisibilityStore mVisibilityStore;
+    private final VisibilityStoreImpl mVisibilityStore;
 
     AppSearchUserInstance(
             @NonNull PlatformLogger logger,
             @NonNull AppSearchImpl appSearchImpl,
-            @NonNull VisibilityStore visibilityStore) {
+            @NonNull VisibilityStoreImpl visibilityStore) {
         mLogger = Objects.requireNonNull(logger);
         mAppSearchImpl = Objects.requireNonNull(appSearchImpl);
         mVisibilityStore = Objects.requireNonNull(visibilityStore);
@@ -52,7 +52,7 @@
     }
 
     @NonNull
-    public VisibilityStore getVisibilityStore() {
+    public VisibilityStoreImpl getVisibilityStore() {
         return mVisibilityStore;
     }
 }
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java
index cedc364..e067d4b 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java
@@ -30,7 +30,7 @@
 import com.android.server.appsearch.external.localstorage.FrameworkOptimizeStrategy;
 import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
 import com.android.server.appsearch.stats.PlatformLogger;
-import com.android.server.appsearch.visibilitystore.VisibilityStore;
+import com.android.server.appsearch.visibilitystore.VisibilityStoreImpl;
 
 import java.io.File;
 import java.util.Map;
@@ -89,25 +89,24 @@
      * <p>If no AppSearchUserInstance exists for the unlocked user, Icing will be initialized and
      * one will be created.
      *
-     * @param context The context
+     * @param userContext Context of the user calling AppSearch
      * @param userHandle The multi-user handle of the device user calling AppSearch
      * @param config Flag manager for AppSearch
      * @return An initialized {@link AppSearchUserInstance} for this user
      */
     @NonNull
     public AppSearchUserInstance getOrCreateUserInstance(
-            @NonNull Context context,
+            @NonNull Context userContext,
             @NonNull UserHandle userHandle,
             @NonNull AppSearchConfig config)
             throws AppSearchException {
-        Objects.requireNonNull(context);
+        Objects.requireNonNull(userContext);
         Objects.requireNonNull(userHandle);
         Objects.requireNonNull(config);
 
         synchronized (mInstancesLocked) {
             AppSearchUserInstance instance = mInstancesLocked.get(userHandle);
             if (instance == null) {
-                Context userContext = context.createContextAsUser(userHandle, /*flags=*/ 0);
                 instance = createUserInstance(userContext, userHandle, config);
                 mInstancesLocked.put(userHandle, instance);
             }
@@ -169,7 +168,7 @@
         InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
 
         // Initialize the classes that make up AppSearchUserInstance
-        PlatformLogger logger = new PlatformLogger(userContext, userHandle, config);
+        PlatformLogger logger = new PlatformLogger(userContext, config);
 
         File appSearchDir = getAppSearchDir(userHandle);
         File icingDir = new File(appSearchDir, "icing");
@@ -178,7 +177,8 @@
                 AppSearchImpl.create(icingDir, initStatsBuilder, new FrameworkOptimizeStrategy());
 
         long prepareVisibilityStoreLatencyStartMillis = SystemClock.elapsedRealtime();
-        VisibilityStore visibilityStore = VisibilityStore.create(appSearchImpl, userContext);
+        VisibilityStoreImpl visibilityStore =
+                VisibilityStoreImpl.create(appSearchImpl, userContext);
         long prepareVisibilityStoreLatencyEndMillis = SystemClock.elapsedRealtime();
 
         initStatsBuilder
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
index 77a1bb4..9dee179 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
@@ -58,7 +58,7 @@
 import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
 import com.android.server.appsearch.external.localstorage.stats.RemoveStats;
 import com.android.server.appsearch.external.localstorage.stats.SearchStats;
-import com.android.server.appsearch.visibilitystore.VisibilityStore;
+import com.android.server.appsearch.external.localstorage.visibilitystore.VisibilityStore;
 
 import com.google.android.icing.IcingSearchEngine;
 import com.google.android.icing.proto.DeleteByQueryResultProto;
@@ -346,11 +346,11 @@
      * @param packageName The package name that owns the schemas.
      * @param databaseName The name of the database where this schema lives.
      * @param schemas Schemas to set for this app.
-     * @param visibilityStore If set, {@code schemasNotPlatformSurfaceable} and {@code
-     *     schemasPackageAccessible} will be saved here if the schema is successfully applied.
-     * @param schemasNotPlatformSurfaceable Schema types that should not be surfaced on platform
+     * @param visibilityStore If set, {@code schemasNotDisplayedBySystem} and {@code
+     *     schemasVisibleToPackages} will be saved here if the schema is successfully applied.
+     * @param schemasNotDisplayedBySystem Schema types that should not be surfaced on platform
      *     surfaces.
-     * @param schemasPackageAccessible Schema types that are visible to the specified packages.
+     * @param schemasVisibleToPackages Schema types that are visible to the specified packages.
      * @param forceOverride Whether to force-apply the schema even if it is incompatible. Documents
      *     which do not comply with the new schema will be deleted.
      * @param version The overall version number of the request.
@@ -366,8 +366,8 @@
             @NonNull String databaseName,
             @NonNull List<AppSearchSchema> schemas,
             @Nullable VisibilityStore visibilityStore,
-            @NonNull List<String> schemasNotPlatformSurfaceable,
-            @NonNull Map<String, List<PackageIdentifier>> schemasPackageAccessible,
+            @NonNull List<String> schemasNotDisplayedBySystem,
+            @NonNull Map<String, List<PackageIdentifier>> schemasVisibleToPackages,
             boolean forceOverride,
             int version)
             throws AppSearchException {
@@ -430,25 +430,25 @@
             }
 
             if (visibilityStore != null) {
-                Set<String> prefixedSchemasNotPlatformSurfaceable =
-                        new ArraySet<>(schemasNotPlatformSurfaceable.size());
-                for (int i = 0; i < schemasNotPlatformSurfaceable.size(); i++) {
-                    prefixedSchemasNotPlatformSurfaceable.add(
-                            prefix + schemasNotPlatformSurfaceable.get(i));
+                Set<String> prefixedSchemasNotDisplayedBySystem =
+                        new ArraySet<>(schemasNotDisplayedBySystem.size());
+                for (int i = 0; i < schemasNotDisplayedBySystem.size(); i++) {
+                    prefixedSchemasNotDisplayedBySystem.add(
+                            prefix + schemasNotDisplayedBySystem.get(i));
                 }
 
-                Map<String, List<PackageIdentifier>> prefixedSchemasPackageAccessible =
-                        new ArrayMap<>(schemasPackageAccessible.size());
+                Map<String, List<PackageIdentifier>> prefixedSchemasVisibleToPackages =
+                        new ArrayMap<>(schemasVisibleToPackages.size());
                 for (Map.Entry<String, List<PackageIdentifier>> entry :
-                        schemasPackageAccessible.entrySet()) {
-                    prefixedSchemasPackageAccessible.put(prefix + entry.getKey(), entry.getValue());
+                        schemasVisibleToPackages.entrySet()) {
+                    prefixedSchemasVisibleToPackages.put(prefix + entry.getKey(), entry.getValue());
                 }
 
                 visibilityStore.setVisibility(
                         packageName,
                         databaseName,
-                        prefixedSchemasNotPlatformSurfaceable,
-                        prefixedSchemasPackageAccessible);
+                        prefixedSchemasNotDisplayedBySystem,
+                        prefixedSchemasVisibleToPackages);
             }
 
             return SetSchemaResponseToProtoConverter.toSetSchemaResponse(
@@ -737,6 +737,9 @@
             if (!filterPackageNames.isEmpty() && !filterPackageNames.contains(packageName)) {
                 // Client wanted to query over some packages that weren't its own. This isn't
                 // allowed through local query so we can return early with no results.
+                if (logger != null) {
+                    sStatsBuilder.setStatusCode(AppSearchResult.RESULT_SECURITY_ERROR);
+                }
                 return new SearchResultPage(Bundle.EMPTY);
             }
 
@@ -768,7 +771,7 @@
      * @param queryExpression Query String to search.
      * @param searchSpec Spec for setting filters, raw query etc.
      * @param callerPackageName Package name of the caller, should belong to the {@code
-     *     userContext}.
+     *     callerUserHandle}.
      * @param visibilityStore Optional visibility store to obtain system and package visibility
      *     settings from
      * @param callerUid UID of the client making the globalQuery call.
@@ -1465,7 +1468,6 @@
         mOptimizeIntervalCountLocked = 0;
         mSchemaMapLocked.clear();
         mNamespaceMapLocked.clear();
-
         if (initStatsBuilder != null) {
             initStatsBuilder
                     .setHasReset(true)
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SchemaMigrationStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SchemaMigrationStats.java
new file mode 100644
index 0000000..6e1e2d5
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SchemaMigrationStats.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appsearch.external.localstorage.stats;
+
+import android.annotation.NonNull;
+import android.app.appsearch.SetSchemaRequest;
+
+import java.util.Objects;
+
+/**
+ * Class holds detailed stats for Schema migration.
+ *
+ * @hide
+ */
+// TODO(b/173532925): Hides getter and setter functions for accessing {@code
+//  mFirstSetSchemaLatencyMillis} and {@code mSecondSetSchemaLatencyMillis} field.
+
+public final class SchemaMigrationStats {
+    /** GetSchema latency in milliseconds. */
+    private final int mGetSchemaLatencyMillis;
+
+    /**
+     * Latency of querying all documents that need to be migrated to new version and transforming
+     * documents to new version in milliseconds.
+     */
+    private final int mQueryAndTransformLatencyMillis;
+
+    private final int mFirstSetSchemaLatencyMillis;
+
+    private final int mSecondSetSchemaLatencyMillis;
+
+    /** Latency of putting migrated document to Icing lib in milliseconds. */
+    private final int mSaveDocumentLatencyMillis;
+
+    private final int mMigratedDocumentCount;
+
+    private final int mSavedDocumentCount;
+
+    SchemaMigrationStats(@NonNull Builder builder) {
+        Objects.requireNonNull(builder);
+        mGetSchemaLatencyMillis = builder.mGetSchemaLatencyMillis;
+        mQueryAndTransformLatencyMillis = builder.mQueryAndTransformLatencyMillis;
+        mFirstSetSchemaLatencyMillis = builder.mFirstSetSchemaLatencyMillis;
+        mSecondSetSchemaLatencyMillis = builder.mSecondSetSchemaLatencyMillis;
+        mSaveDocumentLatencyMillis = builder.mSaveDocumentLatencyMillis;
+        mMigratedDocumentCount = builder.mMigratedDocumentCount;
+        mSavedDocumentCount = builder.mSavedDocumentCount;
+    }
+
+    /** Returns GetSchema latency in milliseconds. */
+    public int getGetSchemaLatencyMillis() {
+        return mGetSchemaLatencyMillis;
+    }
+
+    /**
+     * Returns latency of querying all documents that need to be migrated to new version and
+     * transforming documents to new version in milliseconds.
+     */
+    public int getQueryAndTransformLatencyMillis() {
+        return mQueryAndTransformLatencyMillis;
+    }
+
+    /**
+     * Returns latency of first SetSchema action in milliseconds.
+     *
+     * <p>If all schema fields are backward compatible, the schema will be successful set to Icing.
+     * Otherwise, we will retrieve incompatible types here.
+     *
+     * <p>Please see {@link SetSchemaRequest} for what is "incompatible".
+     */
+    public int getFirstSetSchemaLatencyMillis() {
+        return mFirstSetSchemaLatencyMillis;
+    }
+
+    /**
+     * Returns latency of second SetSchema action in milliseconds.
+     *
+     * <p>If all schema fields are backward compatible, the schema will be successful set to Icing
+     * in the first setSchema action and this value will be 0. Otherwise, schema types will be set
+     * to Icing by this action.
+     */
+    public int getSecondSetSchemaLatencyMillis() {
+        return mSecondSetSchemaLatencyMillis;
+    }
+
+    /** Returns latency of putting migrated document to Icing lib in milliseconds. */
+    public int getSaveDocumentLatencyMillis() {
+        return mSaveDocumentLatencyMillis;
+    }
+
+    /** Returns number of migrated documents. */
+    public int getMigratedDocumentCount() {
+        return mMigratedDocumentCount;
+    }
+
+    /** Returns number of updated documents which are saved in Icing lib. */
+    public int getSavedDocumentCount() {
+        return mSavedDocumentCount;
+    }
+
+    /** Builder for {@link SchemaMigrationStats}. */
+    public static class Builder {
+        int mGetSchemaLatencyMillis;
+        int mQueryAndTransformLatencyMillis;
+        int mFirstSetSchemaLatencyMillis;
+        int mSecondSetSchemaLatencyMillis;
+        int mSaveDocumentLatencyMillis;
+        int mMigratedDocumentCount;
+        int mSavedDocumentCount;
+
+        /** Sets latency for the GetSchema action in milliseconds. */
+        @NonNull
+        public SchemaMigrationStats.Builder setGetSchemaLatencyMillis(int getSchemaLatencyMillis) {
+            mGetSchemaLatencyMillis = getSchemaLatencyMillis;
+            return this;
+        }
+
+        /**
+         * Sets latency for querying all documents that need to be migrated to new version and
+         * transforming documents to new version in milliseconds.
+         */
+        @NonNull
+        public SchemaMigrationStats.Builder setQueryAndTransformLatencyMillis(
+                int queryAndTransformLatencyMillis) {
+            mQueryAndTransformLatencyMillis = queryAndTransformLatencyMillis;
+            return this;
+        }
+
+        /** Sets latency of first SetSchema action in milliseconds. */
+        @NonNull
+        public SchemaMigrationStats.Builder setFirstSetSchemaLatencyMillis(
+                int firstSetSchemaLatencyMillis) {
+            mFirstSetSchemaLatencyMillis = firstSetSchemaLatencyMillis;
+            return this;
+        }
+
+        /** Sets latency of second SetSchema action in milliseconds. */
+        @NonNull
+        public SchemaMigrationStats.Builder setSecondSetSchemaLatencyMillis(
+                int secondSetSchemaLatencyMillis) {
+            mSecondSetSchemaLatencyMillis = secondSetSchemaLatencyMillis;
+            return this;
+        }
+
+        /** Sets latency for putting migrated document to Icing lib in milliseconds. */
+        @NonNull
+        public SchemaMigrationStats.Builder setSaveDocumentLatencyMillis(
+                int saveDocumentLatencyMillis) {
+            mSaveDocumentLatencyMillis = saveDocumentLatencyMillis;
+            return this;
+        }
+
+        /** Sets number of migrated documents. */
+        @NonNull
+        public SchemaMigrationStats.Builder setMigratedDocumentCount(int migratedDocumentCount) {
+            mMigratedDocumentCount = migratedDocumentCount;
+            return this;
+        }
+
+        /** Sets number of updated documents which are saved in Icing lib. */
+        @NonNull
+        public SchemaMigrationStats.Builder setSavedDocumentCount(int savedDocumentCount) {
+            mSavedDocumentCount = savedDocumentCount;
+            return this;
+        }
+
+        /**
+         * Builds a new {@link SchemaMigrationStats} from the {@link SchemaMigrationStats.Builder}.
+         */
+        @NonNull
+        public SchemaMigrationStats build() {
+            return new SchemaMigrationStats(/* builder= */ this);
+        }
+    }
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SetSchemaStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SetSchemaStats.java
index 56a546a..9d789a8 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SetSchemaStats.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SetSchemaStats.java
@@ -17,6 +17,7 @@
 package com.android.server.appsearch.external.localstorage.stats;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.appsearch.AppSearchResult;
 
 import java.util.Objects;
@@ -38,6 +39,12 @@
      */
     @AppSearchResult.ResultCode private final int mStatusCode;
 
+    /**
+     * Stores stats of SchemaMigration in SetSchema process. Is {@code null} if no schema migration
+     * is needed.
+     */
+    @Nullable private final SchemaMigrationStats mSchemaMigrationStats;
+
     private final int mTotalLatencyMillis;
 
     /** Overall time used for the native function call. */
@@ -63,6 +70,7 @@
         mPackageName = builder.mPackageName;
         mDatabase = builder.mDatabase;
         mStatusCode = builder.mStatusCode;
+        mSchemaMigrationStats = builder.mSchemaMigrationStats;
         mTotalLatencyMillis = builder.mTotalLatencyMillis;
         mNativeLatencyMillis = builder.mNativeLatencyMillis;
         mNewTypeCount = builder.mNewTypeCount;
@@ -90,6 +98,15 @@
         return mStatusCode;
     }
 
+    /**
+     * Returns the status of schema migration, if migration is executed during the SetSchema
+     * process. Otherwise, returns {@code null}.
+     */
+    @Nullable
+    public SchemaMigrationStats getSchemaMigrationStats() {
+        return mSchemaMigrationStats;
+    }
+
     /** Returns the total latency of the SetSchema action. */
     public int getTotalLatencyMillis() {
         return mTotalLatencyMillis;
@@ -140,6 +157,7 @@
         @NonNull final String mPackageName;
         @NonNull final String mDatabase;
         @AppSearchResult.ResultCode int mStatusCode;
+        @Nullable SchemaMigrationStats mSchemaMigrationStats;
         int mTotalLatencyMillis;
         int mNativeLatencyMillis;
         int mNewTypeCount;
@@ -161,7 +179,14 @@
             return this;
         }
 
-        /** Sets total latency for the SetSchema action. */
+        /** Sets the status of schema migration. */
+        @NonNull
+        public Builder setSchemaMigrationStats(@NonNull SchemaMigrationStats schemaMigrationStats) {
+            mSchemaMigrationStats = Objects.requireNonNull(schemaMigrationStats);
+            return this;
+        }
+
+        /** Sets total latency for the SetSchema action in milliseconds. */
         @NonNull
         public Builder setTotalLatencyMillis(int totalLatencyMillis) {
             mTotalLatencyMillis = totalLatencyMillis;
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStore.java
new file mode 100644
index 0000000..fb89250
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStore.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.appsearch.external.localstorage.visibilitystore;
+
+import android.annotation.NonNull;
+import android.app.appsearch.PackageIdentifier;
+import android.app.appsearch.exceptions.AppSearchException;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * An interface for classes that store and validate document visibility data.
+ *
+ * @hide
+ */
+public interface VisibilityStore {
+    /**
+     * These cannot have any of the special characters used by AppSearchImpl (e.g. {@code
+     * AppSearchImpl#PACKAGE_DELIMITER} or {@code AppSearchImpl#DATABASE_DELIMITER}.
+     */
+    String PACKAGE_NAME = "VS#Pkg";
+
+    @VisibleForTesting String DATABASE_NAME = "VS#Db";
+
+    /**
+     * Sets visibility settings for the given database. Any previous visibility settings will be
+     * overwritten.
+     *
+     * @param packageName Package of app that owns the schemas.
+     * @param databaseName Database that owns the schemas.
+     * @param schemasNotDisplayedBySystem Set of prefixed schemas that should be hidden from
+     *     platform surfaces.
+     * @param schemasVisibleToPackages Map of prefixed schemas to a list of package identifiers that
+     *     have access to the schema.
+     * @throws AppSearchException on AppSearchImpl error.
+     */
+    void setVisibility(
+            @NonNull String packageName,
+            @NonNull String databaseName,
+            @NonNull Set<String> schemasNotDisplayedBySystem,
+            @NonNull Map<String, List<PackageIdentifier>> schemasVisibleToPackages)
+            throws AppSearchException;
+
+    /**
+     * Checks whether the given package has access to system-surfaceable schemas.
+     *
+     * @param callerUid UID of the app that wants to see the data.
+     */
+    boolean isSchemaSearchableByCaller(
+            @NonNull String packageName,
+            @NonNull String databaseName,
+            @NonNull String prefixedSchema,
+            int callerUid,
+            boolean callerHasSystemAccess);
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java b/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
index 31fead5e..322bd11 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
@@ -22,7 +22,6 @@
 import android.content.Context;
 import android.os.Process;
 import android.os.SystemClock;
-import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.SparseIntArray;
@@ -55,11 +54,8 @@
 public final class PlatformLogger implements AppSearchLogger {
     private static final String TAG = "AppSearchPlatformLogger";
 
-    // Context of the system service.
-    private final Context mContext;
-
-    // User we're logging for.
-    private final UserHandle mUserHandle;
+    // Context of the user we're logging for.
+    private final Context mUserContext;
 
     // Manager holding the configuration flags
     private final AppSearchConfig mConfig;
@@ -120,10 +116,9 @@
      * Westworld constructor
      */
     public PlatformLogger(
-            @NonNull Context context, @NonNull UserHandle userHandle,
+            @NonNull Context userContext,
             @NonNull AppSearchConfig config) {
-        mContext = Objects.requireNonNull(context);
-        mUserHandle = Objects.requireNonNull(userHandle);
+        mUserContext = Objects.requireNonNull(userContext);
         mConfig = Objects.requireNonNull(config);
     }
 
@@ -451,7 +446,7 @@
     private int getPackageUidAsUserLocked(@NonNull String packageName) {
         Integer packageUid = mPackageUidCacheLocked.get(packageName);
         if (packageUid == null) {
-            packageUid = PackageUtil.getPackageUidAsUser(mContext, packageName, mUserHandle);
+            packageUid = PackageUtil.getPackageUid(mUserContext, packageName);
             if (packageUid != Process.INVALID_UID) {
                 mPackageUidCacheLocked.put(packageName, packageUid);
             }
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/util/PackageUtil.java b/apex/appsearch/service/java/com/android/server/appsearch/util/PackageUtil.java
index 53a1bed..714ffb6 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/util/PackageUtil.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/util/PackageUtil.java
@@ -20,7 +20,6 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Process;
-import android.os.UserHandle;
 
 /**
  * Utilities for interacting with {@link android.content.pm.PackageManager},
@@ -32,16 +31,6 @@
     private PackageUtil() {}
 
     /**
-     * Finds the UID of the {@code packageName}. Returns {@link Process#INVALID_UID} if unable to
-     * find the UID.
-     */
-    public static int getPackageUidAsUser(
-            @NonNull Context context, @NonNull String packageName, @NonNull UserHandle user) {
-        Context userContext = context.createContextAsUser(user, /*flags=*/ 0);
-        return getPackageUid(userContext, packageName);
-    }
-
-    /**
      * Finds the UID of the {@code packageName} in the given {@code context}. Returns
      * {@link Process#INVALID_UID} if unable to find the UID.
      */
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotPlatformSurfaceableMap.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotDisplayedBySystemMap.java
similarity index 95%
rename from apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotPlatformSurfaceableMap.java
rename to apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotDisplayedBySystemMap.java
index 9e36fd0..95905af 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotPlatformSurfaceableMap.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotDisplayedBySystemMap.java
@@ -27,7 +27,7 @@
  *
  * This object is not thread safe.
  */
-class NotPlatformSurfaceableMap {
+class NotDisplayedBySystemMap {
     /**
      * Maps packages to databases to the set of prefixed schemas that are platform-hidden within
      * that database.
@@ -39,7 +39,7 @@
      *
      * <p>Any existing mappings for this prefix are overwritten.
      */
-    public void setNotPlatformSurfaceable(
+    public void setNotDisplayedBySystem(
             @NonNull String packageName,
             @NonNull String databaseName,
             @NonNull Set<String> prefixedSchemas) {
@@ -55,7 +55,7 @@
      * Returns whether the given prefixed schema is platform surfaceable (has not opted out) in the
      * given database.
      */
-    public boolean isSchemaPlatformSurfaceable(
+    public boolean isSchemaDisplayedBySystem(
             @NonNull String packageName,
             @NonNull String databaseName,
             @NonNull String prefixedSchema) {
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityDocument.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityDocument.java
index 1771b1d..b010870 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityDocument.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityDocument.java
@@ -28,10 +28,10 @@
     /**
      * Property that holds the list of platform-hidden schemas, as part of the visibility settings.
      */
-    private static final String NOT_PLATFORM_SURFACEABLE_PROPERTY = "notPlatformSurfaceable";
+    private static final String NOT_DISPLAYED_BY_SYSTEM_PROPERTY = "notPlatformSurfaceable";
 
     /** Property that holds nested documents of package accessible schemas. */
-    private static final String PACKAGE_ACCESSIBLE_PROPERTY = "packageAccessible";
+    private static final String VISIBLE_TO_PACKAGES_PROPERTY = "packageAccessible";
 
     /**
      * Schema for the VisibilityStore's documents.
@@ -41,11 +41,11 @@
      */
     public static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE)
             .addProperty(new AppSearchSchema.StringPropertyConfig.Builder(
-                    NOT_PLATFORM_SURFACEABLE_PROPERTY)
+                    NOT_DISPLAYED_BY_SYSTEM_PROPERTY)
                     .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
                     .build())
             .addProperty(new AppSearchSchema.DocumentPropertyConfig.Builder(
-                    PACKAGE_ACCESSIBLE_PROPERTY, PackageAccessibleDocument.SCHEMA_TYPE)
+                    VISIBLE_TO_PACKAGES_PROPERTY, VisibleToPackagesDocument.SCHEMA_TYPE)
                     .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
                     .build())
             .build();
@@ -55,13 +55,13 @@
     }
 
     @Nullable
-    public String[] getNotPlatformSurfaceableSchemas() {
-        return getPropertyStringArray(NOT_PLATFORM_SURFACEABLE_PROPERTY);
+    public String[] getNotDisplayedBySystem() {
+        return getPropertyStringArray(NOT_DISPLAYED_BY_SYSTEM_PROPERTY);
     }
 
     @Nullable
-    public GenericDocument[] getPackageAccessibleSchemas() {
-        return getPropertyDocumentArray(PACKAGE_ACCESSIBLE_PROPERTY);
+    public GenericDocument[] getVisibleToPackages() {
+        return getPropertyDocumentArray(VISIBLE_TO_PACKAGES_PROPERTY);
     }
 
     /** Builder for {@link VisibilityDocument}. */
@@ -72,17 +72,15 @@
 
         /** Sets which prefixed schemas have opted out of platform surfacing. */
         @NonNull
-        public Builder setSchemasNotPlatformSurfaceable(
-                @NonNull String[] notPlatformSurfaceableSchemas) {
-            return setPropertyString(
-                    NOT_PLATFORM_SURFACEABLE_PROPERTY, notPlatformSurfaceableSchemas);
+        public Builder setNotDisplayedBySystem(@NonNull String[] notDisplayedBySystemSchemas) {
+            return setPropertyString(NOT_DISPLAYED_BY_SYSTEM_PROPERTY, notDisplayedBySystemSchemas);
         }
 
         /** Sets which prefixed schemas have configured package access. */
         @NonNull
-        public Builder setPackageAccessibleSchemas(
-                @NonNull PackageAccessibleDocument[] packageAccessibleSchemas) {
-            return setPropertyDocument(PACKAGE_ACCESSIBLE_PROPERTY, packageAccessibleSchemas);
+        public Builder setVisibleToPackages(
+                @NonNull VisibleToPackagesDocument[] visibleToPackagesDocuments) {
+            return setPropertyDocument(VISIBLE_TO_PACKAGES_PROPERTY, visibleToPackagesDocuments);
         }
     }
 }
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStoreImpl.java
similarity index 70%
rename from apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStore.java
rename to apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStoreImpl.java
index ae1ec56..ce142d6 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStore.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStoreImpl.java
@@ -30,9 +30,9 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.appsearch.external.localstorage.AppSearchImpl;
 import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
+import com.android.server.appsearch.external.localstorage.visibilitystore.VisibilityStore;
 import com.android.server.appsearch.util.PackageUtil;
 
 import com.google.android.icing.proto.PersistType;
@@ -60,26 +60,12 @@
  * <p>This class doesn't handle any locking itself. Its callers should handle the locking at a
  * higher level.
  *
- * <p>NOTE: This class holds an instance of AppSearchImpl and AppSearchImpl holds an instance of
- * this class. Take care to not cause any circular dependencies.
- *
  * @hide
  */
-public class VisibilityStore {
-    /** No-op uid that won't have any visibility settings. */
-    public static final int NO_OP_UID = -1;
-
+public class VisibilityStoreImpl implements VisibilityStore {
     /** Version for the visibility schema */
     private static final int SCHEMA_VERSION = 0;
 
-    /**
-     * These cannot have any of the special characters used by AppSearchImpl (e.g. {@code
-     * AppSearchImpl#PACKAGE_DELIMITER} or {@code AppSearchImpl#DATABASE_DELIMITER}.
-     */
-    public static final String PACKAGE_NAME = "VS#Pkg";
-
-    @VisibleForTesting public static final String DATABASE_NAME = "VS#Db";
-
     /** Namespace of documents that contain visibility settings */
     private static final String NAMESPACE = "";
 
@@ -92,11 +78,10 @@
     private final Context mUserContext;
 
     /** Stores the schemas that are platform-hidden. All values are prefixed. */
-    private final NotPlatformSurfaceableMap mNotPlatformSurfaceableMap =
-            new NotPlatformSurfaceableMap();
+    private final NotDisplayedBySystemMap mNotDisplayedBySystemMap = new NotDisplayedBySystemMap();
 
-    /** Stores the schemas that are package accessible. All values are prefixed. */
-    private final PackageAccessibleMap mPackageAccessibleMap = new PackageAccessibleMap();
+    /** Stores the schemas that are visible to 3p packages. All values are prefixed. */
+    private final VisibleToPackagesMap mVisibleToPackagesMap = new VisibleToPackagesMap();
 
     /**
      * Creates and initializes VisibilityStore.
@@ -105,41 +90,41 @@
      * @param userContext Context of the user that the call is being made as
      */
     @NonNull
-    public static VisibilityStore create(
+    public static VisibilityStoreImpl create(
             @NonNull AppSearchImpl appSearchImpl, @NonNull Context userContext)
             throws AppSearchException {
-        return new VisibilityStore(appSearchImpl, userContext);
+        return new VisibilityStoreImpl(appSearchImpl, userContext);
     }
 
-    private VisibilityStore(@NonNull AppSearchImpl appSearchImpl, @NonNull Context userContext)
+    private VisibilityStoreImpl(@NonNull AppSearchImpl appSearchImpl, @NonNull Context userContext)
             throws AppSearchException {
         mAppSearchImpl = Objects.requireNonNull(appSearchImpl);
         mUserContext = Objects.requireNonNull(userContext);
 
         GetSchemaResponse getSchemaResponse = mAppSearchImpl.getSchema(PACKAGE_NAME, DATABASE_NAME);
         boolean hasVisibilityType = false;
-        boolean hasPackageAccessibleType = false;
+        boolean hasVisibleToPackagesType = false;
         for (AppSearchSchema schema : getSchemaResponse.getSchemas()) {
             if (schema.getSchemaType().equals(VisibilityDocument.SCHEMA_TYPE)) {
                 hasVisibilityType = true;
-            } else if (schema.getSchemaType().equals(PackageAccessibleDocument.SCHEMA_TYPE)) {
-                hasPackageAccessibleType = true;
+            } else if (schema.getSchemaType().equals(VisibleToPackagesDocument.SCHEMA_TYPE)) {
+                hasVisibleToPackagesType = true;
             }
 
-            if (hasVisibilityType && hasPackageAccessibleType) {
+            if (hasVisibilityType && hasVisibleToPackagesType) {
                 // Found both our types, can exit early.
                 break;
             }
         }
-        if (!hasVisibilityType || !hasPackageAccessibleType) {
+        if (!hasVisibilityType || !hasVisibleToPackagesType) {
             // Schema type doesn't exist yet. Add it.
             mAppSearchImpl.setSchema(
                     PACKAGE_NAME,
                     DATABASE_NAME,
-                    Arrays.asList(VisibilityDocument.SCHEMA, PackageAccessibleDocument.SCHEMA),
+                    Arrays.asList(VisibilityDocument.SCHEMA, VisibleToPackagesDocument.SCHEMA),
                     /*visibilityStore=*/ null,  // Avoid recursive calls
-                    /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                    /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                    /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                    /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                     /*forceOverride=*/ false,
                     /*version=*/ SCHEMA_VERSION);
         }
@@ -177,26 +162,25 @@
                 }
 
                 // Update platform visibility settings
-                String[] notPlatformSurfaceableSchemas =
-                        visibilityDocument.getNotPlatformSurfaceableSchemas();
-                if (notPlatformSurfaceableSchemas != null) {
-                    mNotPlatformSurfaceableMap.setNotPlatformSurfaceable(
+                String[] notDisplayedBySystemSchemas = visibilityDocument.getNotDisplayedBySystem();
+                if (notDisplayedBySystemSchemas != null) {
+                    mNotDisplayedBySystemMap.setNotDisplayedBySystem(
                             packageName,
                             databaseName,
-                            new ArraySet<>(notPlatformSurfaceableSchemas));
+                            new ArraySet<>(notDisplayedBySystemSchemas));
                 }
 
                 // Update 3p package visibility settings
                 Map<String, Set<PackageIdentifier>> schemaToPackageIdentifierMap = new ArrayMap<>();
-                GenericDocument[] packageAccessibleDocuments =
-                        visibilityDocument.getPackageAccessibleSchemas();
-                if (packageAccessibleDocuments != null) {
-                    for (int i = 0; i < packageAccessibleDocuments.length; i++) {
-                        PackageAccessibleDocument packageAccessibleDocument =
-                                new PackageAccessibleDocument(packageAccessibleDocuments[i]);
+                GenericDocument[] visibleToPackagesDocuments =
+                        visibilityDocument.getVisibleToPackages();
+                if (visibleToPackagesDocuments != null) {
+                    for (int i = 0; i < visibleToPackagesDocuments.length; i++) {
+                        VisibleToPackagesDocument visibleToPackagesDocument =
+                                new VisibleToPackagesDocument(visibleToPackagesDocuments[i]);
                         PackageIdentifier packageIdentifier =
-                                packageAccessibleDocument.getPackageIdentifier();
-                        String prefixedSchema = packageAccessibleDocument.getAccessibleSchemaType();
+                                visibleToPackagesDocument.getPackageIdentifier();
+                        String prefixedSchema = visibleToPackagesDocument.getAccessibleSchemaType();
                         Set<PackageIdentifier> packageIdentifiers =
                                 schemaToPackageIdentifierMap.get(prefixedSchema);
                         if (packageIdentifiers == null) {
@@ -206,61 +190,50 @@
                         schemaToPackageIdentifierMap.put(prefixedSchema, packageIdentifiers);
                     }
                 }
-                mPackageAccessibleMap.setPackageAccessible(
+                mVisibleToPackagesMap.setVisibleToPackages(
                         packageName, databaseName, schemaToPackageIdentifierMap);
             }
         }
     }
 
-    /**
-     * Sets visibility settings for the given database. Any previous visibility settings will be
-     * overwritten.
-     *
-     * @param packageName Package of app that owns the {@code schemasNotPlatformSurfaceable}.
-     * @param databaseName Database that owns the {@code schemasNotPlatformSurfaceable}.
-     * @param schemasNotPlatformSurfaceable Set of prefixed schemas that should be hidden from the
-     *     platform.
-     * @param schemasPackageAccessible Map of prefixed schemas to a list of package identifiers that
-     *     have access to the schema.
-     * @throws AppSearchException on AppSearchImpl error.
-     */
+    @Override
     public void setVisibility(
             @NonNull String packageName,
             @NonNull String databaseName,
-            @NonNull Set<String> schemasNotPlatformSurfaceable,
-            @NonNull Map<String, List<PackageIdentifier>> schemasPackageAccessible)
+            @NonNull Set<String> schemasNotDisplayedBySystem,
+            @NonNull Map<String, List<PackageIdentifier>> schemasVisibleToPackages)
             throws AppSearchException {
         Objects.requireNonNull(packageName);
         Objects.requireNonNull(databaseName);
-        Objects.requireNonNull(schemasNotPlatformSurfaceable);
-        Objects.requireNonNull(schemasPackageAccessible);
+        Objects.requireNonNull(schemasNotDisplayedBySystem);
+        Objects.requireNonNull(schemasVisibleToPackages);
 
         // Persist the document
         VisibilityDocument.Builder visibilityDocument =
                 new VisibilityDocument.Builder(
                         NAMESPACE, /*id=*/ getVisibilityDocumentId(packageName, databaseName));
-        if (!schemasNotPlatformSurfaceable.isEmpty()) {
-            visibilityDocument.setSchemasNotPlatformSurfaceable(
-                    schemasNotPlatformSurfaceable.toArray(new String[0]));
+        if (!schemasNotDisplayedBySystem.isEmpty()) {
+            visibilityDocument.setNotDisplayedBySystem(
+                    schemasNotDisplayedBySystem.toArray(new String[0]));
         }
 
         Map<String, Set<PackageIdentifier>> schemaToPackageIdentifierMap = new ArrayMap<>();
-        List<PackageAccessibleDocument> packageAccessibleDocuments = new ArrayList<>();
+        List<VisibleToPackagesDocument> visibleToPackagesDocuments = new ArrayList<>();
         for (Map.Entry<String, List<PackageIdentifier>> entry :
-                schemasPackageAccessible.entrySet()) {
+                schemasVisibleToPackages.entrySet()) {
             for (int i = 0; i < entry.getValue().size(); i++) {
-                PackageAccessibleDocument packageAccessibleDocument =
-                        new PackageAccessibleDocument.Builder(NAMESPACE, /*id=*/ "")
+                VisibleToPackagesDocument visibleToPackagesDocument =
+                        new VisibleToPackagesDocument.Builder(NAMESPACE, /*id=*/ "")
                                 .setAccessibleSchemaType(entry.getKey())
                                 .setPackageIdentifier(entry.getValue().get(i))
                                 .build();
-                packageAccessibleDocuments.add(packageAccessibleDocument);
+                visibleToPackagesDocuments.add(visibleToPackagesDocument);
             }
             schemaToPackageIdentifierMap.put(entry.getKey(), new ArraySet<>(entry.getValue()));
         }
-        if (!packageAccessibleDocuments.isEmpty()) {
-            visibilityDocument.setPackageAccessibleSchemas(
-                    packageAccessibleDocuments.toArray(new PackageAccessibleDocument[0]));
+        if (!visibleToPackagesDocuments.isEmpty()) {
+            visibilityDocument.setVisibleToPackages(
+                    visibleToPackagesDocuments.toArray(new VisibleToPackagesDocument[0]));
         }
 
         mAppSearchImpl.putDocument(
@@ -269,9 +242,9 @@
         mAppSearchImpl.persistToDisk(PersistType.Code.LITE);
 
         // Update derived data structures.
-        mNotPlatformSurfaceableMap.setNotPlatformSurfaceable(
-                packageName, databaseName, schemasNotPlatformSurfaceable);
-        mPackageAccessibleMap.setPackageAccessible(
+        mNotDisplayedBySystemMap.setNotDisplayedBySystem(
+                packageName, databaseName, schemasNotDisplayedBySystem);
+        mVisibleToPackagesMap.setVisibleToPackages(
                 packageName, databaseName, schemaToPackageIdentifierMap);
     }
 
@@ -287,17 +260,7 @@
                 == PackageManager.PERMISSION_GRANTED;
     }
 
-    /**
-     * Checks whether {@code prefixedSchema} can be searched over by the {@code callerUid}.
-     *
-     * @param packageName Package that owns the schema.
-     * @param databaseName Database within the package that owns the schema.
-     * @param prefixedSchema Prefixed schema type the caller is trying to access.
-     * @param callerUid UID of the client making the globalQuery call.
-     * @param callerHasSystemAccess Whether the caller has been identified as having
-     *                              access to schemas marked system surfaceable by {@link
-     *                              #doesCallerHaveSystemAccess}.
-     */
+    @Override
     public boolean isSchemaSearchableByCaller(
             @NonNull String packageName,
             @NonNull String databaseName,
@@ -313,13 +276,13 @@
         }
 
         if (callerHasSystemAccess
-                && mNotPlatformSurfaceableMap.isSchemaPlatformSurfaceable(
+                && mNotDisplayedBySystemMap.isSchemaDisplayedBySystem(
                 packageName, databaseName, prefixedSchema)) {
             return true;
         }
 
         // May not be platform surfaceable, but might still be accessible through 3p access.
-        return isSchemaPackageAccessible(packageName, databaseName, prefixedSchema, callerUid);
+        return isSchemaVisibleToPackages(packageName, databaseName, prefixedSchema, callerUid);
     }
 
     /**
@@ -331,13 +294,13 @@
      * certificate was once used to sign the package, the package will still be granted access. This
      * does not handle packages that have been signed by multiple certificates.
      */
-    private boolean isSchemaPackageAccessible(
+    private boolean isSchemaVisibleToPackages(
             @NonNull String packageName,
             @NonNull String databaseName,
             @NonNull String prefixedSchema,
             int callerUid) {
         Set<PackageIdentifier> packageIdentifiers =
-                mPackageAccessibleMap.getAccessiblePackages(
+                mVisibleToPackagesMap.getAccessiblePackages(
                         packageName, databaseName, prefixedSchema);
         if (packageIdentifiers.isEmpty()) {
             return false;
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleDocument.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibleToPackagesDocument.java
similarity index 90%
rename from apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleDocument.java
rename to apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibleToPackagesDocument.java
index 0b4e196..8d50339 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleDocument.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibleToPackagesDocument.java
@@ -26,7 +26,7 @@
  *
  * @see android.app.appsearch.SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage
  */
-class PackageAccessibleDocument extends GenericDocument {
+class VisibleToPackagesDocument extends GenericDocument {
     /** Schema type for nested documents that hold package accessible information. */
     public static final String SCHEMA_TYPE = "PackageAccessibleType";
 
@@ -59,7 +59,7 @@
                     .build())
             .build();
 
-    public PackageAccessibleDocument(@NonNull GenericDocument genericDocument) {
+    VisibleToPackagesDocument(@NonNull GenericDocument genericDocument) {
         super(genericDocument);
     }
 
@@ -76,9 +76,9 @@
         return new PackageIdentifier(packageName, sha256Cert);
     }
 
-    /** Builder for {@link PackageAccessibleDocument} instances. */
-    public static class Builder extends GenericDocument.Builder<PackageAccessibleDocument.Builder> {
-        public Builder(@NonNull String namespace, @NonNull String id) {
+    /** Builder for {@link VisibleToPackagesDocument} instances. */
+    public static class Builder extends GenericDocument.Builder<VisibleToPackagesDocument.Builder> {
+        Builder(@NonNull String namespace, @NonNull String id) {
             super(namespace, id, SCHEMA_TYPE);
         }
 
@@ -98,8 +98,8 @@
 
         @Override
         @NonNull
-        public PackageAccessibleDocument build() {
-            return new PackageAccessibleDocument(super.build());
+        public VisibleToPackagesDocument build() {
+            return new VisibleToPackagesDocument(super.build());
         }
     }
 }
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleMap.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibleToPackagesMap.java
similarity index 97%
rename from apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleMap.java
rename to apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibleToPackagesMap.java
index cff729a..e2b51a7 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleMap.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibleToPackagesMap.java
@@ -29,7 +29,7 @@
  *
  * This object is not thread safe.
  */
-class PackageAccessibleMap {
+class VisibleToPackagesMap {
     /**
      * Maps packages to databases to prefixed schemas to PackageIdentifiers that have access to that
      * schema.
@@ -42,7 +42,7 @@
      *
      * <p>Any existing mappings for this prefix are overwritten.
      */
-    public void setPackageAccessible(
+    public void setVisibleToPackages(
             @NonNull String packageName,
             @NonNull String databaseName,
             @NonNull Map<String, Set<PackageIdentifier>> schemaToPackageIdentifier) {
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index 78d39cc..6555107 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-31a54dba5bda4d0109ea91eb1ac047c937cbaae3
+c7387d9b58726a23a0608a9365fb3a1b57b7b8a1
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
index d6ce3eb..7b74578 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
@@ -51,8 +51,6 @@
      * @param request the schema to set or update the AppSearch database to.
      * @return a {@link ListenableFuture} which resolves to a {@link SetSchemaResponse} object.
      */
-    // TODO(b/169883602): Change @code references to @link when setPlatformSurfaceable APIs are
-    //  exposed.
     @NonNull
     ListenableFuture<SetSchemaResponse> setSchema(@NonNull SetSchemaRequest request);
 
diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
index 1efe5cb..4843415 100644
--- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java
+++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
@@ -1285,14 +1285,20 @@
     }
 
     /**
-     * Called to check if the caller has the permission
-     * {@link Manifest.permission#SCHEDULE_EXACT_ALARM}.
-     *
-     * Apps can start {@link android.provider.Settings#ACTION_REQUEST_SCHEDULE_EXACT_ALARM} to
+     * Called to check if the caller can schedule exact alarms.
+     * <p>
+     * Apps targeting {@link Build.VERSION_CODES#S} or higher can schedule exact alarms if they
+     * have the {@link Manifest.permission#SCHEDULE_EXACT_ALARM} permission. These apps can also
+     * start {@link android.provider.Settings#ACTION_REQUEST_SCHEDULE_EXACT_ALARM} to
      * request this from the user.
+     * <p>
+     * Apps targeting lower sdk versions, can always schedule exact alarms.
      *
-     * @return {@code true} if the caller has the permission, {@code false} otherwise.
+     * @return {@code true} if the caller can schedule exact alarms.
      * @see android.provider.Settings#ACTION_REQUEST_SCHEDULE_EXACT_ALARM
+     * @see #setExact(int, long, PendingIntent)
+     * @see #setExactAndAllowWhileIdle(int, long, PendingIntent)
+     * @see #setAlarmClock(AlarmClockInfo, PendingIntent)
      */
     public boolean canScheduleExactAlarms() {
         return hasScheduleExactAlarm(mContext.getOpPackageName(), mContext.getUserId());
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 0e9efbc..ac20187 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -442,6 +442,7 @@
     private long mNextIdlePendingDelay;
     private long mNextIdleDelay;
     private long mNextLightIdleDelay;
+    private long mNextLightIdleDelayFlex;
     private long mNextLightAlarmTime;
     private long mNextSensingTimeoutAlarmTime;
 
@@ -886,16 +887,20 @@
      */
     public final class Constants implements DeviceConfig.OnPropertiesChangedListener {
         // Key names stored in the settings value.
-        private static final String KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT
-                = "light_after_inactive_to";
+        private static final String KEY_FLEX_TIME_SHORT = "flex_time_short";
+        private static final String KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT =
+                "light_after_inactive_to";
         private static final String KEY_LIGHT_PRE_IDLE_TIMEOUT = "light_pre_idle_to";
         private static final String KEY_LIGHT_IDLE_TIMEOUT = "light_idle_to";
+        private static final String KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX =
+                "light_idle_to_initial_flex";
+        private static final String KEY_LIGHT_MAX_IDLE_TIMEOUT_FLEX = "light_max_idle_to_flex";
         private static final String KEY_LIGHT_IDLE_FACTOR = "light_idle_factor";
         private static final String KEY_LIGHT_MAX_IDLE_TIMEOUT = "light_max_idle_to";
-        private static final String KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET
-                = "light_idle_maintenance_min_budget";
-        private static final String KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET
-                = "light_idle_maintenance_max_budget";
+        private static final String KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET =
+                "light_idle_maintenance_min_budget";
+        private static final String KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET =
+                "light_idle_maintenance_max_budget";
         private static final String KEY_MIN_LIGHT_MAINTENANCE_TIME = "min_light_maintenance_time";
         private static final String KEY_MIN_DEEP_MAINTENANCE_TIME = "min_deep_maintenance_time";
         private static final String KEY_INACTIVE_TIMEOUT = "inactive_to";
@@ -903,6 +908,7 @@
         private static final String KEY_LOCATING_TIMEOUT = "locating_to";
         private static final String KEY_LOCATION_ACCURACY = "location_accuracy";
         private static final String KEY_MOTION_INACTIVE_TIMEOUT = "motion_inactive_to";
+        private static final String KEY_MOTION_INACTIVE_TIMEOUT_FLEX = "motion_inactive_to_flex";
         private static final String KEY_IDLE_AFTER_INACTIVE_TIMEOUT = "idle_after_inactive_to";
         private static final String KEY_IDLE_PENDING_TIMEOUT = "idle_pending_to";
         private static final String KEY_MAX_IDLE_PENDING_TIMEOUT = "max_idle_pending_to";
@@ -929,13 +935,20 @@
                 "pre_idle_factor_long";
         private static final String KEY_PRE_IDLE_FACTOR_SHORT =
                 "pre_idle_factor_short";
+        private static final String KEY_USE_WINDOW_ALARMS = "use_window_alarms";
 
+        private static final long DEFAULT_FLEX_TIME_SHORT =
+                !COMPRESS_TIME ? 60 * 1000L : 5 * 1000L;
         private static final long DEFAULT_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT =
-                !COMPRESS_TIME ? 3 * 60 * 1000L : 15 * 1000L;
+                !COMPRESS_TIME ? 60 * 1000L : 15 * 1000L;
         private static final long DEFAULT_LIGHT_PRE_IDLE_TIMEOUT =
                 !COMPRESS_TIME ? 3 * 60 * 1000L : 30 * 1000L;
         private static final long DEFAULT_LIGHT_IDLE_TIMEOUT =
                 !COMPRESS_TIME ? 5 * 60 * 1000L : 15 * 1000L;
+        private static final long DEFAULT_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX =
+                !COMPRESS_TIME ? 60 * 1000L : 5 * 1000L;
+        private static final long DEFAULT_LIGHT_MAX_IDLE_TIMEOUT_FLEX =
+                !COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L;
         private static final float DEFAULT_LIGHT_IDLE_FACTOR = 2f;
         private static final long DEFAULT_LIGHT_MAX_IDLE_TIMEOUT =
                 !COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L;
@@ -958,6 +971,8 @@
         private static final float DEFAULT_LOCATION_ACCURACY = 20f;
         private static final long DEFAULT_MOTION_INACTIVE_TIMEOUT =
                 !COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L;
+        private static final long DEFAULT_MOTION_INACTIVE_TIMEOUT_FLEX =
+                !COMPRESS_TIME ? 60 * 1000L : 5 * 1000L;
         private static final long DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT =
                 (30 * 60 * 1000L) / (!COMPRESS_TIME ? 1 : 10);
         private static final long DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT_SMALL_BATTERY =
@@ -983,6 +998,14 @@
         private static final boolean DEFAULT_WAIT_FOR_UNLOCK = true;
         private static final float DEFAULT_PRE_IDLE_FACTOR_LONG = 1.67f;
         private static final float DEFAULT_PRE_IDLE_FACTOR_SHORT = .33f;
+        private static final boolean DEFAULT_USE_WINDOW_ALARMS = true;
+
+        /**
+         * A somewhat short alarm window size that we will tolerate for various alarm timings.
+         *
+         * @see #KEY_FLEX_TIME_SHORT
+         */
+        public long FLEX_TIME_SHORT = DEFAULT_FLEX_TIME_SHORT;
 
         /**
          * This is the time, after becoming inactive, that we go in to the first
@@ -1002,13 +1025,28 @@
         public long LIGHT_PRE_IDLE_TIMEOUT = DEFAULT_LIGHT_PRE_IDLE_TIMEOUT;
 
         /**
-         * This is the initial time that we will run in idle maintenance mode.
+         * This is the initial time that we will run in light idle maintenance mode.
          *
          * @see #KEY_LIGHT_IDLE_TIMEOUT
          */
         public long LIGHT_IDLE_TIMEOUT = DEFAULT_LIGHT_IDLE_TIMEOUT;
 
         /**
+         * This is the initial alarm window size that we will tolerate for light idle maintenance
+         * timing.
+         *
+         * @see #KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX
+         */
+        public long LIGHT_IDLE_TIMEOUT_INITIAL_FLEX = DEFAULT_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX;
+
+        /**
+         * This is the maximum value that {@link #LIGHT_IDLE_TIMEOUT_INITIAL_FLEX} should take.
+         *
+         * @see #KEY_LIGHT_MAX_IDLE_TIMEOUT_FLEX
+         */
+        public long LIGHT_MAX_IDLE_TIMEOUT_FLEX = DEFAULT_LIGHT_MAX_IDLE_TIMEOUT_FLEX;
+
+        /**
          * Scaling factor to apply to the light idle mode time each time we complete a cycle.
          *
          * @see #KEY_LIGHT_IDLE_FACTOR
@@ -1016,7 +1054,7 @@
         public float LIGHT_IDLE_FACTOR = DEFAULT_LIGHT_IDLE_FACTOR;
 
         /**
-         * This is the maximum time we will run in idle maintenance mode.
+         * This is the maximum time we will stay in light idle mode.
          *
          * @see #KEY_LIGHT_MAX_IDLE_TIMEOUT
          */
@@ -1099,13 +1137,22 @@
         /**
          * This is the time, after seeing motion, that we wait after becoming inactive from
          * that until we start looking for motion again.
+         *
          * @see #KEY_MOTION_INACTIVE_TIMEOUT
          */
         public long MOTION_INACTIVE_TIMEOUT = DEFAULT_MOTION_INACTIVE_TIMEOUT;
 
         /**
+         * This is the alarm window size we will tolerate for motion detection timings.
+         *
+         * @see #KEY_MOTION_INACTIVE_TIMEOUT_FLEX
+         */
+        public long MOTION_INACTIVE_TIMEOUT_FLEX = DEFAULT_MOTION_INACTIVE_TIMEOUT_FLEX;
+
+        /**
          * This is the time, after the inactive timeout elapses, that we will wait looking
          * for motion until we truly consider the device to be idle.
+         *
          * @see #KEY_IDLE_AFTER_INACTIVE_TIMEOUT
          */
         public long IDLE_AFTER_INACTIVE_TIMEOUT = DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT;
@@ -1204,6 +1251,12 @@
 
         public boolean WAIT_FOR_UNLOCK = DEFAULT_WAIT_FOR_UNLOCK;
 
+        /**
+         * Whether to use window alarms. True to use window alarms (call AlarmManager.setWindow()).
+         * False to use the legacy inexact alarms (call AlarmManager.set()).
+         */
+        public boolean USE_WINDOW_ALARMS = DEFAULT_USE_WINDOW_ALARMS;
+
         private final boolean mSmallBatteryDevice;
 
         public Constants() {
@@ -1227,6 +1280,10 @@
                         continue;
                     }
                     switch (name) {
+                        case KEY_FLEX_TIME_SHORT:
+                            FLEX_TIME_SHORT = properties.getLong(
+                                    KEY_FLEX_TIME_SHORT, DEFAULT_FLEX_TIME_SHORT);
+                            break;
                         case KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT:
                             LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = properties.getLong(
                                     KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT,
@@ -1240,9 +1297,19 @@
                             LIGHT_IDLE_TIMEOUT = properties.getLong(
                                     KEY_LIGHT_IDLE_TIMEOUT, DEFAULT_LIGHT_IDLE_TIMEOUT);
                             break;
+                        case KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX:
+                            LIGHT_IDLE_TIMEOUT_INITIAL_FLEX = properties.getLong(
+                                    KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX,
+                                    DEFAULT_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX);
+                            break;
+                        case KEY_LIGHT_MAX_IDLE_TIMEOUT_FLEX:
+                            LIGHT_MAX_IDLE_TIMEOUT_FLEX = properties.getLong(
+                                    KEY_LIGHT_MAX_IDLE_TIMEOUT_FLEX,
+                                    DEFAULT_LIGHT_MAX_IDLE_TIMEOUT_FLEX);
+                            break;
                         case KEY_LIGHT_IDLE_FACTOR:
-                            LIGHT_IDLE_FACTOR = properties.getFloat(
-                                    KEY_LIGHT_IDLE_FACTOR, DEFAULT_LIGHT_IDLE_FACTOR);
+                            LIGHT_IDLE_FACTOR = Math.max(1, properties.getFloat(
+                                    KEY_LIGHT_IDLE_FACTOR, DEFAULT_LIGHT_IDLE_FACTOR));
                             break;
                         case KEY_LIGHT_MAX_IDLE_TIMEOUT:
                             LIGHT_MAX_IDLE_TIMEOUT = properties.getLong(
@@ -1291,6 +1358,11 @@
                             MOTION_INACTIVE_TIMEOUT = properties.getLong(
                                     KEY_MOTION_INACTIVE_TIMEOUT, DEFAULT_MOTION_INACTIVE_TIMEOUT);
                             break;
+                        case KEY_MOTION_INACTIVE_TIMEOUT_FLEX:
+                            MOTION_INACTIVE_TIMEOUT_FLEX = properties.getLong(
+                                    KEY_MOTION_INACTIVE_TIMEOUT_FLEX,
+                                    DEFAULT_MOTION_INACTIVE_TIMEOUT_FLEX);
+                            break;
                         case KEY_IDLE_AFTER_INACTIVE_TIMEOUT:
                             final long defaultIdleAfterInactiveTimeout = mSmallBatteryDevice
                                     ? DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT_SMALL_BATTERY
@@ -1362,6 +1434,10 @@
                             PRE_IDLE_FACTOR_SHORT = properties.getFloat(
                                     KEY_PRE_IDLE_FACTOR_SHORT, DEFAULT_PRE_IDLE_FACTOR_SHORT);
                             break;
+                        case KEY_USE_WINDOW_ALARMS:
+                            USE_WINDOW_ALARMS = properties.getBoolean(
+                                    KEY_USE_WINDOW_ALARMS, DEFAULT_USE_WINDOW_ALARMS);
+                            break;
                         default:
                             Slog.e(TAG, "Unknown configuration key: " + name);
                             break;
@@ -1373,6 +1449,10 @@
         void dump(PrintWriter pw) {
             pw.println("  Settings:");
 
+            pw.print("    "); pw.print(KEY_FLEX_TIME_SHORT); pw.print("=");
+            TimeUtils.formatDuration(FLEX_TIME_SHORT, pw);
+            pw.println();
+
             pw.print("    ");
             pw.print(KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT);
             pw.print("=");
@@ -1387,6 +1467,14 @@
             TimeUtils.formatDuration(LIGHT_IDLE_TIMEOUT, pw);
             pw.println();
 
+            pw.print("    "); pw.print(KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX); pw.print("=");
+            TimeUtils.formatDuration(LIGHT_IDLE_TIMEOUT_INITIAL_FLEX, pw);
+            pw.println();
+
+            pw.print("    "); pw.print(KEY_LIGHT_MAX_IDLE_TIMEOUT_FLEX); pw.print("=");
+            TimeUtils.formatDuration(LIGHT_MAX_IDLE_TIMEOUT_FLEX, pw);
+            pw.println();
+
             pw.print("    "); pw.print(KEY_LIGHT_IDLE_FACTOR); pw.print("=");
             pw.print(LIGHT_IDLE_FACTOR);
             pw.println();
@@ -1431,6 +1519,10 @@
             TimeUtils.formatDuration(MOTION_INACTIVE_TIMEOUT, pw);
             pw.println();
 
+            pw.print("    "); pw.print(KEY_MOTION_INACTIVE_TIMEOUT_FLEX); pw.print("=");
+            TimeUtils.formatDuration(MOTION_INACTIVE_TIMEOUT_FLEX, pw);
+            pw.println();
+
             pw.print("    "); pw.print(KEY_IDLE_AFTER_INACTIVE_TIMEOUT); pw.print("=");
             TimeUtils.formatDuration(IDLE_AFTER_INACTIVE_TIMEOUT, pw);
             pw.println();
@@ -1489,6 +1581,9 @@
 
             pw.print("    "); pw.print(KEY_PRE_IDLE_FACTOR_SHORT); pw.print("=");
             pw.println(PRE_IDLE_FACTOR_SHORT);
+
+            pw.print("    "); pw.print(KEY_USE_WINDOW_ALARMS); pw.print("=");
+            pw.println(USE_WINDOW_ALARMS);
         }
     }
 
@@ -3199,7 +3294,8 @@
             mLightState = LIGHT_STATE_INACTIVE;
             if (DEBUG) Slog.d(TAG, "Moved from LIGHT_STATE_ACTIVE to LIGHT_STATE_INACTIVE");
             resetLightIdleManagementLocked();
-            scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT);
+            scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT,
+                    mConstants.FLEX_TIME_SHORT);
             EventLogTags.writeDeviceIdleLight(mLightState, "no activity");
         }
     }
@@ -3219,6 +3315,7 @@
 
     private void resetLightIdleManagementLocked() {
         mNextLightIdleDelay = 0;
+        mNextLightIdleDelayFlex = 0;
         mCurLightIdleBudget = 0;
         cancelLightAlarmLocked();
     }
@@ -3265,13 +3362,15 @@
                 mCurLightIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
                 // Reset the upcoming idle delays.
                 mNextLightIdleDelay = mConstants.LIGHT_IDLE_TIMEOUT;
+                mNextLightIdleDelayFlex = mConstants.LIGHT_IDLE_TIMEOUT_INITIAL_FLEX;
                 mMaintenanceStartTime = 0;
                 if (!isOpsInactiveLocked()) {
                     // We have some active ops going on...  give them a chance to finish
                     // before going in to our first idle.
                     mLightState = LIGHT_STATE_PRE_IDLE;
                     EventLogTags.writeDeviceIdleLight(mLightState, reason);
-                    scheduleLightAlarmLocked(mConstants.LIGHT_PRE_IDLE_TIMEOUT);
+                    scheduleLightAlarmLocked(mConstants.LIGHT_PRE_IDLE_TIMEOUT,
+                            mConstants.FLEX_TIME_SHORT);
                     break;
                 }
                 // Nothing active, fall through to immediately idle.
@@ -3290,12 +3389,11 @@
                     }
                 }
                 mMaintenanceStartTime = 0;
-                scheduleLightAlarmLocked(mNextLightIdleDelay);
+                scheduleLightAlarmLocked(mNextLightIdleDelay, mNextLightIdleDelayFlex);
                 mNextLightIdleDelay = Math.min(mConstants.LIGHT_MAX_IDLE_TIMEOUT,
-                        (long)(mNextLightIdleDelay * mConstants.LIGHT_IDLE_FACTOR));
-                if (mNextLightIdleDelay < mConstants.LIGHT_IDLE_TIMEOUT) {
-                    mNextLightIdleDelay = mConstants.LIGHT_IDLE_TIMEOUT;
-                }
+                        (long) (mNextLightIdleDelay * mConstants.LIGHT_IDLE_FACTOR));
+                mNextLightIdleDelayFlex = Math.min(mConstants.LIGHT_MAX_IDLE_TIMEOUT_FLEX,
+                        (long) (mNextLightIdleDelayFlex * mConstants.LIGHT_IDLE_FACTOR));
                 if (DEBUG) Slog.d(TAG, "Moved to LIGHT_STATE_IDLE.");
                 mLightState = LIGHT_STATE_IDLE;
                 EventLogTags.writeDeviceIdleLight(mLightState, reason);
@@ -3315,7 +3413,7 @@
                     } else if (mCurLightIdleBudget > mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET) {
                         mCurLightIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET;
                     }
-                    scheduleLightAlarmLocked(mCurLightIdleBudget);
+                    scheduleLightAlarmLocked(mCurLightIdleBudget, mConstants.FLEX_TIME_SHORT);
                     if (DEBUG) Slog.d(TAG,
                             "Moved from LIGHT_STATE_IDLE to LIGHT_STATE_IDLE_MAINTENANCE.");
                     mLightState = LIGHT_STATE_IDLE_MAINTENANCE;
@@ -3326,7 +3424,7 @@
                     // We'd like to do maintenance, but currently don't have network
                     // connectivity...  let's try to wait until the network comes back.
                     // We'll only wait for another full idle period, however, and then give up.
-                    scheduleLightAlarmLocked(mNextLightIdleDelay);
+                    scheduleLightAlarmLocked(mNextLightIdleDelay, mNextLightIdleDelayFlex / 2);
                     if (DEBUG) Slog.d(TAG, "Moved to LIGHT_WAITING_FOR_NETWORK.");
                     mLightState = LIGHT_STATE_WAITING_FOR_NETWORK;
                     EventLogTags.writeDeviceIdleLight(mLightState, reason);
@@ -3844,40 +3942,75 @@
             mAlarmManager.setIdleUntil(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                     mNextAlarmTime, "DeviceIdleController.deep", mDeepAlarmListener, mHandler);
         } else {
-            mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
-                    mNextAlarmTime, "DeviceIdleController.deep", mDeepAlarmListener, mHandler);
+            if (mConstants.USE_WINDOW_ALARMS) {
+                mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                        mConstants.FLEX_TIME_SHORT,
+                        mNextAlarmTime, "DeviceIdleController.deep", mDeepAlarmListener, mHandler);
+            } else {
+                mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                        mNextAlarmTime, "DeviceIdleController.deep", mDeepAlarmListener, mHandler);
+            }
         }
     }
 
-    void scheduleLightAlarmLocked(long delay) {
-        if (DEBUG) Slog.d(TAG, "scheduleLightAlarmLocked(" + delay + ")");
+    void scheduleLightAlarmLocked(long delay, long flex) {
+        if (DEBUG) {
+            Slog.d(TAG, "scheduleLightAlarmLocked(" + delay
+                    + (mConstants.USE_WINDOW_ALARMS ? "/" + flex : "") + ")");
+        }
         mNextLightAlarmTime = SystemClock.elapsedRealtime() + delay;
-        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
-                mNextLightAlarmTime, "DeviceIdleController.light", mLightAlarmListener, mHandler);
+        if (mConstants.USE_WINDOW_ALARMS) {
+            mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP, mNextLightAlarmTime, flex,
+                    "DeviceIdleController.light", mLightAlarmListener, mHandler);
+        } else {
+            mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, mNextLightAlarmTime,
+                    "DeviceIdleController.light", mLightAlarmListener, mHandler);
+        }
     }
 
     private void scheduleMotionRegistrationAlarmLocked() {
         if (DEBUG) Slog.d(TAG, "scheduleMotionRegistrationAlarmLocked");
         long nextMotionRegistrationAlarmTime =
                 mInjector.getElapsedRealtime() + mConstants.MOTION_INACTIVE_TIMEOUT / 2;
-        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextMotionRegistrationAlarmTime,
-                "DeviceIdleController.motion_registration", mMotionRegistrationAlarmListener,
-                mHandler);
+        if (mConstants.USE_WINDOW_ALARMS) {
+            mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                    nextMotionRegistrationAlarmTime, mConstants.MOTION_INACTIVE_TIMEOUT_FLEX,
+                    "DeviceIdleController.motion_registration", mMotionRegistrationAlarmListener,
+                    mHandler);
+        } else {
+            mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextMotionRegistrationAlarmTime,
+                    "DeviceIdleController.motion_registration", mMotionRegistrationAlarmListener,
+                    mHandler);
+        }
     }
 
     private void scheduleMotionTimeoutAlarmLocked() {
         if (DEBUG) Slog.d(TAG, "scheduleMotionAlarmLocked");
         long nextMotionTimeoutAlarmTime =
                 mInjector.getElapsedRealtime() + mConstants.MOTION_INACTIVE_TIMEOUT;
-        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextMotionTimeoutAlarmTime,
-                "DeviceIdleController.motion", mMotionTimeoutAlarmListener, mHandler);
+        if (mConstants.USE_WINDOW_ALARMS) {
+            mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                    nextMotionTimeoutAlarmTime,
+                    mConstants.MOTION_INACTIVE_TIMEOUT_FLEX,
+                    "DeviceIdleController.motion", mMotionTimeoutAlarmListener, mHandler);
+        } else {
+            mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextMotionTimeoutAlarmTime,
+                    "DeviceIdleController.motion", mMotionTimeoutAlarmListener, mHandler);
+        }
     }
 
     void scheduleSensingTimeoutAlarmLocked(long delay) {
         if (DEBUG) Slog.d(TAG, "scheduleSensingAlarmLocked(" + delay + ")");
         mNextSensingTimeoutAlarmTime = SystemClock.elapsedRealtime() + delay;
-        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, mNextSensingTimeoutAlarmTime,
-            "DeviceIdleController.sensing", mSensingTimeoutAlarmListener, mHandler);
+        if (mConstants.USE_WINDOW_ALARMS) {
+            mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                    mNextSensingTimeoutAlarmTime,
+                    mConstants.FLEX_TIME_SHORT,
+                    "DeviceIdleController.sensing", mSensingTimeoutAlarmListener, mHandler);
+        } else {
+            mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, mNextSensingTimeoutAlarmTime,
+                    "DeviceIdleController.sensing", mSensingTimeoutAlarmListener, mHandler);
+        }
     }
 
     private static int[] buildAppIdArray(ArrayMap<String, Integer> systemApps,
@@ -4852,7 +4985,13 @@
             if (mNextLightIdleDelay != 0) {
                 pw.print("  mNextIdleDelay=");
                 TimeUtils.formatDuration(mNextLightIdleDelay, pw);
-                pw.println();
+                if (mConstants.USE_WINDOW_ALARMS) {
+                    pw.print(" (flex=");
+                    TimeUtils.formatDuration(mNextLightIdleDelayFlex, pw);
+                    pw.println(")");
+                } else {
+                    pw.println();
+                }
             }
             if (mNextLightAlarmTime != 0) {
                 pw.print("  mNextLightAlarmTime=");
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index fb5129f..70e548d 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -2572,6 +2572,9 @@
                 throw new SecurityException("Uid " + callingUid
                         + " cannot query hasScheduleExactAlarm for uid " + uid);
             }
+            if (!isExactAlarmChangeEnabled(packageName, userId)) {
+                return true;
+            }
             return (uid > 0) ? hasScheduleExactAlarmInternal(packageName, uid) : false;
         }
 
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
index 37b3c04..d532e20 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
@@ -20,6 +20,7 @@
 import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_USER;
 import static android.app.usage.UsageStatsManager.REASON_MAIN_MASK;
 import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
 import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
 import static android.app.usage.UsageStatsManager.REASON_SUB_MASK;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
@@ -259,8 +260,10 @@
         int bucketingReason = REASON_MAIN_USAGE | usageReason;
         final boolean isUserUsage = isUserUsage(bucketingReason);
 
-        if (appUsageHistory.currentBucket == STANDBY_BUCKET_RESTRICTED && !isUserUsage) {
-            // Only user usage should bring an app out of the RESTRICTED bucket.
+        if (appUsageHistory.currentBucket == STANDBY_BUCKET_RESTRICTED && !isUserUsage
+                && (appUsageHistory.bucketingReason & REASON_MAIN_MASK) != REASON_MAIN_TIMEOUT) {
+            // Only user usage should bring an app out of the RESTRICTED bucket, unless the app
+            // just timed out into RESTRICTED.
             newBucket = STANDBY_BUCKET_RESTRICTED;
             bucketingReason = appUsageHistory.bucketingReason;
         } else {
diff --git a/api/Android.bp b/api/Android.bp
index a84e6a6..2ea180e 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -24,6 +24,41 @@
     default_applicable_licenses: ["frameworks_base_license"],
 }
 
+python_binary_host {
+    name: "api_versions_trimmer",
+    srcs: ["api_versions_trimmer.py"],
+    version: {
+        py2: {
+            enabled: false,
+        },
+        py3: {
+            enabled: true,
+            embedded_launcher: false,
+        },
+    },
+}
+
+python_test_host {
+    name: "api_versions_trimmer_unittests",
+    main: "api_versions_trimmer_unittests.py",
+    srcs: [
+        "api_versions_trimmer_unittests.py",
+        "api_versions_trimmer.py",
+    ],
+    test_options: {
+        unit_test: true,
+    },
+    version: {
+        py2: {
+            enabled: false,
+        },
+        py3: {
+            enabled: true,
+            embedded_launcher: false,
+        },
+    },
+}
+
 metalava_cmd = "$(location metalava)"
 // Silence reflection warnings. See b/168689341
 metalava_cmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED "
@@ -431,3 +466,41 @@
         },
     ],
 }
+
+// This rule will filter classes present in the jar files of mainline modules
+// from the lint database in api-versions.xml.
+// This is done to reduce the number of false positive NewApi findings in
+// java libraries that compile against the module SDK
+genrule {
+    name: "api-versions-xml-public-filtered",
+    srcs: [
+        // Note: order matters: first parameter is the full api-versions.xml
+        // after that the stubs files in any order
+        // stubs files are all modules that export API surfaces EXCEPT ART
+        ":framework-doc-stubs{.api_versions.xml}",
+        ":android.net.ipsec.ike.stubs{.jar}",
+        ":conscrypt.module.public.api.stubs{.jar}",
+        ":framework-appsearch.stubs{.jar}",
+        ":framework-connectivity.stubs{.jar}",
+        ":framework-graphics.stubs{.jar}",
+        ":framework-media.stubs{.jar}",
+        ":framework-mediaprovider.stubs{.jar}",
+        ":framework-permission.stubs{.jar}",
+        ":framework-permission-s.stubs{.jar}",
+        ":framework-scheduling.stubs{.jar}",
+        ":framework-sdkextensions.stubs{.jar}",
+        ":framework-statsd.stubs{.jar}",
+        ":framework-tethering.stubs{.jar}",
+        ":framework-wifi.stubs{.jar}",
+        ":i18n.module.public.api.stubs{.jar}",
+    ],
+    out: ["api-versions-public-filtered.xml"],
+    tools: ["api_versions_trimmer"],
+    cmd: "$(location api_versions_trimmer) $(out) $(in)",
+    dist: {
+        targets: [
+            "sdk",
+            "win_sdk",
+        ],
+    },
+}
diff --git a/api/OWNERS b/api/OWNERS
index 88d0b61..a027270 100644
--- a/api/OWNERS
+++ b/api/OWNERS
@@ -1 +1,6 @@
+hansson@google.com
+
+# Modularization team
+file:platform/packages/modules/common:/OWNERS
+
 per-file Android.bp = file:platform/build/soong:/OWNERS
diff --git a/api/api_versions_trimmer.py b/api/api_versions_trimmer.py
new file mode 100755
index 0000000..9afd95a
--- /dev/null
+++ b/api/api_versions_trimmer.py
@@ -0,0 +1,136 @@
+#!/usr/bin/env python3
+#
+# 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.
+
+"""Script to remove mainline APIs from the api-versions.xml."""
+
+import argparse
+import re
+import xml.etree.ElementTree as ET
+import zipfile
+
+
+def read_classes(stubs):
+  """Read classes from the stubs file.
+
+  Args:
+    stubs: argument can be a path to a file (a string), a file-like object or a
+    path-like object
+
+  Returns:
+    a set of the classes found in the file (set of strings)
+  """
+  classes = set()
+  with zipfile.ZipFile(stubs) as z:
+    for info in z.infolist():
+      if (not info.is_dir()
+          and info.filename.endswith(".class")
+          and not info.filename.startswith("META-INF")):
+        # drop ".class" extension
+        classes.add(info.filename[:-6])
+  return classes
+
+
+def filter_method_tag(method, classes_to_remove):
+  """Updates the signature of this method by calling filter_method_signature.
+
+  Updates the method passed into this function.
+
+  Args:
+    method: xml element that represents a method
+    classes_to_remove: set of classes you to remove
+  """
+  filtered = filter_method_signature(method.get("name"), classes_to_remove)
+  method.set("name", filtered)
+
+
+def filter_method_signature(signature, classes_to_remove):
+  """Removes mentions of certain classes from this method signature.
+
+  Replaces any existing classes that need to be removed, with java/lang/Object
+
+  Args:
+    signature: string that is a java representation of a method signature
+    classes_to_remove: set of classes you to remove
+  """
+  regex = re.compile("L.*?;")
+  start = signature.find("(")
+  matches = set(regex.findall(signature[start:]))
+  for m in matches:
+    # m[1:-1] to drop the leading `L` and `;` ending
+    if m[1:-1] in classes_to_remove:
+      signature = signature.replace(m, "Ljava/lang/Object;")
+  return signature
+
+
+def filter_lint_database(database, classes_to_remove, output):
+  """Reads a lint database and writes a filtered version without some classes.
+
+  Reads database from api-versions.xml and removes any references to classes
+  in the second argument. Writes the result (another xml with the same format
+  of the database) to output.
+
+  Args:
+    database: path to xml with lint database to read
+    classes_to_remove: iterable (ideally a set or similar for quick
+    lookups) that enumerates the classes that should be removed
+    output: path to write the filtered database
+  """
+  xml = ET.parse(database)
+  root = xml.getroot()
+  for c in xml.findall("class"):
+    cname = c.get("name")
+    if cname in classes_to_remove:
+      root.remove(c)
+    else:
+      # find the <extends /> tag inside this class to see if the parent
+      # has been removed from the known classes (attribute called name)
+      super_classes = c.findall("extends")
+      for super_class in super_classes:
+        super_class_name = super_class.get("name")
+        if super_class_name in classes_to_remove:
+          super_class.set("name", "java/lang/Object")
+      interfaces = c.findall("implements")
+      for interface in interfaces:
+        interface_name = interface.get("name")
+        if interface_name in classes_to_remove:
+          c.remove(interface)
+      for method in c.findall("method"):
+        filter_method_tag(method, classes_to_remove)
+  xml.write(output)
+
+
+def main():
+  """Run the program."""
+  parser = argparse.ArgumentParser(
+      description=
+      ("Read a lint database (api-versions.xml) and many stubs jar files. "
+       "Produce another database file that doesn't include the classes present "
+       "in the stubs file(s)."))
+  parser.add_argument("output", help="Destination of the result (xml file).")
+  parser.add_argument(
+      "api_versions",
+      help="The lint database (api-versions.xml file) to read data from"
+  )
+  parser.add_argument("stubs", nargs="+", help="The stubs jar file(s)")
+  parsed = parser.parse_args()
+  classes = set()
+  for stub in parsed.stubs:
+    classes.update(read_classes(stub))
+  filter_lint_database(parsed.api_versions, classes, parsed.output)
+
+
+if __name__ == "__main__":
+  main()
diff --git a/api/api_versions_trimmer_unittests.py b/api/api_versions_trimmer_unittests.py
new file mode 100644
index 0000000..4eb929e
--- /dev/null
+++ b/api/api_versions_trimmer_unittests.py
@@ -0,0 +1,307 @@
+#!/usr/bin/env python3
+#
+# 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.
+
+import io
+import re
+import unittest
+import xml.etree.ElementTree as ET
+import zipfile
+
+import api_versions_trimmer
+
+
+def create_in_memory_zip_file(files):
+  f = io.BytesIO()
+  with zipfile.ZipFile(f, "w") as z:
+    for fname in files:
+      with z.open(fname, mode="w") as class_file:
+        class_file.write(b"")
+  return f
+
+
+def indent(elem, level=0):
+  i = "\n" + level * "  "
+  j = "\n" + (level - 1) * "  "
+  if len(elem):
+    if not elem.text or not elem.text.strip():
+      elem.text = i + "  "
+      if not elem.tail or not elem.tail.strip():
+        elem.tail = i
+        for subelem in elem:
+          indent(subelem, level + 1)
+        if not elem.tail or not elem.tail.strip():
+          elem.tail = j
+    else:
+      if level and (not elem.tail or not elem.tail.strip()):
+        elem.tail = j
+    return elem
+
+
+def pretty_print(s):
+  tree = ET.parse(io.StringIO(s))
+  el = indent(tree.getroot())
+  res = ET.tostring(el).decode("utf-8")
+  # remove empty lines inside the result because this still breaks some
+  # comparisons
+  return re.sub(r"\n\s*\n", "\n", res, re.MULTILINE)
+
+
+class ApiVersionsTrimmerUnittests(unittest.TestCase):
+
+  def setUp(self):
+    # so it prints diffs in long strings (xml files)
+    self.maxDiff = None
+
+  def test_read_classes(self):
+    f = create_in_memory_zip_file(
+        ["a/b/C.class",
+         "a/b/D.class",
+        ]
+    )
+    res = api_versions_trimmer.read_classes(f)
+    self.assertEqual({"a/b/C", "a/b/D"}, res)
+
+  def test_read_classes_ignore_dex(self):
+    f = create_in_memory_zip_file(
+        ["a/b/C.class",
+         "a/b/D.class",
+         "a/b/E.dex",
+         "f.dex",
+        ]
+    )
+    res = api_versions_trimmer.read_classes(f)
+    self.assertEqual({"a/b/C", "a/b/D"}, res)
+
+  def test_read_classes_ignore_manifest(self):
+    f = create_in_memory_zip_file(
+        ["a/b/C.class",
+         "a/b/D.class",
+         "META-INFO/G.class"
+        ]
+    )
+    res = api_versions_trimmer.read_classes(f)
+    self.assertEqual({"a/b/C", "a/b/D"}, res)
+
+  def test_filter_method_signature(self):
+    xml = """
+    <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24"/>
+    """
+    method = ET.fromstring(xml)
+    classes_to_remove = {"android/accessibilityservice/GestureDescription"}
+    expected = "dispatchGesture(Ljava/lang/Object;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z"
+    api_versions_trimmer.filter_method_tag(method, classes_to_remove)
+    self.assertEqual(expected, method.get("name"))
+
+  def test_filter_method_signature_with_L_in_method(self):
+    xml = """
+    <method name="dispatchLeftGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24"/>
+    """
+    method = ET.fromstring(xml)
+    classes_to_remove = {"android/accessibilityservice/GestureDescription"}
+    expected = "dispatchLeftGesture(Ljava/lang/Object;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z"
+    api_versions_trimmer.filter_method_tag(method, classes_to_remove)
+    self.assertEqual(expected, method.get("name"))
+
+  def test_filter_method_signature_with_L_in_class(self):
+    xml = """
+    <method name="dispatchGesture(Landroid/accessibilityservice/LeftGestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24"/>
+    """
+    method = ET.fromstring(xml)
+    classes_to_remove = {"android/accessibilityservice/LeftGestureDescription"}
+    expected = "dispatchGesture(Ljava/lang/Object;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z"
+    api_versions_trimmer.filter_method_tag(method, classes_to_remove)
+    self.assertEqual(expected, method.get("name"))
+
+  def test_filter_method_signature_with_inner_class(self):
+    xml = """
+    <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription$Inner;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24"/>
+    """
+    method = ET.fromstring(xml)
+    classes_to_remove = {"android/accessibilityservice/GestureDescription$Inner"}
+    expected = "dispatchGesture(Ljava/lang/Object;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z"
+    api_versions_trimmer.filter_method_tag(method, classes_to_remove)
+    self.assertEqual(expected, method.get("name"))
+
+  def _run_filter_db_test(self, database_str, expected):
+    """Performs the pattern of testing the filter_lint_database method.
+
+    Filters instances of the class "a/b/C" (hard-coded) from the database string
+    and compares the result with the expected result (performs formatting of
+    the xml of both inputs)
+
+    Args:
+      database_str: string, the contents of the lint database (api-versions.xml)
+      expected: string, the expected result after filtering the original
+    database
+    """
+    database = io.StringIO(database_str)
+    classes_to_remove = {"a/b/C"}
+    output = io.BytesIO()
+    api_versions_trimmer.filter_lint_database(
+        database,
+        classes_to_remove,
+        output
+    )
+    expected = pretty_print(expected)
+    res = pretty_print(output.getvalue().decode("utf-8"))
+    self.assertEqual(expected, res)
+
+  def test_filter_lint_database_updates_method_signature_params(self):
+    self._run_filter_db_test(
+        database_str="""
+    <api version="2">
+      <!-- will be removed -->
+      <class name="a/b/C" since="1">
+        <extends name="java/lang/Object"/>
+      </class>
+
+      <class name="a/b/E" since="1">
+        <!-- extends will be modified -->
+        <extends name="a/b/C"/>
+        <!-- first parameter will be modified -->
+        <method name="dispatchGesture(La/b/C;Landroid/os/Handler;)Z" since="24"/>
+        <!-- second should remain untouched -->
+        <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
+sultCallback;Landroid/os/Handler;)Z" since="24"/>
+      </class>
+    </api>
+    """,
+        expected="""
+    <api version="2">
+      <class name="a/b/E" since="1">
+        <extends name="java/lang/Object"/>
+        <method name="dispatchGesture(Ljava/lang/Object;Landroid/os/Handler;)Z" since="24"/>
+        <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
+sultCallback;Landroid/os/Handler;)Z" since="24"/>
+      </class>
+    </api>
+    """)
+
+  def test_filter_lint_database_updates_method_signature_return(self):
+    self._run_filter_db_test(
+        database_str="""
+    <api version="2">
+      <!-- will be removed -->
+      <class name="a/b/C" since="1">
+        <extends name="java/lang/Object"/>
+      </class>
+
+      <class name="a/b/E" since="1">
+        <!-- extends will be modified -->
+        <extends name="a/b/C"/>
+        <!-- return type should be changed -->
+        <method name="gestureIdToString(I)La/b/C;" since="24"/>
+      </class>
+    </api>
+    """,
+        expected="""
+    <api version="2">
+      <class name="a/b/E" since="1">
+
+        <extends name="java/lang/Object"/>
+
+        <method name="gestureIdToString(I)Ljava/lang/Object;" since="24"/>
+      </class>
+    </api>
+    """)
+
+  def test_filter_lint_database_removes_implements(self):
+    self._run_filter_db_test(
+        database_str="""
+    <api version="2">
+      <!-- will be removed -->
+      <class name="a/b/C" since="1">
+        <extends name="java/lang/Object"/>
+      </class>
+
+      <class name="a/b/D" since="1">
+        <extends name="java/lang/Object"/>
+        <implements name="a/b/C"/>
+        <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
+sultCallback;Landroid/os/Handler;)Z" since="24"/>
+      </class>
+    </api>
+    """,
+        expected="""
+    <api version="2">
+
+      <class name="a/b/D" since="1">
+        <extends name="java/lang/Object"/>
+        <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
+sultCallback;Landroid/os/Handler;)Z" since="24"/>
+      </class>
+    </api>
+    """)
+
+  def test_filter_lint_database_updates_extends(self):
+    self._run_filter_db_test(
+        database_str="""
+    <api version="2">
+      <!-- will be removed -->
+      <class name="a/b/C" since="1">
+        <extends name="java/lang/Object"/>
+      </class>
+
+      <class name="a/b/E" since="1">
+        <!-- extends will be modified -->
+        <extends name="a/b/C"/>
+        <method name="dispatchGesture(Ljava/lang/Object;Landroid/os/Handler;)Z" since="24"/>
+        <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
+sultCallback;Landroid/os/Handler;)Z" since="24"/>
+      </class>
+    </api>
+    """,
+        expected="""
+    <api version="2">
+      <class name="a/b/E" since="1">
+        <extends name="java/lang/Object"/>
+        <method name="dispatchGesture(Ljava/lang/Object;Landroid/os/Handler;)Z" since="24"/>
+        <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
+sultCallback;Landroid/os/Handler;)Z" since="24"/>
+      </class>
+    </api>
+    """)
+
+  def test_filter_lint_database_removes_class(self):
+    self._run_filter_db_test(
+        database_str="""
+    <api version="2">
+      <!-- will be removed -->
+      <class name="a/b/C" since="1">
+        <extends name="java/lang/Object"/>
+      </class>
+
+      <class name="a/b/D" since="1">
+        <extends name="java/lang/Object"/>
+        <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
+sultCallback;Landroid/os/Handler;)Z" since="24"/>
+      </class>
+    </api>
+    """,
+        expected="""
+    <api version="2">
+
+      <class name="a/b/D" since="1">
+        <extends name="java/lang/Object"/>
+        <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
+sultCallback;Landroid/os/Handler;)Z" since="24"/>
+      </class>
+    </api>
+    """)
+
+
+if __name__ == "__main__":
+  unittest.main()
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 02ab314..034ad8e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -447,7 +447,7 @@
 
         @GuardedBy("mLock")
         ContentProviderHolder mHolder; // Temp holder to be used between notifier and waiter
-        Object mLock; // The lock to be used to get notified when the provider is ready
+        final Object mLock; // The lock to be used to get notified when the provider is ready
 
         public ProviderKey(String authority, int userId) {
             this.authority = authority;
@@ -1827,11 +1827,14 @@
 
         @Override
         public void notifyContentProviderPublishStatus(@NonNull ContentProviderHolder holder,
-                @NonNull String auth, int userId, boolean published) {
-            final ProviderKey key = getGetProviderKey(auth, userId);
-            synchronized (key.mLock) {
-                key.mHolder = holder;
-                key.mLock.notifyAll();
+                @NonNull String authorities, int userId, boolean published) {
+            final String auths[] = authorities.split(";");
+            for (String auth: auths) {
+                final ProviderKey key = getGetProviderKey(auth, userId);
+                synchronized (key.mLock) {
+                    key.mHolder = holder;
+                    key.mLock.notifyAll();
+                }
             }
         }
 
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 96d59b8..9bd6c750f 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -4804,6 +4804,16 @@
     public static final int HISTORY_FLAG_DISCRETE = 1 << 1;
 
     /**
+     * Flag for querying app op history: assemble attribution chains, and attach the last visible
+     * node in the chain to the start as a proxy info. This only applies to discrete accesses.
+     *
+     * TODO 191512294: Add to @SystemApi
+     *
+     * @hide
+     */
+    public static final int HISTORY_FLAG_GET_ATTRIBUTION_CHAINS = 1 << 2;
+
+    /**
      * Flag for querying app op history: get all types of historical access information.
      *
      * @see #getHistoricalOps(HistoricalOpsRequest, Executor, Consumer)
@@ -4819,7 +4829,8 @@
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true, prefix = { "HISTORY_FLAG_" }, value = {
             HISTORY_FLAG_AGGREGATE,
-            HISTORY_FLAG_DISCRETE
+            HISTORY_FLAG_DISCRETE,
+            HISTORY_FLAG_GET_ATTRIBUTION_CHAINS
     })
     public @interface OpHistoryFlags {}
 
@@ -5037,7 +5048,8 @@
              * @return This builder.
              */
             public @NonNull Builder setHistoryFlags(@OpHistoryFlags int flags) {
-                Preconditions.checkFlagsArgument(flags, HISTORY_FLAGS_ALL);
+                Preconditions.checkFlagsArgument(flags,
+                        HISTORY_FLAGS_ALL | HISTORY_FLAG_GET_ATTRIBUTION_CHAINS);
                 mHistoryFlags = flags;
                 return this;
             }
@@ -5290,8 +5302,17 @@
                 @Nullable String attributionTag, @UidState int uidState, @OpFlags int opFlag,
                 long discreteAccessTime, long discreteAccessDuration) {
             getOrCreateHistoricalUidOps(uid).addDiscreteAccess(opCode, packageName, attributionTag,
-                    uidState, opFlag, discreteAccessTime, discreteAccessDuration);
-        };
+                    uidState, opFlag, discreteAccessTime, discreteAccessDuration, null);
+        }
+
+        /** @hide */
+        public void addDiscreteAccess(int opCode, int uid, @NonNull String packageName,
+                @Nullable String attributionTag, @UidState int uidState, @OpFlags int opFlag,
+                long discreteAccessTime, long discreteAccessDuration,
+                @Nullable OpEventProxyInfo proxy) {
+            getOrCreateHistoricalUidOps(uid).addDiscreteAccess(opCode, packageName, attributionTag,
+                    uidState, opFlag, discreteAccessTime, discreteAccessDuration, proxy);
+        }
 
 
         /** @hide */
@@ -5623,9 +5644,10 @@
 
         private void addDiscreteAccess(int opCode, @NonNull String packageName,
                 @Nullable String attributionTag, @UidState int uidState,
-                @OpFlags int flag, long discreteAccessTime, long discreteAccessDuration) {
+                @OpFlags int flag, long discreteAccessTime, long discreteAccessDuration,
+                @Nullable OpEventProxyInfo proxy) {
             getOrCreateHistoricalPackageOps(packageName).addDiscreteAccess(opCode, attributionTag,
-                    uidState, flag, discreteAccessTime, discreteAccessDuration);
+                    uidState, flag, discreteAccessTime, discreteAccessDuration, proxy);
         };
 
         /**
@@ -5889,9 +5911,9 @@
 
         private void addDiscreteAccess(int opCode, @Nullable String attributionTag,
                 @UidState int uidState, @OpFlags int flag, long discreteAccessTime,
-                long discreteAccessDuration) {
+                long discreteAccessDuration, @Nullable OpEventProxyInfo proxy) {
             getOrCreateAttributedHistoricalOps(attributionTag).addDiscreteAccess(opCode, uidState,
-                    flag, discreteAccessTime, discreteAccessDuration);
+                    flag, discreteAccessTime, discreteAccessDuration, proxy);
         }
 
         /**
@@ -6212,9 +6234,10 @@
         }
 
         private void addDiscreteAccess(int opCode, @UidState int uidState, @OpFlags int flag,
-                long discreteAccessTime, long discreteAccessDuration) {
+                long discreteAccessTime, long discreteAccessDuration,
+                @Nullable OpEventProxyInfo proxy) {
             getOrCreateHistoricalOp(opCode).addDiscreteAccess(uidState,flag, discreteAccessTime,
-                    discreteAccessDuration);
+                    discreteAccessDuration, proxy);
         }
 
         /**
@@ -6583,11 +6606,12 @@
         }
 
         private void addDiscreteAccess(@UidState int uidState, @OpFlags int flag,
-                long discreteAccessTime, long discreteAccessDuration) {
+                long discreteAccessTime, long discreteAccessDuration,
+                @Nullable OpEventProxyInfo proxy) {
             List<AttributedOpEntry> discreteAccesses = getOrCreateDiscreteAccesses();
             LongSparseArray<NoteOpEvent> accessEvents = new LongSparseArray<>();
             long key = makeKey(uidState, flag);
-            NoteOpEvent note = new NoteOpEvent(discreteAccessTime, discreteAccessDuration, null);
+            NoteOpEvent note = new NoteOpEvent(discreteAccessTime, discreteAccessDuration, proxy);
             accessEvents.append(key, note);
             AttributedOpEntry access = new AttributedOpEntry(mOp, false, accessEvents, null);
             int insertionPoint = discreteAccesses.size() - 1;
@@ -10022,6 +10046,8 @@
                     NoteOpEvent existingAccess = accessEvents.get(key);
                     if (existingAccess == null || existingAccess.getDuration() == -1) {
                         accessEvents.append(key, access);
+                    } else if (existingAccess.mProxy == null && access.mProxy != null ) {
+                        existingAccess.mProxy = access.mProxy;
                     }
                 }
                 if (reject != null) {
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 4555c172..d6ff6d3 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -153,7 +153,7 @@
     void performDirectAction(IBinder activityToken, String actionId,
             in Bundle arguments, in RemoteCallback cancellationCallback,
             in RemoteCallback resultCallback);
-    void notifyContentProviderPublishStatus(in ContentProviderHolder holder, String auth,
+    void notifyContentProviderPublishStatus(in ContentProviderHolder holder, String authorities,
             int userId, boolean published);
     void instrumentWithoutRestart(in ComponentName instrumentationName,
             in Bundle instrumentationArgs,
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 9ed76c1..a2c9795 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -546,6 +546,10 @@
         if (aInfo.sharedLibraryFiles != null) {
             int index = 0;
             for (String lib : aInfo.sharedLibraryFiles) {
+                // sharedLibraryFiles might contain native shared libraries that are not APK paths.
+                if (!lib.endsWith(".apk")) {
+                    continue;
+                }
                 if (!outSeenPaths.contains(lib) && !outZipPaths.contains(lib)) {
                     outZipPaths.add(index, lib);
                     index++;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index ba0bc55..506dfe0 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5606,14 +5606,24 @@
 
             final boolean snoozeEnabled = !hideSnoozeButton
                     && mContext.getContentResolver() != null
-                    && (Settings.Secure.getInt(mContext.getContentResolver(),
-                    Settings.Secure.SHOW_NOTIFICATION_SNOOZE, 0) == 1);
+                    && isSnoozeSettingEnabled();
             if (snoozeEnabled) {
                 big.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
                         RemoteViews.MARGIN_BOTTOM, 0);
             }
         }
 
+        private boolean isSnoozeSettingEnabled() {
+            try {
+                return Settings.Secure.getInt(mContext.getContentResolver(),
+                    Settings.Secure.SHOW_NOTIFICATION_SNOOZE, 0) == 1;
+            } catch (SecurityException ex) {
+                // Most 3p apps can't access this snooze setting, so their NotificationListeners
+                // would be unable to create notification views if we propagated this exception.
+                return false;
+            }
+        }
+
         /**
          * Returns the actions that are not contextual.
          */
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 04a12af..6ca7dfb 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -34,6 +34,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Objects;
@@ -260,12 +261,19 @@
     private static final HashMap<String, Integer> sCorks = new HashMap<>();
 
     /**
+     * A map of cache keys that have been disabled in the local process.  When a key is
+     * disabled locally, existing caches are disabled and the key is saved in this map.
+     * Future cache instances that use the same key will be disabled in their constructor.
+     */
+    @GuardedBy("sCorkLock")
+    private static final HashSet<String> sDisabledKeys = new HashSet<>();
+
+    /**
      * Weakly references all cache objects in the current process, allowing us to iterate over
      * them all for purposes like issuing debug dumps and reacting to memory pressure.
      */
     @GuardedBy("sCorkLock")
-    private static final WeakHashMap<PropertyInvalidatedCache, Void> sCaches =
-            new WeakHashMap<>();
+    private static final WeakHashMap<PropertyInvalidatedCache, Void> sCaches = new WeakHashMap<>();
 
     private final Object mLock = new Object();
 
@@ -348,6 +356,9 @@
             };
         synchronized (sCorkLock) {
             sCaches.put(this, null);
+            if (sDisabledKeys.contains(mCacheName)) {
+                disableInstance();
+            }
         }
     }
 
@@ -372,6 +383,14 @@
     protected abstract Result recompute(Query query);
 
     /**
+     * Return true if the query should bypass the cache.  The default behavior is to
+     * always use the cache but the method can be overridden for a specific class.
+     */
+    protected boolean bypass(Query query) {
+        return false;
+    }
+
+    /**
      * Determines if a pair of responses are considered equal. Used to determine whether
      * a cache is inadvertently returning stale results when VERIFY is set to true.
      */
@@ -414,7 +433,7 @@
     /**
      * Disable the use of this cache in this process.
      */
-    public final void disableLocal() {
+    public final void disableInstance() {
         synchronized (mLock) {
             mDisabled = true;
             clear();
@@ -422,6 +441,30 @@
     }
 
     /**
+     * Disable the local use of all caches with the same name.  All currently registered caches
+     * using the key will be disabled now, and all future cache instances that use the key will be
+     * disabled in their constructor.
+     */
+    public static final void disableLocal(@NonNull String name) {
+        synchronized (sCorkLock) {
+            sDisabledKeys.add(name);
+            for (PropertyInvalidatedCache cache : sCaches.keySet()) {
+                if (name.equals(cache.mCacheName)) {
+                    cache.disableInstance();
+                }
+            }
+        }
+    }
+
+    /**
+     * Disable this cache in the current process, and all other caches that use the same
+     * property.
+     */
+    public final void disableLocal() {
+        disableLocal(mCacheName);
+    }
+
+    /**
      * Return whether the cache is disabled in this process.
      */
     public final boolean isDisabledLocal() {
@@ -435,8 +478,8 @@
         // Let access to mDisabled race: it's atomic anyway.
         long currentNonce = (!isDisabledLocal()) ? getCurrentNonce() : NONCE_DISABLED;
         for (;;) {
-            if (currentNonce == NONCE_DISABLED || currentNonce == NONCE_UNSET ||
-                currentNonce == NONCE_CORKED) {
+            if (currentNonce == NONCE_DISABLED || currentNonce == NONCE_UNSET
+                    || currentNonce == NONCE_CORKED || bypass(query)) {
                 if (!mDisabled) {
                     // Do not bother collecting statistics if the cache is
                     // locally disabled.
@@ -875,6 +918,15 @@
     }
 
     /**
+     * Report the disabled status of this cache instance.  The return value does not
+     * reflect status of the property key.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean getDisabledState() {
+        return isDisabledLocal();
+    }
+
+    /**
      * Returns a list of caches alive at the current time.
      */
     public static ArrayList<PropertyInvalidatedCache> getActiveCaches() {
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 0ab3f2f..7cb1d89 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -697,6 +697,18 @@
      * service element of manifest file. The value of attribute
      * {@link android.R.attr#foregroundServiceType} can be multiple flags ORed together.</p>
      *
+     * <div class="caution">
+     * <p><strong>Note:</strong>
+     * Beginning with SDK Version {@link android.os.Build.VERSION_CODES#S},
+     * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#S}
+     * or higher are not allowed to start foreground services from the background.
+     * See
+     * <a href="{@docRoot}/about/versions/12/behavior-changes-12">
+     * Behavior changes: Apps targeting Android 12
+     * </a>
+     * for more details.
+     * </div>
+     *
      * @throws ForegroundServiceStartNotAllowedException
      * If the app targeting API is
      * {@link android.os.Build.VERSION_CODES#S} or later, and the service is restricted from
@@ -733,6 +745,18 @@
    * {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MANIFEST} to use all flags that
    * is specified in manifest attribute foregroundServiceType.</p>
    *
+   * <div class="caution">
+   * <p><strong>Note:</strong>
+   * Beginning with SDK Version {@link android.os.Build.VERSION_CODES#S},
+   * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#S}
+   * or higher are not allowed to start foreground services from the background.
+   * See
+   * <a href="{@docRoot}/about/versions/12/behavior-changes-12">
+   * Behavior changes: Apps targeting Android 12
+   * </a>
+   * for more details.
+   * </div>
+   *
    * @param id The identifier for this notification as per
    * {@link NotificationManager#notify(int, Notification)
    * NotificationManager.notify(int, Notification)}; must not be 0.
diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java
index cd82deb..7ef0a19 100644
--- a/core/java/android/app/WallpaperColors.java
+++ b/core/java/android/app/WallpaperColors.java
@@ -111,12 +111,15 @@
     public WallpaperColors(Parcel parcel) {
         mMainColors = new ArrayList<>();
         mAllColors = new HashMap<>();
-        final int count = parcel.readInt();
+        int count = parcel.readInt();
         for (int i = 0; i < count; i++) {
             final int colorInt = parcel.readInt();
             Color color = Color.valueOf(colorInt);
             mMainColors.add(color);
-
+        }
+        count = parcel.readInt();
+        for (int i = 0; i < count; i++) {
+            final int colorInt = parcel.readInt();
             final int population = parcel.readInt();
             mAllColors.put(colorInt, population);
         }
@@ -411,9 +414,16 @@
         for (int i = 0; i < count; i++) {
             Color color = mainColors.get(i);
             dest.writeInt(color.toArgb());
-            Integer population = mAllColors.get(color.toArgb());
-            int populationInt = (population != null) ? population : 0;
-            dest.writeInt(populationInt);
+        }
+        count = mAllColors.size();
+        dest.writeInt(count);
+        for (Map.Entry<Integer, Integer> colorEntry : mAllColors.entrySet()) {
+            if (colorEntry.getKey() != null) {
+                dest.writeInt(colorEntry.getKey());
+                Integer population = colorEntry.getValue();
+                int populationInt = (population != null) ? population : 0;
+                dest.writeInt(populationInt);
+            }
         }
         dest.writeInt(mColorHints);
     }
@@ -476,12 +486,13 @@
 
         WallpaperColors other = (WallpaperColors) o;
         return mMainColors.equals(other.mMainColors)
+                && mAllColors.equals(other.mAllColors)
                 && mColorHints == other.mColorHints;
     }
 
     @Override
     public int hashCode() {
-        return 31 * mMainColors.hashCode() + mColorHints;
+        return (31 * mMainColors.hashCode() * mAllColors.hashCode()) + mColorHints;
     }
 
     /**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index b5b7dbd..8284cdd 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -13896,7 +13896,11 @@
     }
 
     /**
-     * Returns whether USB data signaling is currently enabled by the admin. Callable by any app.
+     * Returns whether USB data signaling is currently enabled.
+     *
+     * <p> When called by a device owner or profile owner of an organization-owned managed profile,
+     * this API returns whether USB data signaling is currently enabled by that admin. When called
+     * by any other app, returns whether USB data signaling is currently enabled on the device.
      *
      * @return {@code true} if USB data signaling is enabled, {@code false} otherwise.
      */
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index ea0e321..9c60f43 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3182,7 +3182,8 @@
      * <p>This function will throw {@link SecurityException} if you do not
      * have permission to start the given service.
      *
-     * <p class="note"><strong>Note:</strong> Each call to startService()
+     * <div class="caution">
+     * <p><strong>Note:</strong> Each call to startService()
      * results in significant work done by the system to manage service
      * lifecycle surrounding the processing of the intent, which can take
      * multiple milliseconds of CPU time. Due to this cost, startService()
@@ -3191,6 +3192,25 @@
      * for high frequency calls.
      * </p>
      *
+     * Beginning with SDK Version {@link android.os.Build.VERSION_CODES#O},
+     * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#O}
+     * or higher are not allowed to start background services from the background.
+     * See
+     * <a href="{@docRoot}/about/versions/oreo/background">
+     * Background Execution Limits</a>
+     * for more details.
+     *
+     * <p><strong>Note:</strong>
+     * Beginning with SDK Version {@link android.os.Build.VERSION_CODES#S},
+     * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#S}
+     * or higher are not allowed to start foreground services from the background.
+     * See
+     * <a href="{@docRoot}/about/versions/12/behavior-changes-12">
+     * Behavior changes: Apps targeting Android 12
+     * </a>
+     * for more details.
+     * </div>
+     *
      * @param service Identifies the service to be started.  The Intent must be
      *      fully explicit (supplying a component name).  Additional values
      *      may be included in the Intent extras to supply arguments along with
@@ -3215,6 +3235,7 @@
      * This excemption extends {@link IllegalStateException}, so apps can
      * use {@code catch (IllegalStateException)} to catch both.
      *
+     * @see #startForegroundService(Intent)
      * @see #stopService
      * @see #bindService
      */
@@ -3232,6 +3253,18 @@
      * at any time, regardless of whether the app hosting the service is in a foreground
      * state.
      *
+     * <div class="caution">
+     * <p><strong>Note:</strong>
+     * Beginning with SDK Version {@link android.os.Build.VERSION_CODES#S},
+     * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#S}
+     * or higher are not allowed to start foreground services from the background.
+     * See
+     * <a href="{@docRoot}/about/versions/12/behavior-changes-12">
+     * Behavior changes: Apps targeting Android 12
+     * </a>
+     * for more details.
+     * </div>
+     *
      * @param service Identifies the service to be started.  The Intent must be
      *      fully explicit (supplying a component name).  Additional values
      *      may be included in the Intent extras to supply arguments along with
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index b0ce6a5..12911d6 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -62,6 +62,8 @@
 
     void bypassNextStagedInstallerCheck(boolean value);
 
+    void bypassNextAllowedApexUpdateCheck(boolean value);
+
     void setAllowUnlimitedSilentUpdates(String installerPackageName);
     void setSilentUpdatesThrottleTime(long throttleTimeInSeconds);
 }
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index c2ac80e..d3ed006 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -586,11 +586,6 @@
             String targetCompilerFilter, boolean force);
 
     /**
-    * Ask the package manager to compile layouts in the given package.
-    */
-    boolean compileLayouts(String packageName);
-
-    /**
      * Ask the package manager to dump profiles associated with a package.
      */
     void dumpProfiles(String packageName);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 33a34be..2ed00b5 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1278,6 +1278,13 @@
      */
     public static final int INSTALL_STAGED = 0x00200000;
 
+    /**
+     * Flag parameter for {@link #installPackage} to indicate that check whether given APEX can be
+     * updated should be disabled for this install.
+     * @hide
+     */
+    public static final int INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK = 0x00400000;
+
     /** @hide */
     @IntDef(flag = true, value = {
             DONT_KILL_APP,
diff --git a/core/java/android/content/pm/PackagePartitions.java b/core/java/android/content/pm/PackagePartitions.java
index 52ee4de..d157768 100644
--- a/core/java/android/content/pm/PackagePartitions.java
+++ b/core/java/android/content/pm/PackagePartitions.java
@@ -119,6 +119,9 @@
         @Nullable
         private final DeferredCanonicalFile mOverlayFolder;
 
+        @NonNull
+        private final File mNonConicalFolder;
+
         private SystemPartition(@NonNull File folder, @PartitionType int type,
                 boolean containsPrivApp, boolean containsOverlay) {
             this.type = type;
@@ -128,6 +131,7 @@
                     : null;
             this.mOverlayFolder = containsOverlay ? new DeferredCanonicalFile(folder, "overlay")
                     : null;
+            this.mNonConicalFolder = folder;
         }
 
         public SystemPartition(@NonNull SystemPartition original) {
@@ -136,6 +140,7 @@
             this.mAppFolder = original.mAppFolder;
             this.mPrivAppFolder = original.mPrivAppFolder;
             this.mOverlayFolder = original.mOverlayFolder;
+            this.mNonConicalFolder = original.mNonConicalFolder;
         }
 
         /**
@@ -153,6 +158,12 @@
             return mFolder.getFile();
         }
 
+        /** Returns the non-canonical folder of the partition. */
+        @NonNull
+        public File getNonConicalFolder() {
+            return mNonConicalFolder;
+        }
+
         /** Returns the canonical app folder of the partition. */
         @Nullable
         public File getAppFolder() {
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index 4c44ba1..5a7f210 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -1164,7 +1164,7 @@
         dest.writeTypedList(this.usesPermissions);
         sForInternedStringList.parcel(this.implicitPermissions, dest, flags);
         sForStringSet.parcel(this.upgradeKeySets, dest, flags);
-        dest.writeMap(this.keySetMapping);
+        ParsingPackageUtils.writeKeySetMapping(dest, this.keySetMapping);
         sForInternedStringList.parcel(this.protectedBroadcasts, dest, flags);
         dest.writeTypedList(this.activities);
         dest.writeTypedList(this.receivers);
@@ -1180,7 +1180,7 @@
         sForInternedString.parcel(this.volumeUuid, dest, flags);
         dest.writeParcelable(this.signingDetails, flags);
         dest.writeString(this.mPath);
-        dest.writeParcelableList(this.queriesIntents, flags);
+        dest.writeTypedList(this.queriesIntents, flags);
         sForInternedStringList.parcel(this.queriesPackages, dest, flags);
         sForInternedStringSet.parcel(this.queriesProviders, dest, flags);
         dest.writeString(this.appComponentFactory);
@@ -1287,7 +1287,7 @@
         this.usesPermissions = in.createTypedArrayList(ParsedUsesPermission.CREATOR);
         this.implicitPermissions = sForInternedStringList.unparcel(in);
         this.upgradeKeySets = sForStringSet.unparcel(in);
-        this.keySetMapping = in.readHashMap(boot);
+        this.keySetMapping = ParsingPackageUtils.readKeySetMapping(in);
         this.protectedBroadcasts = sForInternedStringList.unparcel(in);
 
         this.activities = in.createTypedArrayList(ParsedActivity.CREATOR);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 6fd5333..dce242c 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -87,6 +87,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.FileUtils;
+import android.os.Parcel;
 import android.os.RemoteException;
 import android.os.Trace;
 import android.os.UserHandle;
@@ -3160,6 +3161,68 @@
     }
 
     /**
+     * Writes the keyset mapping to the provided package. {@code null} mappings are permitted.
+     */
+    public static void writeKeySetMapping(@NonNull Parcel dest,
+            @NonNull Map<String, ArraySet<PublicKey>> keySetMapping) {
+        if (keySetMapping == null) {
+            dest.writeInt(-1);
+            return;
+        }
+
+        final int N = keySetMapping.size();
+        dest.writeInt(N);
+
+        for (String key : keySetMapping.keySet()) {
+            dest.writeString(key);
+            ArraySet<PublicKey> keys = keySetMapping.get(key);
+            if (keys == null) {
+                dest.writeInt(-1);
+                continue;
+            }
+
+            final int M = keys.size();
+            dest.writeInt(M);
+            for (int j = 0; j < M; j++) {
+                dest.writeSerializable(keys.valueAt(j));
+            }
+        }
+    }
+
+    /**
+     * Reads a keyset mapping from the given parcel at the given data position. May return
+     * {@code null} if the serialized mapping was {@code null}.
+     */
+    @NonNull
+    public static ArrayMap<String, ArraySet<PublicKey>> readKeySetMapping(@NonNull Parcel in) {
+        final int N = in.readInt();
+        if (N == -1) {
+            return null;
+        }
+
+        ArrayMap<String, ArraySet<PublicKey>> keySetMapping = new ArrayMap<>();
+        for (int i = 0; i < N; ++i) {
+            String key = in.readString();
+            final int M = in.readInt();
+            if (M == -1) {
+                keySetMapping.put(key, null);
+                continue;
+            }
+
+            ArraySet<PublicKey> keys = new ArraySet<>(M);
+            for (int j = 0; j < M; ++j) {
+                PublicKey pk = (PublicKey) in.readSerializable();
+                keys.add(pk);
+            }
+
+            keySetMapping.put(key, keys);
+        }
+
+        return keySetMapping;
+    }
+
+
+    /**
      * Callback interface for retrieving information that may be needed while parsing
      * a package.
      */
diff --git a/core/java/android/content/pm/parsing/component/ParsedComponent.java b/core/java/android/content/pm/parsing/component/ParsedComponent.java
index 4aed77a..9d830ec 100644
--- a/core/java/android/content/pm/parsing/component/ParsedComponent.java
+++ b/core/java/android/content/pm/parsing/component/ParsedComponent.java
@@ -172,7 +172,7 @@
         this.packageName = sForInternedString.unparcel(in);
         this.intents = sForIntentInfos.unparcel(in);
         this.metaData = in.readBundle(boot);
-        this.mProperties = in.createTypedArrayMap(Property.CREATOR);
+        this.mProperties = in.readHashMap(boot);
     }
 
     @NonNull
diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
index 79f716c..e665d0f 100644
--- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -60,7 +60,8 @@
             BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
             BIOMETRIC_ERROR_RE_ENROLL,
             BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
-            FINGERPRINT_ERROR_UNKNOWN})
+            FINGERPRINT_ERROR_UNKNOWN,
+            FINGERPRINT_ERROR_BAD_CALIBARTION})
     @Retention(RetentionPolicy.SOURCE)
     @interface FingerprintError {}
 
@@ -181,6 +182,12 @@
     int FINGERPRINT_ERROR_UNKNOWN = 17;
 
     /**
+     * Error indicating that the fingerprint sensor has bad calibration.
+     * @hide
+     */
+    int FINGERPRINT_ERROR_BAD_CALIBARTION = 18;
+
+    /**
      * @hide
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -201,7 +208,8 @@
             FINGERPRINT_ACQUIRED_TOO_FAST,
             FINGERPRINT_ACQUIRED_VENDOR,
             FINGERPRINT_ACQUIRED_START,
-            FINGERPRINT_ACQUIRED_UNKNOWN})
+            FINGERPRINT_ACQUIRED_UNKNOWN,
+            FINGERPRINT_ACQUIRED_IMMOBILE})
     @Retention(RetentionPolicy.SOURCE)
     @interface FingerprintAcquired {}
 
@@ -271,6 +279,14 @@
     int FINGERPRINT_ACQUIRED_UNKNOWN = 8;
 
     /**
+     * This message may be sent during enrollment if the same area of the finger has already
+     * been captured during this enrollment session. In general, enrolling multiple areas of the
+     * same finger can help against false rejections.
+     * @hide
+     */
+    int FINGERPRINT_ACQUIRED_IMMOBILE = 9;
+
+    /**
      * @hide
      */
     int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index e24332a..5b259f7 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -212,9 +212,9 @@
      */
     private static final class CameraExtensionManagerGlobal {
         private static final String TAG = "CameraExtensionManagerGlobal";
-        private static final String PROXY_PACKAGE_NAME = "com.android.camera";
+        private static final String PROXY_PACKAGE_NAME = "com.android.cameraextensions";
         private static final String PROXY_SERVICE_NAME =
-                "com.android.camera.CameraExtensionsProxyService";
+                "com.android.cameraextensions.CameraExtensionsProxyService";
 
         // Singleton instance
         private static final CameraExtensionManagerGlobal GLOBAL_CAMERA_MANAGER =
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index a39bc4e..1c0ae28 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -16,6 +16,7 @@
 
 package android.hardware.display;
 
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.graphics.Point;
 import android.hardware.SensorManager;
@@ -29,6 +30,9 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -37,6 +41,16 @@
  * @hide Only for use within the system server.
  */
 public abstract class DisplayManagerInternal {
+
+    @IntDef(prefix = {"REFRESH_RATE_LIMIT_"}, value = {
+            REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RefreshRateLimitType {}
+
+    /** Refresh rate should be limited when High Brightness Mode is active. */
+    public static final int REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE = 1;
+
     /**
      * Called by the power manager to initialize power management facilities.
      */
@@ -296,9 +310,10 @@
     public abstract int getRefreshRateSwitchingType();
 
     /**
+     * TODO: b/191384041 - Replace this with getRefreshRateLimitations()
      * Return the refresh rate restriction for the specified display and sensor pairing. If the
      * specified sensor is identified as an associated sensor in the specified display's
-     * display-device-config file, then return any refresh rate restrictions that it might specify.
+     * display-device-config file, then return any refresh rate restrictions that it might define.
      * If no restriction is specified, or the sensor is not associated with the display, then null
      * will be returned.
      *
@@ -313,6 +328,15 @@
             int displayId, String name, String type);
 
     /**
+     * Returns a list of various refresh rate limitations for the specified display.
+     *
+     * @param displayId The display to get limitations for.
+     *
+     * @return a list of {@link RefreshRateLimitation}s describing the various limits.
+     */
+    public abstract List<RefreshRateLimitation> getRefreshRateLimitations(int displayId);
+
+    /**
      * Describes the requested power state of the display.
      *
      * This object is intended to describe the general characteristics of the
@@ -613,4 +637,25 @@
             return "(" + min + " " + max + ")";
         }
     }
+
+    /**
+     * Describes a limitation on a display's refresh rate. Includes the allowed refresh rate
+     * range as well as information about when it applies, such as high-brightness-mode.
+     */
+    public static final class RefreshRateLimitation {
+        @RefreshRateLimitType public int type;
+
+        /** The range the that refresh rate should be limited to. */
+        public RefreshRateRange range;
+
+        public RefreshRateLimitation(@RefreshRateLimitType int type, float min, float max) {
+            this.type = type;
+            range = new RefreshRateRange(min, max);
+        }
+
+        @Override
+        public String toString() {
+            return "RefreshRateLimitation(" + type + ": " + range + ")";
+        }
+    }
 }
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index e594666..0819835 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -1375,6 +1375,9 @@
             case BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED:
                 return context.getString(
                         com.android.internal.R.string.fingerprint_error_security_update_required);
+            case FINGERPRINT_ERROR_BAD_CALIBARTION:
+                context.getString(
+                            com.android.internal.R.string.fingerprint_error_bad_calibration);
             case FINGERPRINT_ERROR_VENDOR: {
                 String[] msgArray = context.getResources().getStringArray(
                         com.android.internal.R.array.fingerprint_error_vendor);
@@ -1414,6 +1417,9 @@
             case FINGERPRINT_ACQUIRED_TOO_FAST:
                 return context.getString(
                     com.android.internal.R.string.fingerprint_acquired_too_fast);
+            case FINGERPRINT_ACQUIRED_IMMOBILE:
+                return context.getString(
+                    com.android.internal.R.string.fingerprint_acquired_immobile);
             case FINGERPRINT_ACQUIRED_VENDOR: {
                 String[] msgArray = context.getResources().getStringArray(
                         com.android.internal.R.array.fingerprint_acquired_vendor);
diff --git a/core/java/android/hardware/input/InputDeviceVibrator.java b/core/java/android/hardware/input/InputDeviceVibrator.java
index d8150e4..653c622 100644
--- a/core/java/android/hardware/input/InputDeviceVibrator.java
+++ b/core/java/android/hardware/input/InputDeviceVibrator.java
@@ -56,10 +56,10 @@
         mDeviceId = deviceId;
         mVibratorInfo = new VibratorInfo.Builder(vibratorId)
                 .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
-                // Set predefined support to empty as we know input devices do not support them.
-                .setSupportedEffects()
-                .setSupportedPrimitives()
-                .setSupportedBraking()
+                // The supported effect and braking lists are known to be empty for input devices,
+                // which is different from not being set (that means the device support is unknown).
+                .setSupportedEffects(new int[0])
+                .setSupportedBraking(new int[0])
                 .build();
         mToken = new Binder();
     }
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java
index 662ebb3..5c28553 100644
--- a/core/java/android/net/VpnManager.java
+++ b/core/java/android/net/VpnManager.java
@@ -389,6 +389,10 @@
 
     /**
      * Starts a legacy VPN.
+     *
+     * Legacy VPN is deprecated starting from Android S. So this API shouldn't be called if the
+     * initial SDK version of device is Android S+. Otherwise, UnsupportedOperationException will be
+     * thrown.
      * @hide
      */
     public void startLegacyVpn(VpnProfile profile) {
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index 86cd23d..752ef3e 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -352,6 +352,7 @@
     public int hashCode() {
         return Objects.hash(
                 mGatewayConnectionName,
+                mTunnelConnectionParams,
                 mExposedCapabilities,
                 Arrays.hashCode(mRetryIntervalsMs),
                 mMaxMtu);
@@ -365,6 +366,7 @@
 
         final VcnGatewayConnectionConfig rhs = (VcnGatewayConnectionConfig) other;
         return mGatewayConnectionName.equals(rhs.mGatewayConnectionName)
+                && mTunnelConnectionParams.equals(rhs.mTunnelConnectionParams)
                 && mExposedCapabilities.equals(rhs.mExposedCapabilities)
                 && Arrays.equals(mRetryIntervalsMs, rhs.mRetryIntervalsMs)
                 && mMaxMtu == rhs.mMaxMtu;
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index 77f8a87..f483752 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -306,14 +306,23 @@
                     AggregateBatteryConsumer.CREATOR.createFromParcel(source);
             mAggregateBatteryConsumers[i].setCustomPowerComponentNames(mCustomPowerComponentNames);
         }
-        int uidCount = source.readInt();
+
+        // UidBatteryConsumers are included as a blob to avoid a TransactionTooLargeException
+        final Parcel blob = Parcel.obtain();
+        final byte[] bytes = source.readBlob();
+        blob.unmarshall(bytes, 0, bytes.length);
+        blob.setDataPosition(0);
+
+        final int uidCount = blob.readInt();
         mUidBatteryConsumers = new ArrayList<>(uidCount);
         for (int i = 0; i < uidCount; i++) {
             final UidBatteryConsumer consumer =
-                    UidBatteryConsumer.CREATOR.createFromParcel(source);
+                    UidBatteryConsumer.CREATOR.createFromParcel(blob);
             consumer.setCustomPowerComponentNames(mCustomPowerComponentNames);
             mUidBatteryConsumers.add(consumer);
         }
+        blob.recycle();
+
         int userCount = source.readInt();
         mUserBatteryConsumers = new ArrayList<>(userCount);
         for (int i = 0; i < userCount; i++) {
@@ -323,14 +332,10 @@
             mUserBatteryConsumers.add(consumer);
         }
         if (source.readBoolean()) {
-            mHistoryBuffer = Parcel.obtain();
-            mHistoryBuffer.setDataSize(0);
-            mHistoryBuffer.setDataPosition(0);
+            final byte[] historyBlob = source.readBlob();
 
-            int historyBufferSize = source.readInt();
-            int curPos = source.dataPosition();
-            mHistoryBuffer.appendFrom(source, curPos, historyBufferSize);
-            source.setDataPosition(curPos + historyBufferSize);
+            mHistoryBuffer = Parcel.obtain();
+            mHistoryBuffer.unmarshall(historyBlob, 0, historyBlob.length);
 
             int historyTagCount = source.readInt();
             mHistoryTagPool = new ArrayList<>(historyTagCount);
@@ -362,21 +367,26 @@
         for (int i = 0; i < AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; i++) {
             mAggregateBatteryConsumers[i].writeToParcel(dest, flags);
         }
-        dest.writeInt(mUidBatteryConsumers.size());
+
+        // UidBatteryConsumers are included as a blob, because each UidBatteryConsumer
+        // takes > 300 bytes, so a typical number of UIDs in the system, 300 would result
+        // in a 90 kB Parcel, which is not safe to pass in a binder transaction because
+        // of the possibility of TransactionTooLargeException
+        final Parcel blob = Parcel.obtain();
+        blob.writeInt(mUidBatteryConsumers.size());
         for (int i = mUidBatteryConsumers.size() - 1; i >= 0; i--) {
-            mUidBatteryConsumers.get(i).writeToParcel(dest, flags);
+            mUidBatteryConsumers.get(i).writeToParcel(blob, flags);
         }
+        dest.writeBlob(blob.marshall());
+        blob.recycle();
+
         dest.writeInt(mUserBatteryConsumers.size());
         for (int i = mUserBatteryConsumers.size() - 1; i >= 0; i--) {
             mUserBatteryConsumers.get(i).writeToParcel(dest, flags);
         }
         if (mHistoryBuffer != null) {
             dest.writeBoolean(true);
-
-            final int historyBufferSize = mHistoryBuffer.dataSize();
-            dest.writeInt(historyBufferSize);
-            dest.appendFrom(mHistoryBuffer, 0, historyBufferSize);
-
+            dest.writeBlob(mHistoryBuffer.marshall());
             dest.writeInt(mHistoryTagPool.size());
             for (int i = mHistoryTagPool.size() - 1; i >= 0; i--) {
                 final BatteryStats.HistoryTag tag = mHistoryTagPool.get(i);
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 12538e6..d7893e4 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -126,6 +126,7 @@
     // The default vibration intensity level for ringtones.
     @VibrationIntensity
     private int mDefaultRingVibrationIntensity;
+    private float mHapticChannelMaxVibrationAmplitude;
 
     /**
      * @hide to prevent subclassing from outside of the framework
@@ -134,7 +135,7 @@
     public Vibrator() {
         mPackageName = ActivityThread.currentPackageName();
         final Context ctx = ActivityThread.currentActivityThread().getSystemContext();
-        loadVibrationIntensities(ctx);
+        loadVibrationConfig(ctx);
     }
 
     /**
@@ -142,22 +143,28 @@
      */
     protected Vibrator(Context context) {
         mPackageName = context.getOpPackageName();
-        loadVibrationIntensities(context);
+        loadVibrationConfig(context);
     }
 
-    private void loadVibrationIntensities(Context context) {
+    private void loadVibrationConfig(Context context) {
         mDefaultHapticFeedbackIntensity = loadDefaultIntensity(context,
                 com.android.internal.R.integer.config_defaultHapticFeedbackIntensity);
         mDefaultNotificationVibrationIntensity = loadDefaultIntensity(context,
                 com.android.internal.R.integer.config_defaultNotificationVibrationIntensity);
         mDefaultRingVibrationIntensity = loadDefaultIntensity(context,
                 com.android.internal.R.integer.config_defaultRingVibrationIntensity);
+        mHapticChannelMaxVibrationAmplitude = loadFloat(context,
+                com.android.internal.R.dimen.config_hapticChannelMaxVibrationAmplitude, 0);
     }
 
     private int loadDefaultIntensity(Context ctx, int resId) {
         return ctx != null ? ctx.getResources().getInteger(resId) : VIBRATION_INTENSITY_MEDIUM;
     }
 
+    private float loadFloat(Context ctx, int resId, float defaultValue) {
+        return ctx != null ? ctx.getResources().getFloat(resId) : defaultValue;
+    }
+
     /** @hide */
     protected VibratorInfo getInfo() {
         return VibratorInfo.EMPTY_VIBRATOR_INFO;
@@ -297,6 +304,24 @@
     }
 
     /**
+     * Return the maximum amplitude the vibrator can play using the audio haptic channels.
+     *
+     * <p>This is a positive value, or {@link Float#NaN NaN} if it's unknown. If this returns a
+     * positive value <code>maxAmplitude</code>, then the signals from the haptic channels of audio
+     * tracks should be in the range <code>[-maxAmplitude, maxAmplitude]</code>.
+     *
+     * @return a positive value representing the maximum absolute value the device can play signals
+     * from audio haptic channels, or {@link Float#NaN NaN} if it's unknown.
+     * @hide
+     */
+    public float getHapticChannelMaximumAmplitude() {
+        if (mHapticChannelMaxVibrationAmplitude <= 0) {
+            return Float.NaN;
+        }
+        return mHapticChannelMaxVibrationAmplitude;
+    }
+
+    /**
      * Configure an always-on haptics effect.
      *
      * @param alwaysOnId The board-specific always-on ID to configure.
diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java
index 597df08..486f9f1 100644
--- a/core/java/android/os/VibratorInfo.java
+++ b/core/java/android/os/VibratorInfo.java
@@ -51,8 +51,11 @@
     private final SparseBooleanArray mSupportedEffects;
     @Nullable
     private final SparseBooleanArray mSupportedBraking;
-    @Nullable
     private final SparseIntArray mSupportedPrimitives;
+    private final int mPrimitiveDelayMax;
+    private final int mCompositionSizeMax;
+    private final int mPwlePrimitiveDurationMax;
+    private final int mPwleSizeMax;
     private final float mQFactor;
     private final FrequencyMapping mFrequencyMapping;
 
@@ -62,6 +65,10 @@
         mSupportedEffects = in.readSparseBooleanArray();
         mSupportedBraking = in.readSparseBooleanArray();
         mSupportedPrimitives = in.readSparseIntArray();
+        mPrimitiveDelayMax = in.readInt();
+        mCompositionSizeMax = in.readInt();
+        mPwlePrimitiveDurationMax = in.readInt();
+        mPwleSizeMax = in.readInt();
         mQFactor = in.readFloat();
         mFrequencyMapping = in.readParcelable(VibratorInfo.class.getClassLoader());
     }
@@ -69,48 +76,50 @@
     /**
      * Default constructor.
      *
-     * @param id                  The vibrator id.
-     * @param capabilities        All capability flags of the vibrator, defined in IVibrator.CAP_*.
-     * @param supportedEffects    All supported predefined effects, enum values from {@link
-     *                            android.hardware.vibrator.Effect}.
-     * @param supportedBraking    All supported braking types, enum values from {@link Braking}.
-     * @param supportedPrimitives All supported primitive effects, enum values from {@link
-     *                            android.hardware.vibrator.CompositePrimitive}.
-     * @param primitiveDurations  A mapping of primitive durations, where indexes are enum values
-     *                            from {@link android.hardware.vibrator.CompositePrimitive} and the
-     *                            values are estimated durations in milliseconds.
-     * @param qFactor             The vibrator quality factor.
-     * @param frequencyMapping    The description of the vibrator supported frequencies and max
-     *                            amplitude mappings.
+     * @param id                       The vibrator id.
+     * @param capabilities             All capability flags of the vibrator, defined in
+     *                                 IVibrator.CAP_*.
+     * @param supportedEffects         All supported predefined effects, enum values from
+     *                                 {@link android.hardware.vibrator.Effect}.
+     * @param supportedBraking         All supported braking types, enum values from {@link
+     *                                 Braking}.
+     * @param supportedPrimitives      All supported primitive effects, key are enum values from
+     *                                 {@link android.hardware.vibrator.CompositePrimitive} and
+     *                                 values are estimated durations in milliseconds.
+     * @param primitiveDelayMax        The maximum delay that can be set to a composition primitive
+     *                                 in milliseconds.
+     * @param compositionSizeMax       The maximum number of primitives supported by a composition.
+     * @param pwlePrimitiveDurationMax The maximum duration of a PWLE primitive in milliseconds.
+     * @param pwleSizeMax              The maximum number of primitives supported by a PWLE
+     *                                 composition.
+     * @param qFactor                  The vibrator quality factor.
+     * @param frequencyMapping         The description of the vibrator supported frequencies and max
+     *                                 amplitude mappings.
      * @hide
      */
-    public VibratorInfo(int id, long capabilities, int[] supportedEffects, int[] supportedBraking,
-            int[] supportedPrimitives, int[] primitiveDurations, float qFactor,
-            @NonNull FrequencyMapping frequencyMapping) {
+    public VibratorInfo(int id, long capabilities, @Nullable SparseBooleanArray supportedEffects,
+            @Nullable SparseBooleanArray supportedBraking,
+            @NonNull SparseIntArray supportedPrimitives, int primitiveDelayMax,
+            int compositionSizeMax, int pwlePrimitiveDurationMax, int pwleSizeMax,
+            float qFactor, @NonNull FrequencyMapping frequencyMapping) {
         mId = id;
         mCapabilities = capabilities;
-        mSupportedEffects = toSparseBooleanArray(supportedEffects);
-        mSupportedBraking = toSparseBooleanArray(supportedBraking);
-        mSupportedPrimitives = toSparseIntArray(supportedPrimitives, primitiveDurations);
+        mSupportedEffects = supportedEffects == null ? null : supportedEffects.clone();
+        mSupportedBraking = supportedBraking == null ? null : supportedBraking.clone();
+        mSupportedPrimitives = supportedPrimitives.clone();
+        mPrimitiveDelayMax = primitiveDelayMax;
+        mCompositionSizeMax = compositionSizeMax;
+        mPwlePrimitiveDurationMax = pwlePrimitiveDurationMax;
+        mPwleSizeMax = pwleSizeMax;
         mQFactor = qFactor;
         mFrequencyMapping = frequencyMapping;
     }
 
     protected VibratorInfo(int id, int capabilities, VibratorInfo baseVibrator) {
-        mId = id;
-        mCapabilities = capabilities;
-        mSupportedEffects = baseVibrator.mSupportedEffects == null ? null :
-                baseVibrator.mSupportedEffects.clone();
-        mSupportedBraking = baseVibrator.mSupportedBraking == null ? null :
-                baseVibrator.mSupportedBraking.clone();
-        mSupportedPrimitives = baseVibrator.mSupportedPrimitives == null ? null :
-                baseVibrator.mSupportedPrimitives.clone();
-        mQFactor = baseVibrator.mQFactor;
-        mFrequencyMapping = new FrequencyMapping(baseVibrator.mFrequencyMapping.mMinFrequencyHz,
-                baseVibrator.mFrequencyMapping.mResonantFrequencyHz,
-                baseVibrator.mFrequencyMapping.mFrequencyResolutionHz,
-                baseVibrator.mFrequencyMapping.mSuggestedSafeRangeHz,
-                baseVibrator.mFrequencyMapping.mMaxAmplitudes);
+        this(id, capabilities, baseVibrator.mSupportedEffects, baseVibrator.mSupportedBraking,
+                baseVibrator.mSupportedPrimitives, baseVibrator.mPrimitiveDelayMax,
+                baseVibrator.mCompositionSizeMax, baseVibrator.mPwlePrimitiveDurationMax,
+                baseVibrator.mPwleSizeMax, baseVibrator.mQFactor, baseVibrator.mFrequencyMapping);
     }
 
     @Override
@@ -120,6 +129,10 @@
         dest.writeSparseBooleanArray(mSupportedEffects);
         dest.writeSparseBooleanArray(mSupportedBraking);
         dest.writeSparseIntArray(mSupportedPrimitives);
+        dest.writeInt(mPrimitiveDelayMax);
+        dest.writeInt(mCompositionSizeMax);
+        dest.writeInt(mPwlePrimitiveDurationMax);
+        dest.writeInt(mPwleSizeMax);
         dest.writeFloat(mQFactor);
         dest.writeParcelable(mFrequencyMapping, flags);
     }
@@ -138,24 +151,23 @@
             return false;
         }
         VibratorInfo that = (VibratorInfo) o;
-        if (mSupportedPrimitives == null || that.mSupportedPrimitives == null) {
-            if (mSupportedPrimitives != that.mSupportedPrimitives) {
+        int supportedPrimitivesCount = mSupportedPrimitives.size();
+        if (supportedPrimitivesCount != that.mSupportedPrimitives.size()) {
+            return false;
+        }
+        for (int i = 0; i < supportedPrimitivesCount; i++) {
+            if (mSupportedPrimitives.keyAt(i) != that.mSupportedPrimitives.keyAt(i)) {
                 return false;
             }
-        } else {
-            if (mSupportedPrimitives.size() != that.mSupportedPrimitives.size()) {
+            if (mSupportedPrimitives.valueAt(i) != that.mSupportedPrimitives.valueAt(i)) {
                 return false;
             }
-            for (int i = 0; i < mSupportedPrimitives.size(); i++) {
-                if (mSupportedPrimitives.keyAt(i) != that.mSupportedPrimitives.keyAt(i)) {
-                    return false;
-                }
-                if (mSupportedPrimitives.valueAt(i) != that.mSupportedPrimitives.valueAt(i)) {
-                    return false;
-                }
-            }
         }
         return mId == that.mId && mCapabilities == that.mCapabilities
+                && mPrimitiveDelayMax == that.mPrimitiveDelayMax
+                && mCompositionSizeMax == that.mCompositionSizeMax
+                && mPwlePrimitiveDurationMax == that.mPwlePrimitiveDurationMax
+                && mPwleSizeMax == that.mPwleSizeMax
                 && Objects.equals(mSupportedEffects, that.mSupportedEffects)
                 && Objects.equals(mSupportedBraking, that.mSupportedBraking)
                 && Objects.equals(mQFactor, that.mQFactor)
@@ -166,11 +178,9 @@
     public int hashCode() {
         int hashCode = Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
                 mQFactor, mFrequencyMapping);
-        if (mSupportedPrimitives != null) {
-            for (int i = 0; i < mSupportedPrimitives.size(); i++) {
-                hashCode = 31 * hashCode + mSupportedPrimitives.keyAt(i);
-                hashCode = 31 * hashCode + mSupportedPrimitives.valueAt(i);
-            }
+        for (int i = 0; i < mSupportedPrimitives.size(); i++) {
+            hashCode = 31 * hashCode + mSupportedPrimitives.keyAt(i);
+            hashCode = 31 * hashCode + mSupportedPrimitives.valueAt(i);
         }
         return hashCode;
     }
@@ -184,6 +194,10 @@
                 + ", mSupportedEffects=" + Arrays.toString(getSupportedEffectsNames())
                 + ", mSupportedBraking=" + Arrays.toString(getSupportedBrakingNames())
                 + ", mSupportedPrimitives=" + Arrays.toString(getSupportedPrimitivesNames())
+                + ", mPrimitiveDelayMax=" + mPrimitiveDelayMax
+                + ", mCompositionSizeMax=" + mCompositionSizeMax
+                + ", mPwlePrimitiveDurationMax=" + mPwlePrimitiveDurationMax
+                + ", mPwleSizeMax=" + mPwleSizeMax
                 + ", mQFactor=" + mQFactor
                 + ", mFrequencyMapping=" + mFrequencyMapping
                 + '}';
@@ -247,7 +261,7 @@
      */
     public boolean isPrimitiveSupported(
             @VibrationEffect.Composition.PrimitiveType int primitiveId) {
-        return hasCapability(IVibrator.CAP_COMPOSE_EFFECTS) && mSupportedPrimitives != null
+        return hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)
                 && (mSupportedPrimitives.indexOfKey(primitiveId) >= 0);
     }
 
@@ -260,7 +274,43 @@
      */
     public int getPrimitiveDuration(
             @VibrationEffect.Composition.PrimitiveType int primitiveId) {
-        return mSupportedPrimitives != null ? mSupportedPrimitives.get(primitiveId) : 0;
+        return mSupportedPrimitives.get(primitiveId);
+    }
+
+    /**
+     * Query the maximum delay supported for a primitive in a composed effect.
+     *
+     * @return The max delay in milliseconds, or zero if unlimited.
+     */
+    public int getPrimitiveDelayMax() {
+        return mPrimitiveDelayMax;
+    }
+
+    /**
+     * Query the maximum number of primitives supported in a composed effect.
+     *
+     * @return The max number of primitives supported, or zero if unlimited.
+     */
+    public int getCompositionSizeMax() {
+        return mCompositionSizeMax;
+    }
+
+    /**
+     * Query the maximum duration supported for a primitive in a PWLE composition.
+     *
+     * @return The max duration in milliseconds, or zero if unlimited.
+     */
+    public int getPwlePrimitiveDurationMax() {
+        return mPwlePrimitiveDurationMax;
+    }
+
+    /**
+     * Query the maximum number of primitives supported in a PWLE composition.
+     *
+     * @return The max number of primitives supported, or zero if unlimited.
+     */
+    public int getPwleSizeMax() {
+        return mPwleSizeMax;
     }
 
     /**
@@ -408,52 +458,15 @@
     }
 
     private String[] getSupportedPrimitivesNames() {
-        if (mSupportedPrimitives == null) {
-            return new String[0];
-        }
-        String[] names = new String[mSupportedPrimitives.size()];
-        for (int i = 0; i < mSupportedPrimitives.size(); i++) {
+        int supportedPrimitivesCount = mSupportedPrimitives.size();
+        String[] names = new String[supportedPrimitivesCount];
+        for (int i = 0; i < supportedPrimitivesCount; i++) {
             names[i] = VibrationEffect.Composition.primitiveToString(mSupportedPrimitives.keyAt(i));
         }
         return names;
     }
 
     /**
-     * Create a {@link SparseBooleanArray} from given {@code supportedKeys} where each key is mapped
-     * to {@code true}.
-     */
-    @Nullable
-    private static SparseBooleanArray toSparseBooleanArray(int[] supportedKeys) {
-        if (supportedKeys == null) {
-            return null;
-        }
-        SparseBooleanArray array = new SparseBooleanArray();
-        for (int key : supportedKeys) {
-            array.put(key, true);
-        }
-        return array;
-    }
-
-    /**
-     * Create a {@link SparseIntArray} from given {@code supportedKeys} where each key is mapped
-     * to the value indexed by it.
-     *
-     * <p>If {@code values} is null or does not contain a given key as a index, then zero is stored
-     * to the sparse array so it can still be used to query the supported keys.
-     */
-    @Nullable
-    private static SparseIntArray toSparseIntArray(int[] supportedKeys, int[] values) {
-        if (supportedKeys == null) {
-            return null;
-        }
-        SparseIntArray array = new SparseIntArray();
-        for (int key : supportedKeys) {
-            array.put(key, (values == null || key >= values.length) ? 0 : values[key]);
-        }
-        return array;
-    }
-
-    /**
      * Describes how frequency should be mapped to absolute values for a specific {@link Vibrator}.
      *
      * <p>This mapping is defined by the following parameters:
@@ -675,11 +688,14 @@
     /** @hide */
     public static final class Builder {
         private final int mId;
-        private int mCapabilities = 0;
-        private int[] mSupportedEffects = null;
-        private int[] mSupportedBraking = null;
-        private int[] mSupportedPrimitives = null;
-        private int[] mPrimitiveDurations = new int[0];
+        private long mCapabilities;
+        private SparseBooleanArray mSupportedEffects;
+        private SparseBooleanArray mSupportedBraking;
+        private SparseIntArray mSupportedPrimitives = new SparseIntArray();
+        private int mPrimitiveDelayMax;
+        private int mCompositionSizeMax;
+        private int mPwlePrimitiveDurationMax;
+        private int mPwleSizeMax;
         private float mQFactor = Float.NaN;
         private FrequencyMapping mFrequencyMapping =
                 new FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, Float.NaN, null);
@@ -691,7 +707,7 @@
 
         /** Configure the vibrator capabilities with a combination of IVibrator.CAP_* values. */
         @NonNull
-        public Builder setCapabilities(int capabilities) {
+        public Builder setCapabilities(long capabilities) {
             mCapabilities = capabilities;
             return this;
         }
@@ -699,34 +715,49 @@
         /** Configure the effects supported with {@link android.hardware.vibrator.Effect} values. */
         @NonNull
         public Builder setSupportedEffects(int... supportedEffects) {
-            mSupportedEffects = supportedEffects;
+            mSupportedEffects = toSparseBooleanArray(supportedEffects);
             return this;
         }
 
         /** Configure braking supported with {@link android.hardware.vibrator.Braking} values. */
         @NonNull
         public Builder setSupportedBraking(int... supportedBraking) {
-            mSupportedBraking = supportedBraking;
+            mSupportedBraking = toSparseBooleanArray(supportedBraking);
             return this;
         }
 
-        /**
-         * Configure the primitives supported with
-         * {@link android.hardware.vibrator.CompositePrimitive} values.
-         */
+        /** Configure maximum duration, in milliseconds, of a PWLE primitive. */
         @NonNull
-        public Builder setSupportedPrimitives(int... supportedPrimitives) {
-            mSupportedPrimitives = supportedPrimitives;
+        public Builder setPwlePrimitiveDurationMax(int pwlePrimitiveDurationMax) {
+            mPwlePrimitiveDurationMax = pwlePrimitiveDurationMax;
+            return this;
+        }
+
+        /** Configure maximum number of primitives supported in a single PWLE composed effect. */
+        @NonNull
+        public Builder setPwleSizeMax(int pwleSizeMax) {
+            mPwleSizeMax = pwleSizeMax;
             return this;
         }
 
         /** Configure the duration of a {@link android.hardware.vibrator.CompositePrimitive}. */
         @NonNull
-        public Builder setPrimitiveDuration(int primitiveId, int duration) {
-            if (mPrimitiveDurations.length <= primitiveId) {
-                mPrimitiveDurations = Arrays.copyOf(mPrimitiveDurations, primitiveId + 1);
-            }
-            mPrimitiveDurations[primitiveId] = duration;
+        public Builder setSupportedPrimitive(int primitiveId, int duration) {
+            mSupportedPrimitives.put(primitiveId, duration);
+            return this;
+        }
+
+        /** Configure maximum delay, in milliseconds, supported in a composed effect primitive. */
+        @NonNull
+        public Builder setPrimitiveDelayMax(int primitiveDelayMax) {
+            mPrimitiveDelayMax = primitiveDelayMax;
+            return this;
+        }
+
+        /** Configure maximum number of primitives supported in a single composed effect. */
+        @NonNull
+        public Builder setCompositionSizeMax(int compositionSizeMax) {
+            mCompositionSizeMax = compositionSizeMax;
             return this;
         }
 
@@ -748,7 +779,25 @@
         @NonNull
         public VibratorInfo build() {
             return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
-                    mSupportedPrimitives, mPrimitiveDurations, mQFactor, mFrequencyMapping);
+                    mSupportedPrimitives, mPrimitiveDelayMax, mCompositionSizeMax,
+                    mPwlePrimitiveDurationMax, mPwleSizeMax, mQFactor, mFrequencyMapping);
+        }
+
+        /**
+         * Create a {@link SparseBooleanArray} from given {@code supportedKeys} where each key is
+         * mapped
+         * to {@code true}.
+         */
+        @Nullable
+        private static SparseBooleanArray toSparseBooleanArray(int[] supportedKeys) {
+            if (supportedKeys == null) {
+                return null;
+            }
+            SparseBooleanArray array = new SparseBooleanArray();
+            for (int key : supportedKeys) {
+                array.put(key, true);
+            }
+            return array;
         }
     }
 
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index c5d0cd4..4ef0e6e 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -908,9 +908,32 @@
      */
     public static boolean shouldShowPackageForIndicatorCached(@NonNull Context context,
             @NonNull String packageName) {
-        if (SYSTEM_PKG.equals(packageName)) {
-            return false;
+        return !getIndicatorExemptedPackages(context).contains(packageName);
+    }
+
+    /**
+     * Get the list of packages that are not shown by the indicators. Only a select few roles, and
+     * the system app itself, are hidden. These values are updated at most every 15 seconds.
+     * @hide
+     */
+    public static Set<String> getIndicatorExemptedPackages(@NonNull Context context) {
+        updateIndicatorExemptedPackages(context);
+        ArraySet<String> pkgNames = new ArraySet<>();
+        pkgNames.add(SYSTEM_PKG);
+        for (int i = 0; i < INDICATOR_EXEMPTED_PACKAGES.length; i++) {
+            String exemptedPackage = INDICATOR_EXEMPTED_PACKAGES[i];
+            if (exemptedPackage != null) {
+                pkgNames.add(exemptedPackage);
+            }
         }
+        return pkgNames;
+    }
+
+    /**
+     * Update the cached indicator exempted packages
+     * @hide
+     */
+    public static void updateIndicatorExemptedPackages(@NonNull Context context) {
         long now = SystemClock.elapsedRealtime();
         if (sLastIndicatorUpdateTime == -1
                 || (now - sLastIndicatorUpdateTime) > EXEMPTED_INDICATOR_ROLE_UPDATE_FREQUENCY_MS) {
@@ -919,14 +942,6 @@
                 INDICATOR_EXEMPTED_PACKAGES[i] = context.getString(EXEMPTED_ROLES[i]);
             }
         }
-        for (int i = 0; i < EXEMPTED_ROLES.length; i++) {
-            String exemptedPackage = INDICATOR_EXEMPTED_PACKAGES[i];
-            if (exemptedPackage != null && exemptedPackage.equals(packageName)) {
-                return false;
-            }
-        }
-
-        return true;
     }
     /**
      * Gets the list of packages that have permissions that specified
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index d4e548e..791764b 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -410,7 +410,9 @@
 
             int usageAttr = usage.getPackageIdHash();
             // If this usage has a proxy, but is not a proxy, it is the end of a chain.
-            if (!proxies.containsKey(usageAttr) && usage.proxy != null) {
+            // TODO remove once camera converted
+            if (!proxies.containsKey(usageAttr) && usage.proxy != null
+                    && !usage.op.equals(OPSTR_RECORD_AUDIO)) {
                 proxyLabels.put(usage, new ArrayList<>());
                 proxyPackages.add(usage.getPackageIdHash());
             }
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 0b11aeb..3b4f7e2 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -124,7 +124,6 @@
     public static final int KM_TAG_DEVICE_UNIQUE_ATTESTATION =
             Tag.DEVICE_UNIQUE_ATTESTATION; // KM_BOOL | 720;
 
-    public static final int KM_TAG_ASSOCIATED_DATA = Tag.ASSOCIATED_DATA; // KM_BYTES | 1000;
     public static final int KM_TAG_NONCE = Tag.NONCE; // KM_BYTES | 1001;
     public static final int KM_TAG_MAC_LENGTH = Tag.MAC_LENGTH; // KM_UINT | 1003;
     public static final int KM_TAG_RESET_SINCE_ID_ROTATION =
diff --git a/core/java/android/service/contentcapture/ActivityEvent.java b/core/java/android/service/contentcapture/ActivityEvent.java
index 1188a3f..74a7355 100644
--- a/core/java/android/service/contentcapture/ActivityEvent.java
+++ b/core/java/android/service/contentcapture/ActivityEvent.java
@@ -55,12 +55,25 @@
      */
     public static final int TYPE_ACTIVITY_DESTROYED = Event.ACTIVITY_DESTROYED;
 
+    /**
+     * TODO: change to public field.
+     * The activity was started.
+     *
+     * <p>There are some reason, ACTIVITY_START cannot be added into UsageStats. We don't depend on
+     * UsageEvents for Activity start.
+     * </p>
+     *
+     * @hide
+     */
+    public static final int TYPE_ACTIVITY_STARTED = 10000;
+
     /** @hide */
     @IntDef(prefix = { "TYPE_" }, value = {
             TYPE_ACTIVITY_RESUMED,
             TYPE_ACTIVITY_PAUSED,
             TYPE_ACTIVITY_STOPPED,
-            TYPE_ACTIVITY_DESTROYED
+            TYPE_ACTIVITY_DESTROYED,
+            TYPE_ACTIVITY_STARTED
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ActivityEventType{}
@@ -86,7 +99,8 @@
      * Gets the event type.
      *
      * @return either {@link #TYPE_ACTIVITY_RESUMED}, {@value #TYPE_ACTIVITY_PAUSED},
-     * {@value #TYPE_ACTIVITY_STOPPED}, or {@value #TYPE_ACTIVITY_DESTROYED}.
+     * {@value #TYPE_ACTIVITY_STOPPED}, {@value #TYPE_ACTIVITY_DESTROYED} or 10000 if the Activity
+     * was started.
      */
     @ActivityEventType
     public int getEventType() {
@@ -104,6 +118,8 @@
                 return "ACTIVITY_STOPPED";
             case TYPE_ACTIVITY_DESTROYED:
                 return "ACTIVITY_DESTROYED";
+            case TYPE_ACTIVITY_STARTED:
+                return "ACTIVITY_STARTED";
             default:
                 return "UKNOWN_TYPE: " + type;
         }
diff --git a/core/java/android/service/translation/TranslationService.java b/core/java/android/service/translation/TranslationService.java
index e1d4a56..93c006a 100644
--- a/core/java/android/service/translation/TranslationService.java
+++ b/core/java/android/service/translation/TranslationService.java
@@ -361,6 +361,11 @@
                 new Consumer<Set<TranslationCapability>>() {
                     @Override
                     public void accept(Set<TranslationCapability> values) {
+                        if (!isValidCapabilities(sourceFormat, targetFormat, values)) {
+                            throw new IllegalStateException("Invalid capabilities and "
+                                    + "format compatibility");
+                        }
+
                         final ArraySet<TranslationCapability> capabilities = new ArraySet<>(values);
                         final Bundle bundle = new Bundle();
                         bundle.putParcelableArray(TranslationManager.EXTRA_CAPABILITIES,
@@ -369,4 +374,23 @@
                     }
                 });
     }
+
+    /**
+     * Helper method to validate capabilities and format compatibility.
+     */
+    private boolean isValidCapabilities(@TranslationSpec.DataFormat int sourceFormat,
+            @TranslationSpec.DataFormat int targetFormat, Set<TranslationCapability> capabilities) {
+        if (sourceFormat != TranslationSpec.DATA_FORMAT_TEXT
+                && targetFormat != TranslationSpec.DATA_FORMAT_TEXT) {
+            return true;
+        }
+
+        for (TranslationCapability capability : capabilities) {
+            if (capability.getState() == TranslationCapability.STATE_REMOVED_AND_AVAILABLE) {
+                return false;
+            }
+        }
+
+        return true;
+    }
 }
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 67b97ce..4137416 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -263,6 +263,7 @@
     private static final int MSG_DETECTION_RESUME = 5;
     private static final int MSG_HOTWORD_REJECTED = 6;
     private static final int MSG_HOTWORD_STATUS_REPORTED = 7;
+    private static final int MSG_PROCESS_RESTARTED = 8;
 
     private final String mText;
     private final Locale mLocale;
@@ -1212,6 +1213,12 @@
             message.arg1 = status;
             message.sendToTarget();
         }
+
+        @Override
+        public void onProcessRestarted() {
+            Slog.i(TAG, "onProcessRestarted");
+            mHandler.sendEmptyMessage(MSG_PROCESS_RESTARTED);
+        }
     }
 
     class MyHandler extends Handler {
@@ -1246,6 +1253,9 @@
                 case MSG_HOTWORD_STATUS_REPORTED:
                     mExternalCallback.onHotwordDetectionServiceInitialized(msg.arg1);
                     break;
+                case MSG_PROCESS_RESTARTED:
+                    mExternalCallback.onHotwordDetectionServiceRestarted();
+                    break;
                 default:
                     super.handleMessage(msg);
             }
diff --git a/core/java/android/service/voice/HotwordDetectedResult.java b/core/java/android/service/voice/HotwordDetectedResult.java
index 846f2f9..315392b 100644
--- a/core/java/android/service/voice/HotwordDetectedResult.java
+++ b/core/java/android/service/voice/HotwordDetectedResult.java
@@ -20,11 +20,18 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.content.res.Resources;
+import android.media.AudioRecord;
 import android.media.MediaSyncEvent;
+import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.PersistableBundle;
 
+import com.android.internal.R;
 import com.android.internal.util.DataClass;
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
 
 /**
  * Represents a result supporting the hotword detection.
@@ -187,16 +194,20 @@
         return new PersistableBundle();
     }
 
+    private static int sMaxBundleSize = -1;
+
     /**
      * Returns the maximum byte size of the information contained in the bundle.
      *
-     * <p>The total size will be calculated as a sum of byte sizes over all bundle keys.
-     *
-     * <p>For example, for a bundle containing a single key: {@code "example_key" -> 42.0f}, the
-     * bundle size will be {@code 11 + Float.BYTES = 15} bytes.
+     * <p>The total size will be calculated by how much bundle data should be written into the
+     * Parcel.
      */
     public static int getMaxBundleSize() {
-        return 50;
+        if (sMaxBundleSize < 0) {
+            sMaxBundleSize = Resources.getSystem().getInteger(
+                    R.integer.config_hotwordDetectedResultMaxBundleSize);
+        }
+        return sMaxBundleSize;
     }
 
     /**
@@ -212,6 +223,34 @@
         return mMediaSyncEvent;
     }
 
+    /**
+     * Returns how many bytes should be written into the Parcel
+     *
+     * @hide
+     */
+    public static int getParcelableSize(@NonNull Parcelable parcelable) {
+        final Parcel p = Parcel.obtain();
+        parcelable.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        final int size = p.dataSize();
+        p.recycle();
+        return size;
+    }
+
+    private void onConstructed() {
+        Preconditions.checkArgumentInRange(mScore, 0, getMaxScore(), "score");
+        Preconditions.checkArgumentInRange(mPersonalizedScore, 0, getMaxScore(),
+                "personalizedScore");
+        Preconditions.checkArgumentInRange(mHotwordPhraseId, 0, getMaxHotwordPhraseId(),
+                "hotwordPhraseId");
+        Preconditions.checkArgumentInRange((long) mHotwordDurationMillis, 0,
+                AudioRecord.getMaxSharedAudioHistoryMillis(), "hotwordDurationMillis");
+        if (!mExtras.isEmpty()) {
+            Preconditions.checkArgumentInRange(getParcelableSize(mExtras), 0, getMaxBundleSize(),
+                    "extras");
+        }
+    }
+
 
 
     // Code below generated by codegen v1.0.23.
@@ -290,7 +329,7 @@
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mExtras);
 
-        // onConstructed(); // You can define this method to get a callback
+        onConstructed();
     }
 
     /**
@@ -422,7 +461,7 @@
         //noinspection PointlessBooleanExpression
         return true
                 && mConfidenceLevel == that.mConfidenceLevel
-                && java.util.Objects.equals(mMediaSyncEvent, that.mMediaSyncEvent)
+                && Objects.equals(mMediaSyncEvent, that.mMediaSyncEvent)
                 && mHotwordOffsetMillis == that.mHotwordOffsetMillis
                 && mHotwordDurationMillis == that.mHotwordDurationMillis
                 && mAudioChannel == that.mAudioChannel
@@ -430,7 +469,7 @@
                 && mScore == that.mScore
                 && mPersonalizedScore == that.mPersonalizedScore
                 && mHotwordPhraseId == that.mHotwordPhraseId
-                && java.util.Objects.equals(mExtras, that.mExtras);
+                && Objects.equals(mExtras, that.mExtras);
     }
 
     @Override
@@ -441,7 +480,7 @@
 
         int _hash = 1;
         _hash = 31 * _hash + mConfidenceLevel;
-        _hash = 31 * _hash + java.util.Objects.hashCode(mMediaSyncEvent);
+        _hash = 31 * _hash + Objects.hashCode(mMediaSyncEvent);
         _hash = 31 * _hash + mHotwordOffsetMillis;
         _hash = 31 * _hash + mHotwordDurationMillis;
         _hash = 31 * _hash + mAudioChannel;
@@ -449,13 +488,13 @@
         _hash = 31 * _hash + mScore;
         _hash = 31 * _hash + mPersonalizedScore;
         _hash = 31 * _hash + mHotwordPhraseId;
-        _hash = 31 * _hash + java.util.Objects.hashCode(mExtras);
+        _hash = 31 * _hash + Objects.hashCode(mExtras);
         return _hash;
     }
 
     @Override
     @DataClass.Generated.Member
-    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         // You can override field parcelling by defining methods like:
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
@@ -481,7 +520,7 @@
     /** @hide */
     @SuppressWarnings({"unchecked", "RedundantCast"})
     @DataClass.Generated.Member
-    /* package-private */ HotwordDetectedResult(@NonNull android.os.Parcel in) {
+    /* package-private */ HotwordDetectedResult(@NonNull Parcel in) {
         // You can override field unparcelling by defining methods like:
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
@@ -512,7 +551,7 @@
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mExtras);
 
-        // onConstructed(); // You can define this method to get a callback
+        onConstructed();
     }
 
     @DataClass.Generated.Member
@@ -524,7 +563,7 @@
         }
 
         @Override
-        public HotwordDetectedResult createFromParcel(@NonNull android.os.Parcel in) {
+        public HotwordDetectedResult createFromParcel(@NonNull Parcel in) {
             return new HotwordDetectedResult(in);
         }
     };
@@ -745,10 +784,10 @@
     }
 
     @DataClass.Generated(
-            time = 1621943150502L,
+            time = 1624361647985L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/service/voice/HotwordDetectedResult.java",
-            inputSignatures = "public static final  int CONFIDENCE_LEVEL_NONE\npublic static final  int CONFIDENCE_LEVEL_LOW\npublic static final  int CONFIDENCE_LEVEL_LOW_MEDIUM\npublic static final  int CONFIDENCE_LEVEL_MEDIUM\npublic static final  int CONFIDENCE_LEVEL_MEDIUM_HIGH\npublic static final  int CONFIDENCE_LEVEL_HIGH\npublic static final  int CONFIDENCE_LEVEL_VERY_HIGH\npublic static final  int HOTWORD_OFFSET_UNSET\npublic static final  int AUDIO_CHANNEL_UNSET\nprivate final @android.service.voice.HotwordDetectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate @android.annotation.Nullable android.media.MediaSyncEvent mMediaSyncEvent\nprivate  int mHotwordOffsetMillis\nprivate  int mHotwordDurationMillis\nprivate  int mAudioChannel\nprivate  boolean mHotwordDetectionPersonalized\nprivate final  int mScore\nprivate final  int mPersonalizedScore\nprivate final  int mHotwordPhraseId\nprivate final @android.annotation.NonNull android.os.PersistableBundle mExtras\nprivate static  int defaultConfidenceLevel()\nprivate static  int defaultScore()\nprivate static  int defaultPersonalizedScore()\npublic static  int getMaxScore()\nprivate static  int defaultHotwordPhraseId()\npublic static  int getMaxHotwordPhraseId()\nprivate static  android.os.PersistableBundle defaultExtras()\npublic static  int getMaxBundleSize()\npublic @android.annotation.Nullable android.media.MediaSyncEvent getMediaSyncEvent()\nclass HotwordDetectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
+            inputSignatures = "public static final  int CONFIDENCE_LEVEL_NONE\npublic static final  int CONFIDENCE_LEVEL_LOW\npublic static final  int CONFIDENCE_LEVEL_LOW_MEDIUM\npublic static final  int CONFIDENCE_LEVEL_MEDIUM\npublic static final  int CONFIDENCE_LEVEL_MEDIUM_HIGH\npublic static final  int CONFIDENCE_LEVEL_HIGH\npublic static final  int CONFIDENCE_LEVEL_VERY_HIGH\npublic static final  int HOTWORD_OFFSET_UNSET\npublic static final  int AUDIO_CHANNEL_UNSET\nprivate final @android.service.voice.HotwordDetectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate @android.annotation.Nullable android.media.MediaSyncEvent mMediaSyncEvent\nprivate  int mHotwordOffsetMillis\nprivate  int mHotwordDurationMillis\nprivate  int mAudioChannel\nprivate  boolean mHotwordDetectionPersonalized\nprivate final  int mScore\nprivate final  int mPersonalizedScore\nprivate final  int mHotwordPhraseId\nprivate final @android.annotation.NonNull android.os.PersistableBundle mExtras\nprivate static  int sMaxBundleSize\nprivate static  int defaultConfidenceLevel()\nprivate static  int defaultScore()\nprivate static  int defaultPersonalizedScore()\npublic static  int getMaxScore()\nprivate static  int defaultHotwordPhraseId()\npublic static  int getMaxHotwordPhraseId()\nprivate static  android.os.PersistableBundle defaultExtras()\npublic static  int getMaxBundleSize()\npublic @android.annotation.Nullable android.media.MediaSyncEvent getMediaSyncEvent()\npublic static  int getParcelableSize(android.os.Parcelable)\nprivate  void onConstructed()\nclass HotwordDetectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java
index b66d93d..567ee2f 100644
--- a/core/java/android/service/voice/HotwordDetectionService.java
+++ b/core/java/android/service/voice/HotwordDetectionService.java
@@ -76,6 +76,7 @@
     private static final boolean DBG = true;
 
     private static final long UPDATE_TIMEOUT_MILLIS = 5000;
+
     /** @hide */
     public static final String KEY_INITIALIZATION_STATUS = "initialization_status";
 
@@ -291,9 +292,7 @@
             @Nullable PersistableBundle options,
             @Nullable SharedMemory sharedMemory,
             @DurationMillisLong long callbackTimeoutMillis,
-            @Nullable IntConsumer statusCallback) {
-        // TODO: Handle the unimplemented case by throwing?
-    }
+            @Nullable IntConsumer statusCallback) {}
 
     /**
      * Called when the {@link VoiceInteractionService} requests that this service
@@ -390,6 +389,14 @@
          */
         public void onDetected(@NonNull HotwordDetectedResult result) {
             requireNonNull(result);
+            final PersistableBundle persistableBundle = result.getExtras();
+            if (!persistableBundle.isEmpty() && HotwordDetectedResult.getParcelableSize(
+                    persistableBundle) > HotwordDetectedResult.getMaxBundleSize()) {
+                throw new IllegalArgumentException(
+                        "The bundle size of result is larger than max bundle size ("
+                                + HotwordDetectedResult.getMaxBundleSize()
+                                + ") of HotwordDetectedResult");
+            }
             try {
                 mRemoteCallback.onDetected(result);
             } catch (RemoteException e) {
diff --git a/core/java/android/service/voice/SoftwareHotwordDetector.java b/core/java/android/service/voice/SoftwareHotwordDetector.java
index 204e7df..fb540b1 100644
--- a/core/java/android/service/voice/SoftwareHotwordDetector.java
+++ b/core/java/android/service/voice/SoftwareHotwordDetector.java
@@ -122,7 +122,7 @@
             this.mCallback = callback;
         }
 
-        /** TODO: onDetected */
+        /** Called when the detected result is valid. */
         @Override
         public void onDetected(
                 @Nullable HotwordDetectedResult hotwordDetectedResult,
@@ -150,33 +150,45 @@
         public void onKeyphraseDetected(
                 SoundTrigger.KeyphraseRecognitionEvent recognitionEvent,
                 HotwordDetectedResult result) {
-
+            if (DEBUG) {
+                Slog.i(TAG, "Ignored #onKeyphraseDetected event");
+            }
         }
 
         @Override
         public void onGenericSoundTriggerDetected(
                 SoundTrigger.GenericRecognitionEvent recognitionEvent) throws RemoteException {
-
+            if (DEBUG) {
+                Slog.i(TAG, "Ignored #onGenericSoundTriggerDetected event");
+            }
         }
 
         @Override
         public void onRejected(HotwordRejectedResult result) throws RemoteException {
-
+            if (DEBUG) {
+                Slog.i(TAG, "Ignored #onRejected event");
+            }
         }
 
         @Override
         public void onError(int status) throws RemoteException {
-
+            if (DEBUG) {
+                Slog.i(TAG, "Ignored #onError (" + status + ") event");
+            }
         }
 
         @Override
         public void onRecognitionPaused() throws RemoteException {
-
+            if (DEBUG) {
+                Slog.i(TAG, "Ignored #onRecognitionPaused event");
+            }
         }
 
         @Override
         public void onRecognitionResumed() throws RemoteException {
-
+            if (DEBUG) {
+                Slog.i(TAG, "Ignored #onRecognitionResumed event");
+            }
         }
 
         @Override
@@ -187,6 +199,14 @@
                     mCallback,
                     status));
         }
+
+        @Override
+        public void onProcessRestarted() throws RemoteException {
+            Slog.v(TAG, "onProcessRestarted()");
+            mHandler.sendMessage(obtainMessage(
+                    HotwordDetector.Callback::onHotwordDetectionServiceRestarted,
+                    mCallback));
+        }
     }
 
     /** @hide */
diff --git a/core/java/android/view/CrossWindowBlurListeners.java b/core/java/android/view/CrossWindowBlurListeners.java
index e307b96..761a2b8 100644
--- a/core/java/android/view/CrossWindowBlurListeners.java
+++ b/core/java/android/view/CrossWindowBlurListeners.java
@@ -73,14 +73,14 @@
         return instance;
     }
 
-    boolean isCrossWindowBlurEnabled() {
+    public boolean isCrossWindowBlurEnabled() {
         synchronized (sLock) {
             attachInternalListenerIfNeededLocked();
             return mCrossWindowBlurEnabled;
         }
     }
 
-    void addListener(@NonNull @CallbackExecutor Executor executor,
+    public void addListener(@NonNull @CallbackExecutor Executor executor,
             @NonNull Consumer<Boolean> listener) {
         Preconditions.checkNotNull(listener, "listener cannot be null");
         Preconditions.checkNotNull(executor, "executor cannot be null");
@@ -94,7 +94,7 @@
     }
 
 
-    void removeListener(Consumer<Boolean> listener) {
+    public void removeListener(Consumer<Boolean> listener) {
         Preconditions.checkNotNull(listener, "listener cannot be null");
 
         synchronized (sLock) {
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 8080883..145607a 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1265,7 +1265,7 @@
                     }
                     stateChanged |= getSourceConsumer(types.valueAt(j)).notifyAnimationFinished();
                 }
-                if (invokeCallback && runningAnimation.startDispatched) {
+                if (invokeCallback) {
                     dispatchAnimationEnd(runningAnimation.runner.getAnimation());
                 }
                 break;
diff --git a/core/java/android/view/ScrollCaptureTarget.java b/core/java/android/view/ScrollCaptureTarget.java
index 44017ed..a8bb037 100644
--- a/core/java/android/view/ScrollCaptureTarget.java
+++ b/core/java/android/view/ScrollCaptureTarget.java
@@ -21,13 +21,10 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UiThread;
-import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.CancellationSignal;
 
-import com.android.internal.util.FastMath;
-
 import java.io.PrintWriter;
 import java.util.function.Consumer;
 
@@ -43,8 +40,7 @@
     private final int mHint;
     private Rect mScrollBounds;
 
-    private final float[] mTmpFloatArr = new float[2];
-    private final Matrix mMatrixViewLocalToWindow = new Matrix();
+    private final int[] mTmpIntArr = new int[2];
 
     public ScrollCaptureTarget(@NonNull View scrollTarget, @NonNull Rect localVisibleRect,
             @NonNull Point positionInWindow, @NonNull ScrollCaptureCallback callback) {
@@ -117,28 +113,15 @@
         }
     }
 
-    private static void zero(float[] pointArray) {
-        pointArray[0] = 0;
-        pointArray[1] = 0;
-    }
-
-    private static void roundIntoPoint(Point pointObj, float[] pointArray) {
-        pointObj.x = FastMath.round(pointArray[0]);
-        pointObj.y = FastMath.round(pointArray[1]);
-    }
-
     /**
-     * Refresh the local visible bounds and it's offset within the window, based on the current
+     * Refresh the local visible bounds and its offset within the window, based on the current
      * state of the {@code containing view}.
      */
     @UiThread
     public void updatePositionInWindow() {
-        mMatrixViewLocalToWindow.reset();
-        mContainingView.transformMatrixToGlobal(mMatrixViewLocalToWindow);
-
-        zero(mTmpFloatArr);
-        mMatrixViewLocalToWindow.mapPoints(mTmpFloatArr);
-        roundIntoPoint(mPositionInWindow, mTmpFloatArr);
+        mContainingView.getLocationInWindow(mTmpIntArr);
+        mPositionInWindow.x = mTmpIntArr[0];
+        mPositionInWindow.y = mTmpIntArr[1];
     }
 
     public String toString() {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index f34cd8f..4e66ceb 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -233,6 +233,7 @@
     private static native void nativeRemoveJankDataListener(long nativeListener);
     private static native long nativeCreateJankDataListenerWrapper(OnJankDataListener listener);
     private static native int nativeGetGPUContextPriority();
+    private static native void nativeSetTransformHint(long nativeObject, int transformHint);
 
     @Nullable
     @GuardedBy("mLock")
@@ -348,6 +349,8 @@
     @GuardedBy("mLock")
     private int mHeight;
 
+    private int mTransformHint;
+
     private WeakReference<View> mLocalOwnerView;
 
     static GlobalTransactionWrapper sGlobalTransaction;
@@ -605,6 +608,7 @@
         mName = other.mName;
         mWidth = other.mWidth;
         mHeight = other.mHeight;
+        mTransformHint = other.mTransformHint;
         mLocalOwnerView = other.mLocalOwnerView;
         assignNativeObject(nativeCopyFromSurfaceControl(other.mNativeObject), callsite);
     }
@@ -1467,6 +1471,7 @@
         mName = in.readString8();
         mWidth = in.readInt();
         mHeight = in.readInt();
+        mTransformHint = in.readInt();
 
         long object = 0;
         if (in.readInt() != 0) {
@@ -1485,6 +1490,7 @@
         dest.writeString8(mName);
         dest.writeInt(mWidth);
         dest.writeInt(mHeight);
+        dest.writeInt(mTransformHint);
         if (mNativeObject == 0) {
             dest.writeInt(0);
         } else {
@@ -3602,4 +3608,27 @@
         mHeight = h;
         nativeUpdateDefaultBufferSize(mNativeObject, w, h);
     }
+
+    /**
+     * @hide
+     */
+    public int getTransformHint() {
+        return mTransformHint;
+    }
+
+    /**
+     * Update the transform hint of current SurfaceControl. Only affect if type is
+     * {@link #FX_SURFACE_BLAST}
+     *
+     * The transform hint is used to prevent allocating a buffer of different size when a
+     * layer is rotated. The producer can choose to consume the hint and allocate the buffer
+     * with the same size.
+     * @hide
+     */
+    public void setTransformHint(@Surface.Rotation int transformHint) {
+        if (mTransformHint != transformHint) {
+            mTransformHint = transformHint;
+            nativeSetTransformHint(mNativeObject, transformHint);
+        }
+    }
 }
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 6b0bb9d..4f2cf6d 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -30,7 +30,6 @@
 import android.graphics.BlendMode;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.HardwareRenderer;
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
@@ -214,6 +213,7 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     final Rect mSurfaceFrame = new Rect();
     int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
+    int mTransformHint = 0;
 
     private boolean mGlobalListenersAdded;
     private boolean mAttachedToWindow;
@@ -944,7 +944,7 @@
     }
 
     private boolean performSurfaceTransaction(ViewRootImpl viewRoot, Translator translator,
-            boolean creating, boolean sizeChanged) {
+            boolean creating, boolean sizeChanged, boolean hintChanged) {
         boolean realSizeChanged = false;
 
         mSurfaceLock.lock();
@@ -1009,7 +1009,7 @@
                 }
             }
             mTmpTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
-            if (sizeChanged && !creating) {
+            if ((sizeChanged || hintChanged) && !creating) {
                 setBufferSize(mTmpTransaction);
             }
 
@@ -1081,17 +1081,18 @@
             || mWindowSpaceTop != mLocation[1];
         final boolean layoutSizeChanged = getWidth() != mScreenRect.width()
             || getHeight() != mScreenRect.height();
-
+        final boolean hintChanged = viewRoot.getSurfaceTransformHint() != mTransformHint;
 
         if (creating || formatChanged || sizeChanged || visibleChanged ||
                 (mUseAlpha && alphaChanged) || windowVisibleChanged ||
-                positionChanged || layoutSizeChanged) {
+                positionChanged || layoutSizeChanged || hintChanged) {
             getLocationInWindow(mLocation);
 
             if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
                     + "Changes: creating=" + creating
                     + " format=" + formatChanged + " size=" + sizeChanged
                     + " visible=" + visibleChanged + " alpha=" + alphaChanged
+                    + " hint=" + hintChanged
                     + " mUseAlpha=" + mUseAlpha
                     + " visible=" + visibleChanged
                     + " left=" + (mWindowSpaceLeft != mLocation[0])
@@ -1105,6 +1106,7 @@
                 mSurfaceHeight = myHeight;
                 mFormat = mRequestedFormat;
                 mLastWindowVisibility = mWindowVisibility;
+                mTransformHint = viewRoot.getSurfaceTransformHint();
 
                 mScreenRect.left = mWindowSpaceLeft;
                 mScreenRect.top = mWindowSpaceTop;
@@ -1130,9 +1132,9 @@
                 }
 
                 final boolean realSizeChanged = performSurfaceTransaction(viewRoot,
-                        translator, creating, sizeChanged);
-                final boolean redrawNeeded = sizeChanged || creating ||
-                    (mVisible && !mDrawFinished);
+                        translator, creating, sizeChanged, hintChanged);
+                final boolean redrawNeeded = sizeChanged || creating || hintChanged
+                        || (mVisible && !mDrawFinished);
 
                 try {
                     SurfaceHolder.Callback[] callbacks = null;
@@ -1158,7 +1160,7 @@
                                 c.surfaceCreated(mSurfaceHolder);
                             }
                         }
-                        if (creating || formatChanged || sizeChanged
+                        if (creating || formatChanged || sizeChanged || hintChanged
                                 || visibleChanged || realSizeChanged) {
                             if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
                                     + "surfaceChanged -- format=" + mFormat
@@ -1234,6 +1236,7 @@
 
     private void setBufferSize(Transaction transaction) {
         if (mUseBlastAdapter) {
+            mBlastSurfaceControl.setTransformHint(mTransformHint);
             mBlastBufferQueue.update(mBlastSurfaceControl, mSurfaceWidth, mSurfaceHeight, mFormat);
         } else {
             transaction.setBufferSize(mSurfaceControl, mSurfaceWidth, mSurfaceHeight);
@@ -1330,6 +1333,8 @@
         if (mBlastBufferQueue != null) {
             mBlastBufferQueue.destroy();
         }
+        mTransformHint = viewRoot.getSurfaceTransformHint();
+        mBlastSurfaceControl.setTransformHint(mTransformHint);
         mBlastBufferQueue = new BLASTBufferQueue(name, mBlastSurfaceControl, mSurfaceWidth,
                 mSurfaceHeight, mFormat);
     }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index cf8c746..573ae99 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -10382,4 +10382,8 @@
         });
         return true;
     }
+
+    int getSurfaceTransformHint() {
+        return mSurfaceControl.getTransformHint();
+    }
 }
diff --git a/core/java/android/view/ViewRootInsetsControllerHost.java b/core/java/android/view/ViewRootInsetsControllerHost.java
index d8cd605..27821fd 100644
--- a/core/java/android/view/ViewRootInsetsControllerHost.java
+++ b/core/java/android/view/ViewRootInsetsControllerHost.java
@@ -110,6 +110,10 @@
     @Override
     public void dispatchWindowInsetsAnimationEnd(@NonNull WindowInsetsAnimation animation) {
         if (DEBUG) Log.d(TAG, "windowInsetsAnimation ended");
+        if (mViewRoot.mView == null) {
+            // The view has already detached from window.
+            return;
+        }
         mViewRoot.mView.dispatchWindowInsetsAnimationEnd(animation);
     }
 
diff --git a/core/java/android/view/WindowInsetsAnimation.java b/core/java/android/view/WindowInsetsAnimation.java
index ab5b5ba..6576eea 100644
--- a/core/java/android/view/WindowInsetsAnimation.java
+++ b/core/java/android/view/WindowInsetsAnimation.java
@@ -360,6 +360,13 @@
          * finished, and then revert to the starting state of the animation in the first
          * {@link #onProgress} callback by using post-layout view properties like {@link View#setX}
          * and related methods.
+         *
+         * <p>Note that the animation might be cancelled before {@link #onStart} is dispatched. On
+         * {@link android.os.Build.VERSION_CODES#S S} and later, {@link #onEnd} is immediately
+         * dispatched without an {@link #onStart} in that case.
+         * On {@link android.os.Build.VERSION_CODES#R R}, no callbacks are dispatched after
+         * {@code #onPrepare} for such an animation.
+         *
          * <p>
          * Note: If the animation is application controlled by using
          * {@link WindowInsetsController#controlWindowInsetsAnimation}, the end state of the
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index 10ae691..ce6d034 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -286,6 +286,15 @@
         return this;
     }
 
+    boolean hasSameComposingSpan(@NonNull ContentCaptureEvent other) {
+        return mComposingStart == other.mComposingStart && mComposingEnd == other.mComposingEnd;
+    }
+
+    boolean hasSameSelectionSpan(@NonNull ContentCaptureEvent other) {
+        return mSelectionStartIndex == other.mSelectionStartIndex
+                && mSelectionEndIndex == other.mSelectionEndIndex;
+    }
+
     private int getComposingStart() {
         return mComposingStart;
     }
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index bcb9142..7ec9d34 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -368,7 +368,10 @@
                     final CharSequence lastText = lastEvent.getText();
                     final boolean bothNonEmpty = !TextUtils.isEmpty(lastText)
                             && !TextUtils.isEmpty(text);
-                    boolean equalContent = TextUtils.equals(lastText, text);
+                    boolean equalContent =
+                            TextUtils.equals(lastText, text)
+                            && lastEvent.hasSameComposingSpan(event)
+                            && lastEvent.hasSameSelectionSpan(event);
                     if (equalContent) {
                         addEvent = false;
                     } else if (bothNonEmpty) {
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index bdd1206..c4540b0 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -163,6 +163,17 @@
     }
 
     /**
+     * Called after only the composing region is modified (so it isn't called if the text also
+     * changes).
+     * <p>
+     * Default implementation does nothing.
+     *
+     * @hide
+     */
+    public void endComposingRegionEditInternal() {
+    }
+
+    /**
      * Default implementation calls {@link #finishComposingText()} and
      * {@code setImeConsumesInput(false)}.
      */
@@ -468,6 +479,7 @@
             // Note: sendCurrentText does nothing unless mFallbackMode is set
             sendCurrentText();
             endBatchEdit();
+            endComposingRegionEditInternal();
         }
         return true;
     }
@@ -734,6 +746,7 @@
             // Note: sendCurrentText does nothing unless mFallbackMode is set
             sendCurrentText();
             endBatchEdit();
+            endComposingRegionEditInternal();
         }
         return true;
     }
diff --git a/core/java/android/view/translation/TranslationCapability.java b/core/java/android/view/translation/TranslationCapability.java
index d104b24..65b749a 100644
--- a/core/java/android/view/translation/TranslationCapability.java
+++ b/core/java/android/view/translation/TranslationCapability.java
@@ -61,6 +61,13 @@
      * was dropped.</p>
      */
     public static final @ModelState int STATE_NOT_AVAILABLE = 4;
+    /**
+     * The translation between the source and target specs were removed from the system, but is
+     * still available to be downloaded again.
+     *
+     * @hide
+     */
+    public static final @ModelState int STATE_REMOVED_AND_AVAILABLE = 1000;
 
     /**
      * The state of translation readiness between {@code mSourceSpec} and {@code mTargetSpec}.
@@ -134,7 +141,8 @@
         STATE_AVAILABLE_TO_DOWNLOAD,
         STATE_DOWNLOADING,
         STATE_ON_DEVICE,
-        STATE_NOT_AVAILABLE
+        STATE_NOT_AVAILABLE,
+        STATE_REMOVED_AND_AVAILABLE
     })
     @Retention(RetentionPolicy.SOURCE)
     @DataClass.Generated.Member
@@ -152,6 +160,8 @@
                     return "STATE_ON_DEVICE";
             case STATE_NOT_AVAILABLE:
                     return "STATE_NOT_AVAILABLE";
+            case STATE_REMOVED_AND_AVAILABLE:
+                    return "STATE_REMOVED_AND_AVAILABLE";
             default: return Integer.toHexString(value);
         }
     }
@@ -255,13 +265,15 @@
         if (!(mState == STATE_AVAILABLE_TO_DOWNLOAD)
                 && !(mState == STATE_DOWNLOADING)
                 && !(mState == STATE_ON_DEVICE)
-                && !(mState == STATE_NOT_AVAILABLE)) {
+                && !(mState == STATE_NOT_AVAILABLE)
+                && !(mState == STATE_REMOVED_AND_AVAILABLE)) {
             throw new java.lang.IllegalArgumentException(
                     "state was " + mState + " but must be one of: "
                             + "STATE_AVAILABLE_TO_DOWNLOAD(" + STATE_AVAILABLE_TO_DOWNLOAD + "), "
                             + "STATE_DOWNLOADING(" + STATE_DOWNLOADING + "), "
                             + "STATE_ON_DEVICE(" + STATE_ON_DEVICE + "), "
-                            + "STATE_NOT_AVAILABLE(" + STATE_NOT_AVAILABLE + ")");
+                            + "STATE_NOT_AVAILABLE(" + STATE_NOT_AVAILABLE + "), "
+                            + "STATE_REMOVED_AND_AVAILABLE(" + STATE_REMOVED_AND_AVAILABLE + ")");
         }
 
         this.mSourceSpec = sourceSpec;
@@ -293,10 +305,10 @@
     };
 
     @DataClass.Generated(
-            time = 1621545303074L,
+            time = 1624307114468L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/view/translation/TranslationCapability.java",
-            inputSignatures = "public static final @android.view.translation.TranslationCapability.ModelState int STATE_AVAILABLE_TO_DOWNLOAD\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_DOWNLOADING\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_ON_DEVICE\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_NOT_AVAILABLE\nprivate final @android.view.translation.TranslationCapability.ModelState int mState\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mSourceSpec\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mTargetSpec\nprivate final  boolean mUiTranslationEnabled\nprivate final @android.view.translation.TranslationContext.TranslationFlag int mSupportedTranslationFlags\nclass TranslationCapability extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstDefs=true, genToString=true, genConstructor=false)")
+            inputSignatures = "public static final @android.view.translation.TranslationCapability.ModelState int STATE_AVAILABLE_TO_DOWNLOAD\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_DOWNLOADING\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_ON_DEVICE\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_NOT_AVAILABLE\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_REMOVED_AND_AVAILABLE\nprivate final @android.view.translation.TranslationCapability.ModelState int mState\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mSourceSpec\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mTargetSpec\nprivate final  boolean mUiTranslationEnabled\nprivate final @android.view.translation.TranslationContext.TranslationFlag int mSupportedTranslationFlags\nclass TranslationCapability extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstDefs=true, genToString=true, genConstructor=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index 5ac878d..592993c 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -424,7 +424,7 @@
                     if (callback == null) {
                         if (view instanceof TextView) {
                             // developer doesn't provide their override, we set the default TextView
-                            // implememtation.
+                            // implementation.
                             callback = new TextViewTranslationCallback();
                             view.setViewTranslationCallback(callback);
                         } else {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 3c4fd5e..83fd7b4 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -129,7 +129,6 @@
 import android.text.method.TimeKeyListener;
 import android.text.method.TransformationMethod;
 import android.text.method.TransformationMethod2;
-import android.text.method.TranslationTransformationMethod;
 import android.text.method.WordIterator;
 import android.text.style.CharacterStyle;
 import android.text.style.ClickableSpan;
@@ -199,7 +198,6 @@
 import android.view.translation.UiTranslationController;
 import android.view.translation.ViewTranslationCallback;
 import android.view.translation.ViewTranslationRequest;
-import android.view.translation.ViewTranslationResponse;
 import android.widget.RemoteViews.RemoteView;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -10832,11 +10830,19 @@
             }
         }
 
+        notifyContentCaptureTextChanged();
+    }
+
+    /**
+     * Notifies the ContentCapture service that the text of the view has changed (only if
+     * ContentCapture has been notified of this view's existence already).
+     *
+     * @hide
+     */
+    public void notifyContentCaptureTextChanged() {
         // TODO(b/121045053): should use a flag / boolean to keep status of SHOWN / HIDDEN instead
         // of using isLaidout(), so it's not called in cases where it's laid out but a
         // notifyAppeared was not sent.
-
-        // ContentCapture
         if (isLaidOut() && isImportantForContentCapture() && getNotifiedContentCaptureAppeared()) {
             final ContentCaptureManager cm = mContext.getSystemService(ContentCaptureManager.class);
             if (cm != null && cm.isContentCaptureEnabled()) {
@@ -13938,33 +13944,4 @@
         }
         requestsCollector.accept(requestBuilder.build());
     }
-
-    /**
-     *
-     * Called when the content from {@link #onCreateViewTranslationRequest} had been translated by
-     * the TranslationService. The default implementation will replace the current
-     * {@link TransformationMethod} to transform the original text to the translated text display.
-     *
-     * @param response a {@link ViewTranslationResponse} that contains the translated information
-     * which can be shown in the view.
-     */
-    @Override
-    public void onViewTranslationResponse(@NonNull ViewTranslationResponse response) {
-        // set ViewTranslationResponse
-        super.onViewTranslationResponse(response);
-        // TODO(b/178353965): move to ViewTranslationCallback.onShow()
-        ViewTranslationCallback callback = getViewTranslationCallback();
-        if (callback instanceof TextViewTranslationCallback) {
-            TextViewTranslationCallback textViewDefaultCallback =
-                    (TextViewTranslationCallback) callback;
-            TranslationTransformationMethod oldTranslationMethod =
-                    textViewDefaultCallback.getTranslationTransformation();
-            TransformationMethod originalTranslationMethod = oldTranslationMethod != null
-                    ? oldTranslationMethod.getOriginalTransformationMethod() : mTransformation;
-            TranslationTransformationMethod newTranslationMethod =
-                    new TranslationTransformationMethod(response, originalTranslationMethod);
-            // TODO(b/178353965): well-handle setTransformationMethod.
-            textViewDefaultCallback.setTranslationTransformation(newTranslationMethod);
-        }
-    }
 }
diff --git a/core/java/android/widget/TextViewTranslationCallback.java b/core/java/android/widget/TextViewTranslationCallback.java
index a7d5ee4..e1b04f8 100644
--- a/core/java/android/widget/TextViewTranslationCallback.java
+++ b/core/java/android/widget/TextViewTranslationCallback.java
@@ -56,26 +56,6 @@
 
     private CharSequence mContentDescription;
 
-    /**
-     * Invoked by the platform when receiving the successful {@link ViewTranslationResponse} for the
-     * view that provides the translatable information by {@link View#createTranslationRequest} and
-     * sent by the platform.
-     */
-    void setTranslationTransformation(TranslationTransformationMethod method) {
-        if (method == null) {
-            if (DEBUG) {
-                Log.w(TAG, "setTranslationTransformation: should not set null "
-                        + "TranslationTransformationMethod");
-            }
-            return;
-        }
-        mTranslationTransformation = method;
-    }
-
-    TranslationTransformationMethod getTranslationTransformation() {
-        return mTranslationTransformation;
-    }
-
     private void clearTranslationTransformation() {
         if (DEBUG) {
             Log.v(TAG, "clearTranslationTransformation: " + mTranslationTransformation);
@@ -88,34 +68,33 @@
      */
     @Override
     public boolean onShowTranslation(@NonNull View view) {
-        if (view.getViewTranslationResponse() == null) {
-            Log.wtf(TAG, "onShowTranslation() shouldn't be called before "
+        ViewTranslationResponse response = view.getViewTranslationResponse();
+        if (response == null) {
+            Log.e(TAG, "onShowTranslation() shouldn't be called before "
                     + "onViewTranslationResponse().");
             return false;
         }
-        if (mTranslationTransformation != null) {
-            final TransformationMethod transformation = mTranslationTransformation;
-            runWithAnimation(
-                    (TextView) view,
-                    () -> {
-                        mIsShowingTranslation = true;
-                        ((TextView) view).setTransformationMethod(transformation);
-                    });
-            ViewTranslationResponse response = view.getViewTranslationResponse();
-            if (response.getKeys().contains(ViewTranslationRequest.ID_CONTENT_DESCRIPTION)) {
-                CharSequence translatedContentDescription =
-                        response.getValue(ViewTranslationRequest.ID_CONTENT_DESCRIPTION).getText();
-                if (!TextUtils.isEmpty(translatedContentDescription)) {
-                    mContentDescription = view.getContentDescription();
-                    view.setContentDescription(translatedContentDescription);
-                }
+        if (mTranslationTransformation == null) {
+            TransformationMethod originalTranslationMethod =
+                    ((TextView) view).getTransformationMethod();
+            mTranslationTransformation = new TranslationTransformationMethod(response,
+                    originalTranslationMethod);
+        }
+        final TransformationMethod transformation = mTranslationTransformation;
+        runWithAnimation(
+                (TextView) view,
+                () -> {
+                    mIsShowingTranslation = true;
+                    // TODO(b/178353965): well-handle setTransformationMethod.
+                    ((TextView) view).setTransformationMethod(transformation);
+                });
+        if (response.getKeys().contains(ViewTranslationRequest.ID_CONTENT_DESCRIPTION)) {
+            CharSequence translatedContentDescription =
+                    response.getValue(ViewTranslationRequest.ID_CONTENT_DESCRIPTION).getText();
+            if (!TextUtils.isEmpty(translatedContentDescription)) {
+                mContentDescription = view.getContentDescription();
+                view.setContentDescription(translatedContentDescription);
             }
-        } else {
-            if (DEBUG) {
-                // TODO(b/182433547): remove before S release
-                Log.w(TAG, "onShowTranslation(): no translated text.");
-            }
-            return false;
         }
         return true;
     }
@@ -126,7 +105,7 @@
     @Override
     public boolean onHideTranslation(@NonNull View view) {
         if (view.getViewTranslationResponse() == null) {
-            Log.wtf(TAG, "onHideTranslation() shouldn't be called before "
+            Log.e(TAG, "onHideTranslation() shouldn't be called before "
                     + "onViewTranslationResponse().");
             return false;
         }
diff --git a/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl b/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl
index ec99c95..d0214e6 100644
--- a/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl
+++ b/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl
@@ -78,4 +78,7 @@
      * @param status The status about the result of requesting update state action.
      */
     void onStatusReported(int status);
+
+    /** Called when the hotword detection process is restarted */
+    void onProcessRestarted();
 }
diff --git a/core/java/com/android/internal/content/om/OverlayConfig.java b/core/java/com/android/internal/content/om/OverlayConfig.java
index b38f623e..3f3c9bd 100644
--- a/core/java/com/android/internal/content/om/OverlayConfig.java
+++ b/core/java/com/android/internal/content/om/OverlayConfig.java
@@ -111,7 +111,7 @@
             // Rebase the system partitions and settings file on the specified root directory.
             partitions = new ArrayList<>(PackagePartitions.getOrderedPartitions(
                     p -> new OverlayPartition(
-                            new File(rootDirectory, p.getFolder().getPath()),
+                            new File(rootDirectory, p.getNonConicalFolder().getPath()),
                             p)));
         }
 
diff --git a/core/java/com/android/internal/infra/ServiceConnector.java b/core/java/com/android/internal/infra/ServiceConnector.java
index af21f81..9ced609 100644
--- a/core/java/com/android/internal/infra/ServiceConnector.java
+++ b/core/java/com/android/internal/infra/ServiceConnector.java
@@ -228,7 +228,7 @@
         private final int mBindingFlags;
         private final @Nullable Function<IBinder, I> mBinderAsInterface;
         private final @NonNull Handler mHandler;
-        private final @NonNull Executor mExecutor;
+        protected final @NonNull Executor mExecutor;
 
         private volatile I mService = null;
         private boolean mBinding = false;
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
index 4f2f973b..dfd561a 100644
--- a/core/java/com/android/internal/os/BatterySipper.java
+++ b/core/java/com/android/internal/os/BatterySipper.java
@@ -23,7 +23,10 @@
 
 /**
  * Contains power usage of an application, system service, or hardware type.
+ *
+ * @deprecated Please use BatteryStatsManager.getBatteryUsageStats instead.
  */
+@Deprecated
 public class BatterySipper implements Comparable<BatterySipper> {
     @UnsupportedAppUsage
     public int userId;
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index b20f50d..608782a 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -59,7 +59,10 @@
  *
  * The caller must initialize this class as soon as activity object is ready to use (for example, in
  * onAttach() for Fragment), call create() in onCreate() and call destroy() in onDestroy().
+ *
+ * @deprecated Please use BatteryStatsManager.getBatteryUsageStats instead.
  */
+@Deprecated
 public class BatteryStatsHelper {
     static final boolean DEBUG = false;
 
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 945a6ab..dab3e9f 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -12141,6 +12141,15 @@
                 }
             }
         }
+
+        void reset() {
+            idleTimeMs = 0;
+            rxTimeMs = 0;
+            txTimeMs = 0;
+            energy = 0;
+            uidRxBytes.clear();
+            uidTxBytes.clear();
+        }
     }
 
     private final BluetoothActivityInfoCache mLastBluetoothActivityInfo
@@ -12167,6 +12176,15 @@
 
         mHasBluetoothReporting = true;
 
+        if (info.getControllerRxTimeMillis() < mLastBluetoothActivityInfo.rxTimeMs
+                || info.getControllerTxTimeMillis() < mLastBluetoothActivityInfo.txTimeMs
+                || info.getControllerIdleTimeMillis() < mLastBluetoothActivityInfo.idleTimeMs
+                || info.getControllerEnergyUsed() < mLastBluetoothActivityInfo.energy) {
+            // A drop in accumulated Bluetooth stats is a sign of a Bluetooth crash.
+            // Reset the preserved previous snapshot in order to restart accumulating deltas.
+            mLastBluetoothActivityInfo.reset();
+        }
+
         final long rxTimeMs =
                 info.getControllerRxTimeMillis() - mLastBluetoothActivityInfo.rxTimeMs;
         final long txTimeMs =
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 1a23cc1..134b158 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.AttachedSurfaceControl;
 import android.view.ContextThemeWrapper;
 import android.view.CrossWindowBlurListeners;
 import android.view.Gravity;
@@ -3148,7 +3148,6 @@
         if (cb == null || isDestroyed()) {
             result = false;
         } else {
-            sendCloseSystemWindows("search");
             int deviceId = event.getDeviceId();
             SearchEvent searchEvent = null;
             if (deviceId != 0) {
diff --git a/core/java/com/android/internal/util/function/DodecConsumer.java b/core/java/com/android/internal/util/function/DodecConsumer.java
new file mode 100644
index 0000000..b4d2fb9
--- /dev/null
+++ b/core/java/com/android/internal/util/function/DodecConsumer.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util.function;
+
+import java.util.function.Consumer;
+
+
+/**
+ * A 12-argument {@link Consumer}
+ *
+ * @hide
+ */
+public interface DodecConsumer<A, B, C, D, E, F, G, H, I, J, K, L> {
+    void accept(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k, L l);
+}
diff --git a/core/java/com/android/internal/util/function/DodecFunction.java b/core/java/com/android/internal/util/function/DodecFunction.java
new file mode 100644
index 0000000..178b2c1
--- /dev/null
+++ b/core/java/com/android/internal/util/function/DodecFunction.java
@@ -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 com.android.internal.util.function;
+
+import java.util.function.Function;
+
+/**
+ * A 12-argument {@link Function}
+ *
+ * @hide
+ */
+public interface DodecFunction<A, B, C, D, E, F, G, H, I, J, K, L, R> {
+    R apply(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k, L l);
+}
diff --git a/core/java/com/android/internal/util/function/DodecPredicate.java b/core/java/com/android/internal/util/function/DodecPredicate.java
new file mode 100644
index 0000000..d3a2b85
--- /dev/null
+++ b/core/java/com/android/internal/util/function/DodecPredicate.java
@@ -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 com.android.internal.util.function;
+
+import java.util.function.Predicate;
+
+/**
+ * A 12-argument {@link Predicate}
+ *
+ * @hide
+ */
+public interface DodecPredicate<A, B, C, D, E, F, G, H, I, J, K, L> {
+    boolean test(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k, L l);
+}
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambda.java b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
index a60cc0f..f073c1c0 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambda.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
@@ -23,6 +23,8 @@
 
 import com.android.internal.util.function.DecConsumer;
 import com.android.internal.util.function.DecFunction;
+import com.android.internal.util.function.DodecConsumer;
+import com.android.internal.util.function.DodecFunction;
 import com.android.internal.util.function.HeptConsumer;
 import com.android.internal.util.function.HeptFunction;
 import com.android.internal.util.function.HexConsumer;
@@ -188,7 +190,7 @@
             A arg1) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -205,7 +207,7 @@
             A arg1) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 1, 0, ReturnType.BOOLEAN, arg1, null, null, null, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -222,7 +224,7 @@
             A arg1) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 1, 0, ReturnType.OBJECT, arg1, null, null, null, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -253,7 +255,7 @@
         synchronized (Message.sPoolSync) {
             PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
                     function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null, null,
-                    null, null, null);
+                    null, null, null, null);
             return Message.obtain().setCallback(callback.recycleOnUse());
         }
     }
@@ -273,7 +275,7 @@
             A arg1, B arg2) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -291,7 +293,7 @@
             A arg1, B arg2) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 2, 0, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -309,7 +311,7 @@
             A arg1, B arg2) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 2, 0, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -327,7 +329,7 @@
             ArgumentPlaceholder<A> arg1, B arg2) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -345,7 +347,7 @@
             ArgumentPlaceholder<A> arg1, B arg2) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -364,7 +366,7 @@
             ArgumentPlaceholder<A> arg1, B arg2, C arg3) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 3, 1, ReturnType.BOOLEAN, arg1, arg2, arg3, null, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -384,7 +386,7 @@
             ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 4, 1, ReturnType.BOOLEAN, arg1, arg2, arg3, arg4, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -405,7 +407,7 @@
             ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4, E arg5) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 5, 1, ReturnType.BOOLEAN, arg1, arg2, arg3, arg4, arg5, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -423,7 +425,7 @@
             ArgumentPlaceholder<A> arg1, B arg2) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -441,7 +443,7 @@
             A arg1, ArgumentPlaceholder<B> arg2) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -459,7 +461,7 @@
             A arg1, ArgumentPlaceholder<B> arg2) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -477,7 +479,7 @@
             A arg1, ArgumentPlaceholder<B> arg2) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -509,7 +511,7 @@
         synchronized (Message.sPoolSync) {
             PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
                     function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
-                    null, null, null);
+                    null, null, null, null);
             return Message.obtain().setCallback(callback.recycleOnUse());
         }
     }
@@ -530,7 +532,7 @@
             A arg1, B arg2, C arg3) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -549,7 +551,7 @@
             A arg1, B arg2, C arg3) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 3, 0, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -568,7 +570,7 @@
             ArgumentPlaceholder<A> arg1, B arg2, C arg3) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -587,7 +589,7 @@
             ArgumentPlaceholder<A> arg1, B arg2, C arg3) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -606,7 +608,7 @@
             A arg1, ArgumentPlaceholder<B> arg2, C arg3) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -625,7 +627,7 @@
             A arg1, ArgumentPlaceholder<B> arg2, C arg3) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -644,7 +646,7 @@
             A arg1, B arg2, ArgumentPlaceholder<C> arg3) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -663,7 +665,7 @@
             A arg1, B arg2, ArgumentPlaceholder<C> arg3) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -696,7 +698,7 @@
         synchronized (Message.sPoolSync) {
             PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
                     function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
-                    null, null, null);
+                    null, null, null, null);
             return Message.obtain().setCallback(callback.recycleOnUse());
         }
     }
@@ -718,7 +720,7 @@
             A arg1, B arg2, C arg3, D arg4) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -738,7 +740,7 @@
             A arg1, B arg2, C arg3, D arg4) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 4, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -758,7 +760,7 @@
             ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -778,7 +780,7 @@
             ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -798,7 +800,7 @@
             A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -818,7 +820,7 @@
             A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -838,7 +840,7 @@
             A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -858,7 +860,7 @@
             A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -878,7 +880,7 @@
             A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -898,7 +900,7 @@
             A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -932,7 +934,7 @@
         synchronized (Message.sPoolSync) {
             PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
                     function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
-                    null, null, null);
+                    null, null, null, null);
             return Message.obtain().setCallback(callback.recycleOnUse());
         }
     }
@@ -955,7 +957,7 @@
             A arg1, B arg2, C arg3, D arg4, E arg5) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -976,7 +978,7 @@
                     function, A arg1, B arg2, C arg3, D arg4, E arg5) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 5, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, null, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -1012,7 +1014,7 @@
         synchronized (Message.sPoolSync) {
             PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
                     function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null, null,
-                    null, null, null);
+                    null, null, null, null);
             return Message.obtain().setCallback(callback.recycleOnUse());
         }
     }
@@ -1036,7 +1038,7 @@
             A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -1058,7 +1060,7 @@
                     ? extends R> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 6, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -1095,7 +1097,7 @@
         synchronized (Message.sPoolSync) {
             PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
                     function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
-                    null, null, null);
+                    null, null, null, null);
             return Message.obtain().setCallback(callback.recycleOnUse());
         }
     }
@@ -1120,7 +1122,7 @@
                     ? super G> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -1144,7 +1146,7 @@
             A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 7, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -1182,7 +1184,7 @@
         synchronized (Message.sPoolSync) {
             PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
                     function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null,
-                    null, null, null);
+                    null, null, null, null);
             return Message.obtain().setCallback(callback.recycleOnUse());
         }
     }
@@ -1209,7 +1211,7 @@
             H arg8) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 8, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -1234,7 +1236,7 @@
             A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 8, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
-                null, null, null);
+                null, null, null, null);
     }
 
     /**
@@ -1274,7 +1276,7 @@
         synchronized (Message.sPoolSync) {
             PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
                     function, 8, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
-                    null, null, null);
+                    null, null, null, null);
             return Message.obtain().setCallback(callback.recycleOnUse());
         }
     }
@@ -1302,7 +1304,7 @@
             E arg5, F arg6, G arg7, H arg8, I arg9) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 9, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
-                arg9, null, null);
+                arg9, null, null, null);
     }
 
     /**
@@ -1328,7 +1330,7 @@
             A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 9, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
-                arg9, null, null);
+                arg9, null, null, null);
     }
 
     /**
@@ -1369,7 +1371,7 @@
         synchronized (Message.sPoolSync) {
             PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
                     function, 9, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
-                    arg9, null, null);
+                    arg9, null, null, null);
             return Message.obtain().setCallback(callback.recycleOnUse());
         }
     }
@@ -1398,7 +1400,7 @@
             D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 10, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
-                arg9, arg10, null);
+                arg9, arg10, null, null);
     }
 
     /**
@@ -1425,7 +1427,7 @@
             A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 10, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
-                arg9, arg10, null);
+                arg9, arg10, null, null);
     }
 
     /**
@@ -1467,7 +1469,7 @@
         synchronized (Message.sPoolSync) {
             PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
                     function, 10, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
-                    arg8, arg9, arg10, null);
+                    arg8, arg9, arg10, null, null);
             return Message.obtain().setCallback(callback.recycleOnUse());
         }
     }
@@ -1498,7 +1500,7 @@
             C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10, K arg11) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 11, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
-                arg9, arg10, arg11);
+                arg9, arg10, arg11, null);
     }
 
     /**
@@ -1528,7 +1530,7 @@
             K arg11) {
         return acquire(PooledLambdaImpl.sPool,
                 function, 11, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
-                arg9, arg10, arg11);
+                arg9, arg10, arg11, null);
     }
 
     /**
@@ -1571,7 +1573,118 @@
         synchronized (Message.sPoolSync) {
             PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
                     function, 11, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
-                    arg8, arg9, arg10, arg11);
+                    arg8, arg9, arg10, arg11, null);
+            return Message.obtain().setCallback(callback.recycleOnUse());
+        }
+    }
+
+    /**
+     * {@link PooledRunnable} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 parameter supplied to {@code function} on call
+     * @param arg4 parameter supplied to {@code function} on call
+     * @param arg5 parameter supplied to {@code function} on call
+     * @param arg6 parameter supplied to {@code function} on call
+     * @param arg7 parameter supplied to {@code function} on call
+     * @param arg8 parameter supplied to {@code function} on call
+     * @param arg9 parameter supplied to {@code function} on call
+     * @param arg10 parameter supplied to {@code function} on call
+     * @param arg11 parameter supplied to {@code function} on call
+     * @param arg12 parameter supplied to {@code function} on call
+     * @return a {@link PooledRunnable}, equivalent to lambda:
+     *         {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10,
+     *         arg11, arg12) }
+     */
+    static <A, B, C, D, E, F, G, H, I, J, K, L> PooledRunnable obtainRunnable(
+            DodecConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+                                ? super G, ? super H, ? super I, ? super J, ? super K,
+                                ? super L> function,
+            A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10,
+            K arg11, L arg12) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 12, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+                arg9, arg10, arg11, arg12);
+    }
+
+    /**
+     * {@link PooledSupplier} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 parameter supplied to {@code function} on call
+     * @param arg4 parameter supplied to {@code function} on call
+     * @param arg5 parameter supplied to {@code function} on call
+     * @param arg6 parameter supplied to {@code function} on call
+     * @param arg7 parameter supplied to {@code function} on call
+     * @param arg8 parameter supplied to {@code function} on call
+     * @param arg9 parameter supplied to {@code function} on call
+     * @param arg10 parameter supplied to {@code function} on call
+     * @param arg11 parameter supplied to {@code function} on call
+     * @param arg12 parameter supplied to {@code function} on call
+     * @return a {@link PooledSupplier}, equivalent to lambda:
+     *         {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10,
+     *         arg11) }
+     */
+    static <A, B, C, D, E, F, G, H, I, J, K, L, R> PooledSupplier<R> obtainSupplier(
+            DodecFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+                                ? super G, ? super H, ? super I, ? super J, ? super K, ? extends L,
+                                ? extends R> function,
+            A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10,
+            K arg11, L arg12) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 11, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+                arg9, arg10, arg11, arg12);
+    }
+
+    /**
+     * Factory of {@link Message}s that contain an
+     * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
+     * {@link Message#getCallback internal callback}.
+     *
+     * The callback is equivalent to one obtainable via
+     * {@link #obtainRunnable(QuintConsumer, Object, Object, Object, Object, Object)}
+     *
+     * Note that using this method with {@link android.os.Handler#handleMessage}
+     * is more efficient than the alternative of {@link android.os.Handler#post}
+     * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points
+     * when obtaining {@link Message} and {@link PooledRunnable} from pools separately
+     *
+     * You may optionally set a {@link Message#what} for the message if you want to be
+     * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise
+     * there's no need to do so
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 parameter supplied to {@code function} on call
+     * @param arg4 parameter supplied to {@code function} on call
+     * @param arg5 parameter supplied to {@code function} on call
+     * @param arg6 parameter supplied to {@code function} on call
+     * @param arg7 parameter supplied to {@code function} on call
+     * @param arg8 parameter supplied to {@code function} on call
+     * @param arg9 parameter supplied to {@code function} on call
+     * @param arg10 parameter supplied to {@code function} on call
+     * @param arg11 parameter supplied to {@code function} on call
+     * @param arg12 parameter supplied to {@code function} on call
+     * @return a {@link Message} invoking {@code function(arg1, arg2, arg3, arg4, arg5, arg6,
+     * arg7, arg8, arg9, arg10, arg11) } when handled
+     */
+    static <A, B, C, D, E, F, G, H, I, J, K, L> Message obtainMessage(
+            DodecConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+                    ? super G, ? super H, ? super I, ? super J, ? super K, ? super L> function,
+            A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10,
+            K arg11, L arg12) {
+        synchronized (Message.sPoolSync) {
+            PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
+                    function, 11, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
+                    arg8, arg9, arg10, arg11, arg12);
             return Message.obtain().setCallback(callback.recycleOnUse());
         }
     }
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
index 1646a07..19f0816 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
@@ -28,6 +28,9 @@
 import com.android.internal.util.function.DecConsumer;
 import com.android.internal.util.function.DecFunction;
 import com.android.internal.util.function.DecPredicate;
+import com.android.internal.util.function.DodecConsumer;
+import com.android.internal.util.function.DodecFunction;
+import com.android.internal.util.function.DodecPredicate;
 import com.android.internal.util.function.HeptConsumer;
 import com.android.internal.util.function.HeptFunction;
 import com.android.internal.util.function.HeptPredicate;
@@ -458,6 +461,28 @@
                     }
                 }
             } break;
+
+            case 12: {
+                switch (returnType) {
+                    case LambdaType.ReturnType.VOID: {
+                        ((DodecConsumer) mFunc).accept(popArg(0), popArg(1),
+                                popArg(2), popArg(3), popArg(4), popArg(5),
+                                popArg(6), popArg(7), popArg(8), popArg(9), popArg(10), popArg(11));
+                        return null;
+                    }
+                    case LambdaType.ReturnType.BOOLEAN: {
+                        return (R) (Object) ((DodecPredicate) mFunc).test(popArg(0),
+                                popArg(1), popArg(2), popArg(3), popArg(4),
+                                popArg(5), popArg(6), popArg(7), popArg(8), popArg(9), popArg(10),
+                                popArg(11));
+                    }
+                    case LambdaType.ReturnType.OBJECT: {
+                        return (R) ((DodecFunction) mFunc).apply(popArg(0), popArg(1),
+                                popArg(2), popArg(3), popArg(4), popArg(5),
+                                popArg(6), popArg(7), popArg(8), popArg(9), popArg(10), popArg(11));
+                    }
+                }
+            } break;
         }
         throw new IllegalStateException("Unknown function type: " + LambdaType.toString(funcType));
     }
@@ -523,7 +548,8 @@
      */
     static <E extends PooledLambda> E acquire(Pool pool, Object func,
             int fNumArgs, int numPlaceholders, int fReturnType, Object a, Object b, Object c,
-            Object d, Object e, Object f, Object g, Object h, Object i, Object j, Object k) {
+            Object d, Object e, Object f, Object g, Object h, Object i, Object j, Object k,
+            Object l) {
         PooledLambdaImpl r = acquire(pool);
         if (DEBUG) {
             Log.i(LOG_TAG,
@@ -543,6 +569,7 @@
                             + ", i = " + i
                             + ", j = " + j
                             + ", k = " + k
+                            + ", l = " + l
                             + ")");
         }
         r.mFunc = Objects.requireNonNull(func);
@@ -560,6 +587,7 @@
         setIfInBounds(r.mArgs, 8, i);
         setIfInBounds(r.mArgs, 9, j);
         setIfInBounds(r.mArgs, 10, k);
+        setIfInBounds(r.mArgs, 11, l);
         return (E) r;
     }
 
diff --git a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
index 8aa2d57..9e09006 100644
--- a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
+++ b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
@@ -198,12 +198,9 @@
         private static final float LIGHT_RADIUS_DP = 800;
         private static final String TAG = "ViewRenderer";
 
-        private HardwareRenderer mRenderer;
-        private RenderNode mCaptureRenderNode;
-        private final RectF mTempRectF = new RectF();
-        private final Rect mSourceRect = new Rect();
+        private final HardwareRenderer mRenderer;
+        private final RenderNode mCaptureRenderNode;
         private final Rect mTempRect = new Rect();
-        private final Matrix mTempMatrix = new Matrix();
         private final int[] mTempLocation = new int[2];
         private long mLastRenderedSourceDrawingId = -1;
         private Surface mSurface;
@@ -313,11 +310,9 @@
         }
 
         private void transformToRoot(View local, Rect localRect, Rect outRect) {
-            mTempMatrix.reset();
-            local.transformMatrixToGlobal(mTempMatrix);
-            mTempRectF.set(localRect);
-            mTempMatrix.mapRect(mTempRectF);
-            mTempRectF.round(outRect);
+            local.getLocationInWindow(mTempLocation);
+            outRect.set(localRect);
+            outRect.offset(mTempLocation[0], mTempLocation[1]);
         }
 
         public void setColorMode(@ColorMode int colorMode) {
diff --git a/core/java/com/android/internal/widget/CachingIconView.java b/core/java/com/android/internal/widget/CachingIconView.java
index 4a70f74..299cbe1 100644
--- a/core/java/com/android/internal/widget/CachingIconView.java
+++ b/core/java/com/android/internal/widget/CachingIconView.java
@@ -257,7 +257,7 @@
         boolean hasColor = color != ColoredIconHelper.COLOR_INVALID;
         if (background == null) {
             // This is the pre-S style -- colored icon with no background.
-            if (hasColor) {
+            if (hasColor && icon != null) {
                 icon.mutate().setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
             }
         } else {
@@ -265,7 +265,9 @@
             // colorize the icon itself with the background color, creating an inverted effect.
             if (hasColor) {
                 background.mutate().setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
-                icon.mutate().setColorFilter(mBackgroundColor, PorterDuff.Mode.SRC_ATOP);
+                if (icon != null) {
+                    icon.mutate().setColorFilter(mBackgroundColor, PorterDuff.Mode.SRC_ATOP);
+                }
             } else {
                 background.mutate().setColorFilter(mBackgroundColor, PorterDuff.Mode.SRC_ATOP);
             }
diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java
index 3d054a5..02ffe8c 100644
--- a/core/java/com/android/internal/widget/EditableInputConnection.java
+++ b/core/java/com/android/internal/widget/EditableInputConnection.java
@@ -99,6 +99,12 @@
     }
 
     @Override
+    public void endComposingRegionEditInternal() {
+        // The ContentCapture service is interested in Composing-state changes.
+        mTextView.notifyContentCaptureTextChanged();
+    }
+
+    @Override
     public void closeConnection() {
         super.closeConnection();
         synchronized(this) {
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 73c5460..3f756d7 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -1524,7 +1524,9 @@
             if (virtualViewId != ExploreByTouchHelper.INVALID_ID) {
                 int row = (virtualViewId - VIRTUAL_BASE_VIEW_ID) / 3;
                 int col = (virtualViewId - VIRTUAL_BASE_VIEW_ID) % 3;
-                return !mPatternDrawLookup[row][col];
+                if (row < 3) {
+                    return !mPatternDrawLookup[row][col];
+                }
             }
             return false;
         }
@@ -1570,7 +1572,6 @@
             final Rect bounds = mTempRect;
             final int row = ordinal / 3;
             final int col = ordinal % 3;
-            final CellState cell = mCellStates[row][col];
             float centerX = getCenterXForColumn(col);
             float centerY = getCenterYForRow(row);
             float cellheight = mSquareHeight * mHitFactor * 0.5f;
diff --git a/core/java/com/android/server/AppWidgetBackupBridge.java b/core/java/com/android/server/AppWidgetBackupBridge.java
index 7d82d35..8e834a8 100644
--- a/core/java/com/android/server/AppWidgetBackupBridge.java
+++ b/core/java/com/android/server/AppWidgetBackupBridge.java
@@ -47,9 +47,9 @@
                 : null;
     }
 
-    public static void restoreStarting(int userId) {
+    public static void systemRestoreStarting(int userId) {
         if (sAppWidgetService != null) {
-            sAppWidgetService.restoreStarting(userId);
+            sAppWidgetService.systemRestoreStarting(userId);
         }
     }
 
@@ -59,9 +59,9 @@
         }
     }
 
-    public static void restoreFinished(int userId) {
+    public static void systemRestoreFinished(int userId) {
         if (sAppWidgetService != null) {
-            sAppWidgetService.restoreFinished(userId);
+            sAppWidgetService.systemRestoreFinished(userId);
         }
     }
 }
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index bd0de29..a8dcbaf 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -86,6 +86,7 @@
     // and "allow-ignore-location-settings".
     private static final int ALLOW_OVERRIDE_APP_RESTRICTIONS = 0x100;
     private static final int ALLOW_IMPLICIT_BROADCASTS = 0x200;
+    private static final int ALLOW_VENDOR_APEX = 0x400;
     private static final int ALLOW_ALL = ~0;
 
     // property for runtime configuration differentiation
@@ -240,6 +241,7 @@
 
     private final ArraySet<String> mRollbackWhitelistedPackages = new ArraySet<>();
     private final ArraySet<String> mWhitelistedStagedInstallers = new ArraySet<>();
+    private final ArraySet<String> mAllowedVendorApexes = new ArraySet<>();
 
     /**
      * Map of system pre-defined, uniquely named actors; keys are namespace,
@@ -410,6 +412,10 @@
         return mWhitelistedStagedInstallers;
     }
 
+    public Set<String> getAllowedVendorApexes() {
+        return mAllowedVendorApexes;
+    }
+
     public ArraySet<String> getAppDataIsolationWhitelistedApps() {
         return mAppDataIsolationWhitelistedApps;
     }
@@ -484,7 +490,7 @@
 
         // Vendors are only allowed to customize these
         int vendorPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PRIVAPP_PERMISSIONS
-                | ALLOW_ASSOCIATIONS;
+                | ALLOW_ASSOCIATIONS | ALLOW_VENDOR_APEX;
         if (Build.VERSION.DEVICE_INITIAL_SDK_INT <= Build.VERSION_CODES.O_MR1) {
             // For backward compatibility
             vendorPermissionFlag |= (ALLOW_PERMISSIONS | ALLOW_APP_CONFIGS);
@@ -525,7 +531,8 @@
         }
 
         // Allow OEM to customize these
-        int oemPermissionFlag = ALLOW_FEATURES | ALLOW_OEM_PERMISSIONS | ALLOW_ASSOCIATIONS;
+        int oemPermissionFlag = ALLOW_FEATURES | ALLOW_OEM_PERMISSIONS | ALLOW_ASSOCIATIONS
+                | ALLOW_VENDOR_APEX;
         readPermissions(Environment.buildPath(
                 Environment.getOemDirectory(), "etc", "sysconfig"), oemPermissionFlag);
         readPermissions(Environment.buildPath(
@@ -536,7 +543,8 @@
         // the use of hidden APIs from the product partition.
         int productPermissionFlag = ALLOW_FEATURES | ALLOW_LIBS | ALLOW_PERMISSIONS
                 | ALLOW_APP_CONFIGS | ALLOW_PRIVAPP_PERMISSIONS | ALLOW_HIDDENAPI_WHITELISTING
-                | ALLOW_ASSOCIATIONS | ALLOW_OVERRIDE_APP_RESTRICTIONS | ALLOW_IMPLICIT_BROADCASTS;
+                | ALLOW_ASSOCIATIONS | ALLOW_OVERRIDE_APP_RESTRICTIONS | ALLOW_IMPLICIT_BROADCASTS
+                | ALLOW_VENDOR_APEX;
         if (Build.VERSION.DEVICE_INITIAL_SDK_INT <= Build.VERSION_CODES.R) {
             // TODO(b/157393157): This must check product interface enforcement instead of
             // DEVICE_INITIAL_SDK_INT for the devices without product interface enforcement.
@@ -663,6 +671,7 @@
                     (permissionFlag & ALLOW_OVERRIDE_APP_RESTRICTIONS) != 0;
             final boolean allowImplicitBroadcasts = (permissionFlag & ALLOW_IMPLICIT_BROADCASTS)
                     != 0;
+            final boolean allowVendorApex = (permissionFlag & ALLOW_VENDOR_APEX) != 0;
             while (true) {
                 XmlUtils.nextElement(parser);
                 if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
@@ -1212,6 +1221,20 @@
                         }
                         XmlUtils.skipCurrentTag(parser);
                     } break;
+                    case "allowed-vendor-apex": {
+                        if (allowVendorApex) {
+                            String pkgName = parser.getAttributeValue(null, "package");
+                            if (pkgName == null) {
+                                Slog.w(TAG, "<" + name + "> without package in " + permFile
+                                        + " at " + parser.getPositionDescription());
+                            } else {
+                                mAllowedVendorApexes.add(pkgName);
+                            }
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
                     default: {
                         Slog.w(TAG, "Tag " + name + " is unknown in "
                                 + permFile + " at " + parser.getPositionDescription());
diff --git a/core/java/com/android/server/WidgetBackupProvider.java b/core/java/com/android/server/WidgetBackupProvider.java
index a2efbdd..5453c4d 100644
--- a/core/java/com/android/server/WidgetBackupProvider.java
+++ b/core/java/com/android/server/WidgetBackupProvider.java
@@ -28,7 +28,7 @@
 public interface WidgetBackupProvider {
     public List<String> getWidgetParticipants(int userId);
     public byte[] getWidgetState(String packageName, int userId);
-    public void restoreStarting(int userId);
+    public void systemRestoreStarting(int userId);
     public void restoreWidgetState(String packageName, byte[] restoredState, int userId);
-    public void restoreFinished(int userId);
+    public void systemRestoreFinished(int userId);
 }
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index d528428..e9e79dc3 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1775,6 +1775,16 @@
     return static_cast<jint>(SurfaceComposerClient::getGPUContextPriority());
 }
 
+static void nativeSetTransformHint(JNIEnv* env, jclass clazz, jlong nativeSurfaceControl,
+                                   jint transformHint) {
+    sp<SurfaceControl> surface(reinterpret_cast<SurfaceControl*>(nativeSurfaceControl));
+    if (surface == nullptr) {
+        return;
+    }
+    surface->setTransformHint(
+            ui::Transform::toRotationFlags(static_cast<ui::Rotation>(transformHint)));
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod sSurfaceControlMethods[] = {
@@ -1962,6 +1972,8 @@
             (void*)nativeCreateJankDataListenerWrapper },
     {"nativeGetGPUContextPriority", "()I",
             (void*)nativeGetGPUContextPriority },
+    {"nativeSetTransformHint", "(JI)V",
+            (void*)nativeSetTransformHint },
         // clang-format on
 };
 
diff --git a/core/proto/android/server/apphibernationservice.proto b/core/proto/android/server/apphibernationservice.proto
index d341c4b..64c2a32 100644
--- a/core/proto/android/server/apphibernationservice.proto
+++ b/core/proto/android/server/apphibernationservice.proto
@@ -39,4 +39,5 @@
 message GlobalLevelHibernationStateProto {
   optional string package_name = 1;
   optional bool hibernated = 2;
+  optional int64 saved_byte = 3;
 }
\ No newline at end of file
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 803ba64..cd89318 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Geen vingerafdrukke is geregistreer nie."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Hierdie toetstel het nie \'n vingerafdruksensor nie."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor is tydelik gedeaktiveer."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Vinger <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Gebruik vingerafdruk"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Gebruik vingerafdruk of skermslot"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index e5ef55a..b261c46 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ምንም የጣት አሻራዎች አልተመዘገቡም።"</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ይህ መሣሪያ የጣት አሻራ ዳሳሽ የለውም።"</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ዳሳሽ ለጊዜው ተሰናክሏል።"</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"ጣት <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"የጣት አሻራ ይጠቀሙ"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"የጣት አሻራ ወይም የማያ ገጽ መቆለፊያ ይጠቀሙ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 86a09ee..b38bf9f 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -613,6 +613,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ليست هناك بصمات إصبع مسجَّلة."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"لا يحتوي هذا الجهاز على مستشعِر بصمات إصبع."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"تم إيقاف جهاز الاستشعار مؤقتًا."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"الإصبع <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"استخدام بصمة الإصبع"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"استخدام بصمة الإصبع أو قفل الشاشة"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 5b2149e..846fb2c 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"কোনো ফিংগাৰপ্ৰিণ্ট যোগ কৰা নহ\'ল।"</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"এই ডিভাইচটোত ফিংগাৰপ্ৰিণ্ট ছেন্সৰ নাই।"</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ছেন্সৰটো সাময়িকভাৱে অক্ষম হৈ আছে।"</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g> আঙুলি"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ফিংগাৰপ্ৰিণ্ট ব্যৱহাৰ কৰক"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ফিংগাৰপ্ৰিণ্ট অথবা স্ক্ৰীন লক ব্যৱহাৰ কৰক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 54dc932..6b4736f 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Barmaq izi qeydə alınmayıb."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu cihazda barmaq izi sensoru yoxdur."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor müvəqqəti deaktivdir."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Barmaq <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Barmaq izini istifadə edin"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Barmaq izi və ya ekran kilidindən istifadə edin"</string>
@@ -1624,7 +1626,7 @@
     <string name="wireless_display_route_description" msgid="8297563323032966831">"Simsiz ekran"</string>
     <string name="media_route_button_content_description" msgid="2299223698196869956">"İştirakçılar"</string>
     <string name="media_route_chooser_title" msgid="6646594924991269208">"Cihaza qoş"</string>
-    <string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"Ekranı cihaza yayımla"</string>
+    <string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"Ekran yayımı"</string>
     <string name="media_route_chooser_searching" msgid="6119673534251329535">"Cihazlar axtarılır..."</string>
     <string name="media_route_chooser_extended_settings" msgid="2506352159381327741">"Ayarlar"</string>
     <string name="media_route_controller_disconnect" msgid="7362617572732576959">"Əlaqəni kəsin"</string>
@@ -1695,15 +1697,15 @@
     <string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"DEAKTİV"</string>
     <string name="accessibility_enable_service_title" msgid="3931558336268541484">"<xliff:g id="SERVICE">%1$s</xliff:g> xidmətinin cihaza tam nəzarət etməsinə icazə verilsin?"</string>
     <string name="accessibility_enable_service_encryption_warning" msgid="8603532708618236909">"<xliff:g id="SERVICE">%1$s</xliff:g> aktiv olarsa, cihazınız data şifrələnməsini genişləndirmək üçün ekran kilidini istifadə etməyəcək."</string>
-    <string name="accessibility_service_warning_description" msgid="291674995220940133">"Tam nəzarət əlçatımlılıq ehtiyaclarınızı ödəyən bəzi tətbiqlər üçün uyğundur."</string>
+    <string name="accessibility_service_warning_description" msgid="291674995220940133">"Tam nəzarət icazəsi xüsusi imkanlara dair yardım edən tətbiqlərə lazımdır, digər tətbiqlərə lazım deyil."</string>
     <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Baxış və nəzarət ekranı"</string>
     <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Ekrandakı bütün kontenti oxuya və kontenti digər tətbiqlərin üzərində göstərə bilər."</string>
     <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Əməliyyatlara baxın və icra edin"</string>
-    <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"O, tətbiq və ya avadanlıq sensoru ilə interaktivliyinizi izləyir və əvəzinizdən tətbiqlərlə qarşılıqlı əlaqəyə girir."</string>
+    <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Tətbiq və sensorlarla əlaqələrinizi izləyib tətbiqlərə adınızdan əmrlər verə bilər."</string>
     <string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"İcazə verin"</string>
     <string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"İmtina edin"</string>
     <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Funksiyanı istifadə etmək üçün onun üzərinə toxunun:"</string>
-    <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Əlçatımlılıq düyməsi ilə istifadə edəcəyiniz funksiyaları seçin"</string>
+    <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Xüsusi imkanlar düyməsinin köməyilə işə salınacaq funksiyaları seçin"</string>
     <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Səs səviyyəsi düyməsinin qısayolu ilə istifadə edəcəyiniz funksiyaları seçin"</string>
     <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> deaktiv edilib"</string>
     <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Qısayolları redaktə edin"</string>
@@ -1720,7 +1722,7 @@
     <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Xüsusi imkanlar düyməsinə toxunanda istədiyiniz funksiyanı seçin:"</string>
     <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Əlçatımlılıq jesti (iki barmağınızla ekranın aşağısından yuxarı doğru sürüşdürün) ilə istifadə edəcəyiniz funksiyanı seçin:"</string>
     <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Əlçatımlılıq jesti (üç barmağınızla ekranın aşağısından yuxarı doğru sürüşdürün) ilə istifadə edəcəyiniz funksiyanı seçin:"</string>
-    <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Funksiyalar arasında keçid etmək üçün əlçatımlılıq düyməsinə toxunub saxlayın."</string>
+    <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Funksiyalar arasında keçid etmək üçün xüsusi imkanlar düyməsini basıb saxlayın."</string>
     <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Funksiyalar arasında keçid etmək üçün iki barmağınızla yuxarı sürüşdürüb saxlayın."</string>
     <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Funksiyalar arasında keçid etmək üçün üç barmağınızla yuxarı doğru sürüşdürüb saxlayın."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Böyütmə"</string>
@@ -2137,7 +2139,7 @@
     <string name="accessibility_system_action_quick_settings_label" msgid="4583900123506773783">"Sürətli Ayarlar"</string>
     <string name="accessibility_system_action_power_dialog_label" msgid="8095341821683910781">"Yandırıb-söndürmə dialoqu"</string>
     <string name="accessibility_system_action_lock_screen_label" msgid="5484190691945563838">"Kilid Ekranı"</string>
-    <string name="accessibility_system_action_screenshot_label" msgid="3581566515062741676">"Ekran şəkli"</string>
+    <string name="accessibility_system_action_screenshot_label" msgid="3581566515062741676">"Skrinşot"</string>
     <string name="accessibility_system_action_on_screen_a11y_shortcut_label" msgid="8488701469459210309">"Ekranda Əlçatımlılıq Qısayolu"</string>
     <string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Ekranda Əlçatımlılıq Qısayolu Seçicisi"</string>
     <string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Əlçatımlılıq Qısayolu"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index ff2289f..5fcd2df 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -604,6 +604,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nije registrovan nijedan otisak prsta."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor za otisak prsta."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je privremeno onemogućen."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Koristite otisak prsta"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Koristite otisak prsta ili zaključavanje ekrana"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index b6ba47a..1af3ddd 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -607,6 +607,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Адбіткі пальцаў не зарэгістраваны."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"На гэтай прыладзе няма сканера адбіткаў пальцаў."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Датчык часова выключаны."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Палец <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Выкарыстоўваць адбітак пальца"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Выкарыстоўваць адбітак пальца ці блакіроўку экрана"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 9ab5dfe..b3722ab 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Няма регистрирани отпечатъци."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Това устройство няма сензор за отпечатъци."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сензорът е временно деактивиран."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Пръст <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Използване на отпечатък"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Използване на отпечатък или опцията за заключване на екрана"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 2a9ecf6..69ddbb4 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"কোনও আঙ্গুলের ছাপ নথিভুক্ত করা হয়নি।"</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"এই ডিভাইসে আঙ্গুলের ছাপ নেওয়ার সেন্সর নেই।"</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"সেন্সর অস্থায়ীভাবে বন্ধ করা আছে।"</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"আঙ্গুল <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"আঙ্গুলের ছাপ ব্যবহার করুন"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"আঙ্গুলের ছাপ অথবা স্ক্রিন লক ব্যবহার করুন"</string>
@@ -1458,10 +1460,8 @@
     <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"একটি অ্যাপ্লিকেশানকে প্যাকেজগুলি মুছে দেওয়ার অনুরোধ জানাতে দেয়৷"</string>
     <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ব্যাটারি অপ্টিমাইজেশন উপেক্ষা করার জন্য অনুমতি চাওয়া"</string>
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"কোনো অ্যাপের জন্য ব্যাটারি অপ্টিমাইজেশন উপেক্ষা করতে সেটিকে অনুমতির চাওয়ার মঞ্জুরি দেয়৷"</string>
-    <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
-    <skip />
-    <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
-    <skip />
+    <string name="permlab_queryAllPackages" msgid="2928450604653281650">"অন্যান্য সব প্যাকেজের তথ্য সম্পর্কিত কোয়েরি দেখুন"</string>
+    <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"এটি কোনও অ্যাপকে সর্বদা ইনস্টল করা সব প্যাকেজ দেখতে অনুমতি দেয়।"</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"জুম নিয়ন্ত্রণের জন্য দুবার ট্যাপ করুন"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"উইজেট যোগ করা যায়নি৷"</string>
     <string name="ime_action_go" msgid="5536744546326495436">"যান"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 2d7003b..74da24a 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -604,6 +604,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nije prijavljen nijedan otisak prsta."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor za otisak prsta."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je privremeno onemogućen."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Koristi otisak prsta"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Koristi otisak prsta ili zaključavanje ekrana"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 330713d..03e7ef2 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No s\'ha registrat cap empremta digital."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Aquest dispositiu no té sensor d\'empremtes digitals."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"El sensor està desactivat temporalment."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Dit <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utilitza l\'empremta digital"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Utilitza l\'empremta digital o el bloqueig de pantalla"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index c4eb66e..bfd5634 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -607,6 +607,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nejsou zaregistrovány žádné otisky prstů."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Toto zařízení nemá snímač otisků prstů."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je dočasně deaktivován."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Použít otisk prstu"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Použít otisk prstu nebo zámek obrazovky"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 2708487..4231048 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Der er ikke registreret nogen fingeraftryk."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Denne enhed har ingen fingeraftrykslæser."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensoren er midlertidigt deaktiveret."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Fingeraftryk <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Brug fingeraftryk"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Brug fingeraftryk eller skærmlås"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 03b9904..7a75eea 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Keine Fingerabdrücke erfasst."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dieses Gerät hat keinen Fingerabdrucksensor."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Der Sensor ist vorübergehend deaktiviert."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Fingerabdruck verwenden"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Fingerabdruck oder Displaysperre verwenden"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index fce9d98..523cde1 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Δεν έχουν καταχωριστεί δακτυλικά αποτυπώματα."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Αυτή η συσκευή δεν διαθέτει αισθητήρα δακτυλικού αποτυπώματος."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Ο αισθητήρας απενεργοποιήθηκε προσωρινά."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Δάχτυλο <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Χρήση δακτυλικού αποτυπώματος"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Χρήση δακτυλικού αποτυπώματος ή κλειδώματος οθόνης"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 805f344..b2eaab0 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Use fingerprint or screen lock"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 86ddb1f..0a257f4 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Use fingerprint or screen lock"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index da1c49a..5eca8a68 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Use fingerprint or screen lock"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index eeedf21..74d32d4 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Use fingerprint or screen lock"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 5a2d3f3..385608f 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -601,6 +601,7 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‏‎‎‎‎‏‏‏‎‏‏‎‏‏‎‏‎‎‎‎‎‏‎‏‎‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‎‏‎‏‏‎‎‏‏‎‏‏‎No fingerprints enrolled.‎‏‎‎‏‎"</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‎‎‎‏‎‎‎‏‎‏‏‎‏‏‏‏‎‎‎‏‎‎‎‏‏‏‏‎‏‎‎‏‏‎‎‏‎‏‎‎‎‎‎‎‎‎‏‏‎‎‏‎‏‏‎‎This device does not have a fingerprint sensor.‎‏‎‎‏‎"</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‏‏‏‎‎‎‏‏‎‎‏‎‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎‏‏‎‎‎‏‎‎‎‏‏‏‎‎‎‎‏‎‏‏‏‎‎‎‏‎Sensor temporarily disabled.‎‏‎‎‏‎"</string>
+    <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‎‎‏‏‎‎‏‎‎‎‏‎‏‎‎‎‏‎‏‏‏‎‎‏‎‎‏‏‏‏‎‎‏‏‎‏‎‎‎‎‏‎‎‎‎‎‏‎‎‏‏‏‏‏‏‏‎Sensor needs calibration‎‏‎‎‏‎"</string>
     <string name="fingerprint_name_template" msgid="8941662088160289778">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‏‏‎‎‏‎‏‎‏‎‎‏‎‎‏‎‏‏‎‏‎‏‎‎‏‎‎‏‎‏‏‏‏‏‎‎‏‎‎‎‏‏‏‏‏‏‎‎‏‎‎Finger ‎‏‎‎‏‏‎<xliff:g id="FINGERID">%d</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‎‎‎‎‏‎‎‎‎‏‏‎‏‎‏‏‎‏‎‏‏‎‎‎‏‏‎‎‎‏‏‏‎‎‏‏‏‏‏‎‏‎‎‏‏‎‎‎‏‎‏‎‎‏‎‎‎Use fingerprint‎‏‎‎‏‎"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‏‏‎‎‎‏‎‏‏‎‎‎‏‎‎‏‎‏‏‏‎‎‏‏‏‏‏‎‎‎‎‎‎‎‎‎‏‎‏‏‏‏‏‎‎‏‏‏‎‏‎‏‏‎Use fingerprint or screen lock‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index d3614f1..fafb766 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No se registraron huellas digitales."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo no tiene sensor de huellas dactilares."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Se inhabilitó temporalmente el sensor."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar huella digital"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Usar bloqueo de huella dactilar o pantalla"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 7257159..3468da5 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No se ha registrado ninguna huella digital."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo no tiene sensor de huellas digitales."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"El sensor está inhabilitado en estos momentos."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar huella digital"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Usar huella digital o bloqueo de pantalla"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 1ae8730..0a60d66 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ühtegi sõrmejälge pole registreeritud."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Selles seadmes pole sõrmejäljeandurit."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Andur on ajutiselt keelatud."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Sõrmejälg <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Sõrmejälje kasutamine"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Sõrmejälje või ekraaniluku kasutamine"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index de6e5e7..e36cc32 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ez da erregistratu hatz-markarik."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Gailu honek ez du hatz-marken sentsorerik."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sentsorea aldi baterako desgaitu da."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. hatza"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Erabili hatz-marka"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Erabili hatz-marka edo pantailaren blokeoa"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index cc2223c..add623a 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"اثر انگشتی ثبت نشده است."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"این دستگاه حسگر اثر انگشت ندارد."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"حسگر به‌طور موقت غیرفعال است."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"انگشت <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"استفاده از اثر انگشت"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"استفاده از اثر انگشت یا قفل صفحه"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 1b28ca1..5b4caa7 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Sormenjälkiä ei ole otettu käyttöön."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Laitteessa ei ole sormenjälkitunnistinta."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Tunnistin poistettu väliaikaisesti käytöstä."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Sormi <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Käytä sormenjälkeä"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Käytä sormenjälkeä tai näytön lukitusta"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index fa373784..29c9698 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Aucune empreinte digitale enregistrée."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Cet appareil ne possède pas de capteur d\'empreintes digitales."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Le capteur a été désactivé temporairement."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Doigt <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utiliser l\'empreinte digitale"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Utiliser l\'empreinte digitale ou le verrouillage de l\'écran"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 0ee3c88..23e599e 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Aucune empreinte digitale enregistrée."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Aucun lecteur d\'empreinte digitale n\'est installé sur cet appareil."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Capteur temporairement désactivé."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Doigt <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utiliser l\'empreinte digitale"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Utiliser votre empreinte digitale ou le verrouillage de l\'écran"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 5e10fdb..70f703a 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Non se rexistraron impresións dixitais."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo non ten sensor de impresión dixital."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Desactivouse o sensor temporalmente."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utilizar impresión dixital"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Utilizar impresión dixital ou credencial do dispositivo"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 4157e1f..4d27b10 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"કોઈ ફિંગરપ્રિન્ટની નોંધણી કરવામાં આવી નથી."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"આ ડિવાઇસમાં કોઈ ફિંગરપ્રિન્ટ સેન્સર નથી."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"સેન્સર હંગામી રૂપે બંધ કર્યું છે."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"આંગળી <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ફિંગરપ્રિન્ટનો ઉપયોગ કરો"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ફિંગરપ્રિન્ટ અથવા સ્ક્રીન લૉકનો ઉપયોગ કરો"</string>
@@ -1458,10 +1460,8 @@
     <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"ઍપ્લિકેશનને પૅકેજો કાઢી નાખવાની વિનંતી કરવાની મંજૂરી આપે છે."</string>
     <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"બૅટરી ઓપ્ટિમાઇઝેશન્સને અવગણવા માટે પૂછો"</string>
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ઍપ્લિકેશનને તે ઍપ્લિકેશન માટે બૅટરી ઓપ્ટિમાઇઝેશન્સને અવગણવાની પરવાનગી આપવા માટે પૂછવાની મંજૂરી આપે છે."</string>
-    <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
-    <skip />
-    <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
-    <skip />
+    <string name="permlab_queryAllPackages" msgid="2928450604653281650">"બધા પૅકેજ જુઓ"</string>
+    <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"કોઈ ઍપને ઇન્સ્ટૉલ કરેલા બધા પૅકેજ જોવાની મંજૂરી આપે છે."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ઝૂમ નિયંત્રણ માટે બેવાર ટૅપ કરો"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"વિજેટ ઉમેરી શકાયું નથી."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"જાઓ"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 8fe4ebf..74f4f46 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"कोई फ़िंगरप्रिंट रजिस्टर नहीं किया गया है."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"इस डिवाइस में फ़िंगरप्रिंट सेंसर नहीं है."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"सेंसर कुछ समय के लिए बंद कर दिया गया है."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"फ़िंगरप्रिंट <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"फ़िंगरप्रिंट इस्तेमाल करें"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"फ़िंगरप्रिंट या स्क्रीन लॉक का क्रेडेंशियल इस्तेमाल करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 630ba2b..13a7c45 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -604,6 +604,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nije registriran nijedan otisak prsta."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor otiska prsta."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je privremeno onemogućen."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Upotreba otiska prsta"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Upotreba otiska prsta ili zaključavanja zaslona"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index f945d39..a478e67 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nincsenek regisztrált ujjlenyomatok."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ez az eszköz nem rendelkezik ujjlenyomat-érzékelővel."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Az érzékelő átmenetileg le van tiltva."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. ujj"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Ujjlenyomat használata"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"A folytatás ujjlenyomattal vagy képernyőzárral lehetséges"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index d3057ca..e42eccb 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Գրանցված մատնահետք չկա:"</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Այս սարքը չունի մատնահետքերի սկաներ։"</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Տվիչը ժամանակավորապես անջատված է:"</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Մատնահետք <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Օգտագործել մատնահետք"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Օգտագործել մատնահետք կամ էկրանի կողպում"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index abe3608..f50c470 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Tidak ada sidik jari yang terdaftar."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Perangkat ini tidak memiliki sensor sidik jari."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor dinonaktifkan untuk sementara."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Jari <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Gunakan sidik jari"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Gunakan sidik jari atau kunci layar"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 6be07b0..bfc7476 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Engin fingraför hafa verið skráð."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Þetta tæki er ekki með fingrafaralesara."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Slökkt tímabundið á skynjara."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Fingur <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Nota fingrafar"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Nota fingrafar eða skjálás"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index d4f2fdb..708841d 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nessuna impronta digitale registrata."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Questo dispositivo non dispone di sensore di impronte."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensore temporaneamente disattivato."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Dito <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usa l\'impronta"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Usa l\'impronta o il blocco schermo"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 7c9d97e..fabffbf 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -607,6 +607,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"לא נסרקו טביעות אצבע."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"במכשיר הזה אין חיישן טביעות אצבע."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"החיישן מושבת באופן זמני."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"אצבע <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"שימוש בטביעת אצבע"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"שימוש בטביעת אצבע או בנעילת מסך"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 3968187..e275df03 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"指紋が登録されていません。"</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"このデバイスには指紋認証センサーがありません。"</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"センサーが一時的に無効になっています。"</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"指紋 <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"指紋の使用"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"指紋または画面ロックの使用"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index e941b5e..bd85e61 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"თითის ანაბეჭდები რეგისტრირებული არ არის."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ამ მოწყობილობას არ აქვს თითის ანაბეჭდის სენსორი."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"სენსორი დროებით გათიშულია."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"თითი <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"გამოიყენეთ თითის ანაბეჭდი"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"გამოიყენეთ თითის ანაბეჭდი ან ეკრანის დაბლოკვა"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 1324d2d..16da28f 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Саусақ іздері тіркелмеген."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Бұл құрылғыда саусақ ізін оқу сканері жоқ."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Датчик уақытша өшірулі."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>-саусақ"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Саусақ ізін пайдалану"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Саусақ ізін немесе экран құлпын пайдалану"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index df0b082..e72db13 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"មិន​មាន​ការ​ចុះឈ្មោះស្នាម​ម្រាមដៃទេ។"</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ឧបករណ៍នេះ​មិនមាន​ឧបករណ៍ចាប់​ស្នាមម្រាមដៃទេ។"</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"បានបិទ​ឧបករណ៍​ចាប់សញ្ញាជា​បណ្តោះអាសន្ន។"</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"ម្រាមដៃ <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ប្រើស្នាមម្រាមដៃ"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ប្រើស្នាមម្រាមដៃ ឬ​ការចាក់សោអេក្រង់"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index a8e5b640..8858d0b 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ಯಾವುದೇ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್‌ ಅನ್ನು ನೋಂದಣಿ ಮಾಡಿಲ್ಲ."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ಈ ಸಾಧನವು ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್‌‌ ಅನ್ನು ಹೊಂದಿಲ್ಲ."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ಸೆನ್ಸಾರ್ ಅನ್ನು ತಾತ್ಕಾಲಿಕವಾಗಿ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"ಫಿಂಗರ್ <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ಫಿಂಗರ್ ಪ್ರಿಂಟ್ ಬಳಸಿ"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ಫಿಂಗರ್‌ ಪ್ರಿಂಟ್ ಅಥವಾ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಬಳಸಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index cf9ade7..e9d0fc5 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"등록된 지문이 없습니다."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"기기에 지문 센서가 없습니다."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"센서가 일시적으로 사용 중지되었습니다."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"손가락 <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"지문 사용"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"지문 또는 화면 잠금 사용"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 5479f11..c1961db 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Бир да манжа изи катталган эмес."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Бул түзмөктө манжа изинин сенсору жок."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сенсор убактылуу өчүрүлгөн."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>-манжа"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Манжа изин колдонуу"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Манжа изин же экрандын кулпусун колдонуу"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index ee2f058..65643c3 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ບໍ່ມີການລົງທະບຽນລາຍນິ້ວມື."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ອຸປະກອນນີ້ບໍ່ມີເຊັນເຊີລາຍນິ້ວມື."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ປິດການເຮັດວຽກຂອງເຊັນເຊີໄວ້ຊົ່ວຄາວແລ້ວ."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"ນີ້ວ​ມື <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ໃຊ້ລາຍນິ້ວມື"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ໃຊ້ລາຍນິ້ວມື ຫຼື ການລັອກໜ້າຈໍ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 8ebb09e..9849124 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -607,6 +607,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Neužregistruota jokių kontrolinių kodų."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Šiame įrenginyje nėra kontrolinio kodo jutiklio."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Jutiklis laikinai išjungtas."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g> pirštas"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Naudoti kontrolinį kodą"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Naudoti kontrolinį kodą arba ekrano užraktą"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 9b86c4d..8407622 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -604,6 +604,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nav reģistrēts neviens pirksta nospiedums."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Šajā ierīcē nav pirksta nospieduma sensora."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensors ir īslaicīgi atspējots."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. pirksts"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Pirksta nospieduma izmantošana"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Pirksta nospieduma vai ekrāna bloķēšanas metodes izmantošana"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index d0e11f1..42a55c2 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Не се запишани отпечатоци."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Уредов нема сензор за отпечатоци."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сензорот е привремено оневозможен."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Прст <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Користи отпечаток"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Користи отпечаток или заклучување екран"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 22fac7e..f4a79e3 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"വിരലടയാളങ്ങൾ എൻറോൾ ചെയ്തിട്ടില്ല."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ഈ ഉപകരണത്തിൽ ഫിംഗർപ്രിന്റ് സെൻസറില്ല."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"സെൻസർ താൽക്കാലികമായി പ്രവർത്തനരഹിതമാക്കി."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"ഫിംഗർ <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ഫിംഗർപ്രിന്റ് ഉപയോഗിക്കുക"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ഫിംഗർപ്രിന്റ് അല്ലെങ്കിൽ സ്‌ക്രീൻ ലോക്ക് ഉപയോഗിക്കുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 1060f64..7d0a643 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Бүртгүүлсэн хурууны хээ алга."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Энэ төхөөрөмжид хурууны хээ мэдрэгч алга."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Мэдрэгчийг түр хугацаанд идэвхгүй болгосон."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Хурууны хээ <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Хурууны хээ ашиглах"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Хурууны хээ эсвэл дэлгэцийн түгжээ ашиглах"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index bf5f8e7..3933bf3 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"कोणत्याही फिंगरप्रिंटची नोंद झाली नाही"</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"या डिव्हाइसमध्ये फिंगरप्रिंट सेन्सर नाही."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"सेन्सर तात्पुरता बंद केला आहे."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g> बोट"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"फिंगरप्रिंट वापरा"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"फिंगरप्रिंट किंवा स्क्रीन लॉक वापरा"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index ccc0828..2ad6baf 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Tiada cap jari didaftarkan."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Peranti ini tiada penderia cap jari."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Penderia dilumpuhkan sementara."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Jari <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Gunakan cap jari"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Gunakan cap jari atau kunci skrin"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 7797e08..3543be0f 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"မည်သည့် လက်ဗွေကိုမျှ ထည့်သွင်းမထားပါ။"</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ဤစက်တွင် လက်ဗွေအာရုံခံကိရိယာ မရှိပါ။"</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"အာရုံခံကိရိယာကို ယာယီပိတ်ထားသည်။"</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"လက်ချောင်း <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"လက်ဗွေ သုံးခြင်း"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"လက်ဗွေ (သို့) ဖန်သားပြင်လော့ခ်ချခြင်းကို သုံးခြင်း"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 395dc1d..9ed34e0 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ingen fingeravtrykk er registrert."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Denne enheten har ikke fingeravtrykkssensor."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensoren er midlertidig slått av."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Bruk fingeravtrykk"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Bruk fingeravtrykk eller skjermlås"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 130adcdc..25f59c2 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"कुनै पनि फिंगरप्रिन्ट दर्ता गरिएको छैन।"</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"यो डिभाइसमा कुनै पनि फिंगरप्रिन्ट सेन्सर छैन।"</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"केही समयका लागि सेन्सर असक्षम पारियो।"</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"औंला <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"फिंगरप्रिन्ट प्रयोग गर्नुहोस्"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"फिंगरप्रिन्ट वा स्क्रिन लक प्रयोग गर्नुहोस्"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 9449ac1..307c125 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Geen vingerafdrukken geregistreerd."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dit apparaat heeft geen vingerafdruksensor."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor staat tijdelijk uit."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Vinger <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Vingerafdruk gebruiken"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Vingerafdruk of schermvergrendeling gebruiken"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index b609827..1e6eb37 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"କୌଣସି ଆଙ୍ଗୁଠି ଚିହ୍ନ ପଞ୍ଜୀକୃତ ହୋଇନାହିଁ।"</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ଏହି ଡିଭାଇସ୍‌ରେ ଟିପଚିହ୍ନ ସେନ୍‍ସର୍ ନାହିଁ।"</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ସେନ୍ସରକୁ ଅସ୍ଥାୟୀ ଭାବେ ଅକ୍ଷମ କରାଯାଇଛି।"</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"ଆଙ୍ଗୁଠି <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ଟିପଚିହ୍ନ ବ୍ୟବହାର କରନ୍ତୁ"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ଟିପଚିହ୍ନ ବା ସ୍କ୍ରିନ୍ ଲକ୍ ବ୍ୟବହାର କରନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 2efcc76..fbccb25 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ਕੋਈ ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਰਜ ਨਹੀਂ ਕੀਤੇ ਗਏ।"</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ਇਸ ਡੀਵਾਈਸ ਵਿੱਚ ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਨਹੀਂ ਹੈ।"</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ਸੈਂਸਰ ਅਸਥਾਈ ਤੌਰ \'ਤੇ ਬੰਦ ਕੀਤਾ ਗਿਆ।"</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"ਉਂਗਲ <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਜਾਂ ਸਕ੍ਰੀਨ ਲਾਕ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 49890fc..0671787 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -607,6 +607,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nie zarejestrowano odcisków palców."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"To urządzenie nie jest wyposażone w czytnik linii papilarnych."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Czujnik jest tymczasowo wyłączony."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Odcisk palca <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Używaj odcisku palca"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Używaj odcisku palca lub blokady ekranu"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index f4c0cdb..8c6b146 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nenhuma impressão digital registrada."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem um sensor de impressão digital."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor desativado temporariamente."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar impressão digital"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Usar impressão digital ou bloqueio de tela"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index f1c8476..0fa0e50 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nenhuma impressão digital registada."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem sensor de impressões digitais."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporariamente desativado."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utilizar a impressão digital"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Utilizar o bloqueio de ecrã ou a impressão digital"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index f4c0cdb..8c6b146 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nenhuma impressão digital registrada."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem um sensor de impressão digital."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor desativado temporariamente."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar impressão digital"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Usar impressão digital ou bloqueio de tela"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index c50f2e3..7bbf3e1 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -604,6 +604,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nu au fost înregistrate amprente."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dispozitivul nu are senzor de amprentă."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzorul este dezactivat temporar."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Degetul <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Folosiți amprenta"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Folosiți amprenta sau blocarea ecranului"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index dde5ebc..ebe46e8 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -607,6 +607,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Нет отсканированных отпечатков пальцев"</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"На этом устройстве нет сканера отпечатков пальцев."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сканер отпечатков пальцев временно отключен."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Отпечаток <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Использовать отпечаток пальца"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Использовать отпечаток пальца или блокировку экрана"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 13df5e2..eea2594 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ඇඟිලි සලකුණු ඇතුළත් කර නොමැත."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"මෙම උපාංගයේ ඇඟිලි සලකුණු සංවේදකයක් නොමැත."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"සංවේදකය තාවකාලිකව අබල කර ඇත."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"ඇඟිලි <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ඇඟිලි සලකුණ භාවිත කරන්න"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ඇඟිලි සලකුණ හෝ තිර අගුල භාවිත කරන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 4f8329a..234e3a8 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -607,6 +607,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Neregistrovali ste žiadne odtlačky prstov."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Toto zariadenie nemá senzor odtlačkov prstov."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je dočasne vypnutý."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Prst: <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Použiť odtlačok prsta"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Použiť odtlačok prsta alebo zámku obrazovky"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index fb6fc9a..19c4434 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -607,6 +607,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ni registriranih prstnih odtisov."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ta naprava nima tipala prstnih odtisov."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Tipalo je začasno onemogočeno."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Uporaba prstnega odtisa"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Uporaba prstnega odtisa ali odklepanja s poverilnico"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 646cc85..7934473 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nuk ka asnjë gjurmë gishti të regjistruar."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Kjo pajisje nuk ka sensor të gjurmës së gishtit."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensori është çaktivizuar përkohësisht."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Gishti <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Përdor gjurmën e gishtit"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Përdor gjurmën e gishtit ose kyçjen e ekranit"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 98ddb39..a0ae389 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -604,6 +604,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Није регистрован ниједан отисак прста."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Овај уређај нема сензор за отисак прста."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сензор је привремено онемогућен."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Прст <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Користите отисак прста"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Користите отисак прста или закључавање екрана"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index f155f3c..34d5e2c 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Inga fingeravtryck har registrerats."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Enheten har ingen fingeravtryckssensor."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensorn har tillfälligt inaktiverats."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Använd ditt fingeravtryck"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Använd ditt fingeravtryck eller skärmlåset"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 8ca0ac0..cc8d644 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Hakuna alama za vidole zilizojumuishwa."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Kifaa hiki hakina kitambua alama ya kidole."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Kitambuzi kimezimwa kwa muda."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Kidole cha <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Tumia alama ya kidole"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Tumia alama ya kidole au mbinu ya kufunga skrini"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 4a50960..0d28151 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"கைரேகைப் பதிவுகள் எதுவும் இல்லை."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"இந்தச் சாதனத்தில் கைரேகை சென்சார் இல்லை."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"சென்சார் தற்காலிகமாக முடக்கப்பட்டுள்ளது."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"கைரேகை <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"கைரேகையைப் பயன்படுத்து"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"கைரேகையையோ திரைப் பூட்டையோ பயன்படுத்து"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index d910224..936b758 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"వేలిముద్రలు నమోదు చేయబడలేదు."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ఈ పరికరంలో వేలిముద్ర సెన్సార్ ఎంపిక లేదు."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"సెన్సార్ తాత్కాలికంగా డిజేబుల్ చేయబడింది."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"వేలు <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"వేలిముద్రను ఉపయోగించండి"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"వేలిముద్ర లేదా స్క్రీన్ లాక్‌ను ఉపయోగించండి"</string>
@@ -1458,10 +1460,8 @@
     <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"ప్యాకేజీల తొలగింపును అభ్యర్థించడానికి యాప్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"బ్యాటరీ అనుకూలీకరణలను విస్మరించడానికి అడగాలి"</string>
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ఆ యాప్ కోసం బ్యాటరీ అనుకూలీకరణలు విస్మరించేలా అనుమతి కోరడానికి యాప్‌ను అనుమతిస్తుంది."</string>
-    <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
-    <skip />
-    <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
-    <skip />
+    <string name="permlab_queryAllPackages" msgid="2928450604653281650">"అన్ని ప్యాకేజీలను క్వెరీ చేయండి"</string>
+    <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ఇన్‌స్టాల్ చేసిన అన్ని ప్యాకేజీలను చూడటానికి యాప్‌ను అనుమతించండి."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"జూమ్ నియంత్రణ కోసం రెండుసార్లు నొక్కండి"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"విడ్జెట్‌ను జోడించడం సాధ్యపడలేదు."</string>
     <string name="ime_action_go" msgid="5536744546326495436">"వెళ్లు"</string>
diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml
index 3ecb1dd..55e5685 100644
--- a/core/res/res/values-television/config.xml
+++ b/core/res/res/values-television/config.xml
@@ -42,4 +42,8 @@
 
     <!-- Allow SystemUI to show the shutdown dialog -->
     <bool name="config_showSysuiShutdown">true</bool>
+
+    <!-- Component name of the activity used to inform a user about a sensory being blocked because
+     of privacy settings. -->
+    <string name="config_sensorUseStartedActivity">com.android.systemui/com.android.systemui.sensorprivacy.television.TvUnblockSensorActivity</string>
 </resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 3f9ae3a..9b2966f 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ไม่มีลายนิ้วมือที่ลงทะเบียน"</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"อุปกรณ์นี้ไม่มีเซ็นเซอร์ลายนิ้วมือ"</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ปิดใช้เซ็นเซอร์ชั่วคราวแล้ว"</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"นิ้ว <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ใช้ลายนิ้วมือ"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ใช้ลายนิ้วมือหรือการล็อกหน้าจอ"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index edc7427..a34ac28 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Walang naka-enroll na fingerprint."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Walang sensor ng fingerprint ang device na ito."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Pansamantalang na-disable ang sensor."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Daliri <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Gumamit ng fingerprint"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Gumamit ng fingerprint o lock ng screen"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index e5f9fe9..a08dcb2 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Parmak izi kaydedilmedi."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu cihazda parmak izi sensörü yok."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensör geçici olarak devre dışı bırakıldı."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. parmak"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Parmak izi kullan"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Parmak izi veya ekran kilidi kullan"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 964c077..8be7da6 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -607,6 +607,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Відбитки пальців не зареєстровано."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"На цьому пристрої немає сканера відбитків пальців."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Датчик тимчасово вимкнено."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Відбиток пальця <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Доступ за відбитком пальця"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Використовувати відбиток пальця або дані для розблокування екрана"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 7f5046b..e0344e0 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"کوئی فنگر پرنٹ مندرج شدہ نہیں ہے۔"</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"اس آلہ میں فنگر پرنٹ سینسر نہیں ہے۔"</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"سینسر عارضی طور غیر فعال ہے۔"</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"انگلی <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"فنگر پرنٹ استعمال کریں"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"فنگر پرنٹ یا اسکرین لاک استعمال کریں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index d8769b2..25a6a1a 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Hech qanday barmoq izi qayd qilinmagan."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu qurilmada barmoq izi skaneri mavjud emas."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor vaqtincha faol emas."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Barmoq izi <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Barmoq izi ishlatish"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Barmoq izi yoki ekran qulfi"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index b276ff1..a5cfbaf 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Chưa đăng ký vân tay."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Thiết bị này không có cảm biến vân tay."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Đã tạm thời tắt cảm biến."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Ngón tay <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Dùng vân tay"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Dùng vân tay hoặc phương thức khóa màn hình"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index b745661..23832eb 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"未注册任何指纹。"</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"此设备没有指纹传感器。"</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"传感器已暂时停用。"</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"手指 <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"使用指纹"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"使用指纹或屏幕锁定凭据"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index fd0bd9f..0b59e52 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"未註冊任何指紋"</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"此裝置沒有指紋感應器。"</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"感應器已暫時停用。"</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"手指 <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"使用指紋鎖定"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"使用指紋或螢幕鎖定"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 310663f..8f81d83 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"未登錄任何指紋。"</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"這個裝置沒有指紋感應器。"</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"感應器已暫時停用。"</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"手指 <xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"使用指紋"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"使用指紋或螢幕鎖定功能"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index ae01829..c5c7818 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -601,6 +601,8 @@
     <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Azikho izigxivizo zeminwe ezibhalisiwe."</string>
     <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Le divayisi ayinayo inzwa yezigxivizo zeminwe."</string>
     <string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Inzwa ikhutshazwe okwesikhashana."</string>
+    <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) -->
+    <skip />
     <string name="fingerprint_name_template" msgid="8941662088160289778">"Umunwe ongu-<xliff:g id="FINGERID">%d</xliff:g>"</string>
     <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Sebenzisa izigxivizo zeminwe"</string>
     <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Sebenzisa izigxivizo zeminwe noma ukukhiya isikrini"</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3c47366..7c7a893 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1706,6 +1706,10 @@
          config_enableFusedLocationOverlay is false. -->
     <string name="config_fusedLocationProviderPackageName" translatable="false">com.android.location.fused</string>
 
+    <!-- Default value for the ADAS GNSS Location Enabled setting if this setting has never been
+         set before. -->
+    <bool name="config_defaultAdasGnssLocationEnabled" translatable="false">false</bool>
+
     <string-array name="config_locationExtraPackageNames" translatable="false"></string-array>
 
     <!-- The package name of the default network recommendation app.
@@ -3350,6 +3354,10 @@
     <!-- The default vibration strength, must be between 1 and 255 inclusive. -->
     <integer name="config_defaultVibrationAmplitude">255</integer>
 
+    <!-- The max vibration strength allowed in audio haptic channels, must be positive or zero if
+         limit is unknown. -->
+    <item name="config_hapticChannelMaxVibrationAmplitude" format="float" type="dimen">0</item>
+
     <!-- If the device should still vibrate even in low power mode, for certain priority vibrations
      (e.g. accessibility, alarms). This is mainly for Wear devices that don't have speakers. -->
     <bool name="config_allowPriorityVibrationsInLowPowerMode">false</bool>
@@ -5003,6 +5011,10 @@
     <!-- Default value for Settings.ASSIST_TOUCH_GESTURE_ENABLED -->
     <bool name="config_assistTouchGestureEnabledDefault">true</bool>
 
+    <!-- The maximum byte size of the information contained in the bundle of
+    HotwordDetectedResult. -->
+    <integer translatable="false" name="config_hotwordDetectedResultMaxBundleSize">0</integer>
+
     <!-- The amount of dimming to apply to wallpapers with mid range luminance. 0 displays
          the wallpaper at full brightness. 1 displays the wallpaper as fully black. -->
     <item name="config_wallpaperDimAmount" format="float" type="dimen">0.05</item>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index bdeff893..302bd94 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1603,6 +1603,8 @@
     <string name="fingerprint_acquired_too_bright">Too bright</string>
     <!-- Message shown during fingerprint acquisition when a fingerprint must be adjusted.[CHAR LIMIT=50] -->
     <string name="fingerprint_acquired_try_adjusting">Try adjusting</string>
+    <!-- Message shown during fingerprint acquisition when a fingeprint area has already been captured during enrollment [CHAR LIMIT=100] -->
+    <string name="fingerprint_acquired_immobile">Change the position of your finger slightly each time</string>
     <!-- Array containing custom messages shown during fingerprint acquisision from vendor.  Vendor is expected to add and translate these strings -->
     <string-array name="fingerprint_acquired_vendor">
     </string-array>
@@ -1636,6 +1638,8 @@
     <string name="fingerprint_error_hw_not_present">This device does not have a fingerprint sensor.</string>
     <!-- Generic error message shown when fingerprint is not available due to a security vulnerability. [CHAR LIMIT=50] -->
     <string name="fingerprint_error_security_update_required">Sensor temporarily disabled.</string>
+    <!-- Generic error message shown when fingerprint needs calibration [CHAR LIMIT=50] -->
+    <string name="fingerprint_error_bad_calibration">Sensor needs calibration</string>
 
     <!-- Template to be used to name enrolled fingerprints by default. -->
     <string name="fingerprint_name_template">Finger <xliff:g id="fingerId" example="1">%d</xliff:g></string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 97a2a38..94c65a0 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1918,6 +1918,7 @@
   <java-symbol type="bool" name="config_tintNotificationActionButtons" />
   <java-symbol type="bool" name="config_dozeAfterScreenOffByDefault" />
   <java-symbol type="bool" name="config_enableActivityRecognitionHardwareOverlay" />
+  <java-symbol type="bool" name="config_defaultAdasGnssLocationEnabled" />
   <java-symbol type="bool" name="config_enableFusedLocationOverlay" />
   <java-symbol type="bool" name="config_enableGeocoderOverlay" />
   <java-symbol type="bool" name="config_enableGeofenceOverlay" />
@@ -2013,6 +2014,7 @@
   <java-symbol type="integer" name="config_notificationServiceArchiveSize" />
   <java-symbol type="integer" name="config_previousVibrationsDumpLimit" />
   <java-symbol type="integer" name="config_defaultVibrationAmplitude" />
+  <java-symbol type="dimen" name="config_hapticChannelMaxVibrationAmplitude" />
   <java-symbol type="integer" name="config_vibrationWaveformRampStepDuration" />
   <java-symbol type="integer" name="config_vibrationWaveformRampDownDuration" />
   <java-symbol type="integer" name="config_radioScanningTimeout" />
@@ -2537,6 +2539,8 @@
   <java-symbol type="string" name="fingerprint_error_no_fingerprints" />
   <java-symbol type="string" name="fingerprint_error_hw_not_present" />
   <java-symbol type="string" name="fingerprint_error_security_update_required" />
+  <java-symbol type="string" name="fingerprint_error_bad_calibration" />
+  <java-symbol type="string" name="fingerprint_acquired_immobile" />
 
   <!-- Fingerprint config -->
   <java-symbol type="integer" name="config_fingerprintMaxTemplatesPerUser"/>
@@ -4401,5 +4405,7 @@
   <java-symbol type="bool" name="config_assistLongPressHomeEnabledDefault" />
   <java-symbol type="bool" name="config_assistTouchGestureEnabledDefault" />
 
+  <java-symbol type="integer" name="config_hotwordDetectedResultMaxBundleSize" />
+
   <java-symbol type="dimen" name="config_wallpaperDimAmount" />
 </resources>
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
new file mode 100644
index 0000000..8c05978
--- /dev/null
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -0,0 +1,117 @@
+/*
+ * 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.app;
+
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Test for verifying the behavior of {@link PropertyInvalidatedCache}.  This test does
+ * not use any actual binder calls - it is entirely self-contained.
+ * <p>
+ * Build/Install/Run:
+ *  atest FrameworksCoreTests:PropertyInvalidatedCacheTests
+ */
+@SmallTest
+public class PropertyInvalidatedCacheTests {
+
+    static final String CACHE_PROPERTY = "cache_key.cache_test_a";
+
+    // This class is a proxy for binder calls.  It contains a counter that increments
+    // every time the class is queried.
+    private static class ServerProxy {
+        // The number of times this class was queried.
+        private int mCount = 0;
+
+        // A single query.  The key behavior is that the query count is incremented.
+        boolean query(int x) {
+            mCount++;
+            return value(x);
+        }
+
+        // Return the expected value of an input, without incrementing the query count.
+        boolean value(int x) {
+            return x % 3 == 0;
+        }
+
+        // Verify the count.
+        void verify(int x) {
+            assertEquals(x, mCount);
+        }
+    }
+
+    @Test
+    public void testDisableCache1() {
+
+        // A stand-in for the binder.  The test verifies that calls are passed through to
+        // this class properly.
+        ServerProxy tester = new ServerProxy();
+
+        // Three caches, all using the same system property but one uses a different name.
+        PropertyInvalidatedCache<Integer, Boolean> cache1 =
+                new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
+                    @Override
+                    protected Boolean recompute(Integer x) {
+                        return tester.query(x);
+                    }
+                };
+        PropertyInvalidatedCache<Integer, Boolean> cache2 =
+                new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
+                    @Override
+                    protected Boolean recompute(Integer x) {
+                        return tester.query(x);
+                    }
+                };
+        PropertyInvalidatedCache<Integer, Boolean> cache3 =
+                new PropertyInvalidatedCache<>(4, CACHE_PROPERTY, "cache3") {
+                    @Override
+                    protected Boolean recompute(Integer x) {
+                        return tester.query(x);
+                    }
+                };
+
+        // Caches are enabled upon creation.
+        assertEquals(false, cache1.getDisabledState());
+        assertEquals(false, cache2.getDisabledState());
+        assertEquals(false, cache3.getDisabledState());
+
+        // Disable the cache1 instance.  Only cache1 is disabled
+        cache1.disableInstance();
+        assertEquals(true, cache1.getDisabledState());
+        assertEquals(false, cache2.getDisabledState());
+        assertEquals(false, cache3.getDisabledState());
+
+        // Disable cache1.  This will disable cache1 and cache2 because they share the
+        // same name.  cache3 has a different name and will not be disabled.
+        cache1.disableLocal();
+        assertEquals(true, cache1.getDisabledState());
+        assertEquals(true, cache2.getDisabledState());
+        assertEquals(false, cache3.getDisabledState());
+
+        // Create a new cache1.  Verify that the new instance is disabled.
+        cache1 = new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
+                    @Override
+                    protected Boolean recompute(Integer x) {
+                        return tester.query(x);
+                    }
+                };
+        assertEquals(true, cache1.getDisabledState());
+    }
+}
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchResultTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchResultTest.java
index de0670b..228a061 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchResultTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchResultTest.java
@@ -18,7 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.testng.Assert.expectThrows;
+import static org.junit.Assert.assertThrows;
 
 import org.junit.Test;
 
@@ -26,7 +26,7 @@
     @Test
     public void testMapNullPointerException() {
         NullPointerException e =
-                expectThrows(
+                assertThrows(
                         NullPointerException.class,
                         () -> {
                             Object o = null;
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java
deleted file mode 100644
index 6fad4b8d..0000000
--- a/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.appsearch;
-
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.android.server.appsearch.testing.AppSearchEmail;
-
-import com.google.common.collect.ImmutableSet;
-
-import org.junit.Test;
-
-import java.util.Set;
-
-public class PutDocumentsRequestTest {
-
-    @Test
-    public void addGenericDocument_byCollection() {
-        Set<AppSearchEmail> emails =
-                ImmutableSet.of(
-                        new AppSearchEmail.Builder("namespace", "test1").build(),
-                        new AppSearchEmail.Builder("namespace", "test2").build());
-        PutDocumentsRequest request =
-                new PutDocumentsRequest.Builder().addGenericDocuments(emails).build();
-
-        assertThat(request.getGenericDocuments().get(0).getId()).isEqualTo("test1");
-        assertThat(request.getGenericDocuments().get(1).getId()).isEqualTo("test2");
-    }
-}
diff --git a/core/tests/coretests/src/android/app/appsearch/external/util/IndentingStringBuilderTest.java b/core/tests/coretests/src/android/app/appsearch/external/util/IndentingStringBuilderTest.java
new file mode 100644
index 0000000..057ecbd
--- /dev/null
+++ b/core/tests/coretests/src/android/app/appsearch/external/util/IndentingStringBuilderTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appsearch.util;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import org.junit.Test;
+
+public class IndentingStringBuilderTest {
+    @Test
+    public void testAppendIndentedStrings() {
+        IndentingStringBuilder stringBuilder = new IndentingStringBuilder();
+        stringBuilder
+                .increaseIndentLevel()
+                .append("\nIndentLevel1\nIndentLevel1\n")
+                .decreaseIndentLevel()
+                .append("IndentLevel0,\n");
+
+        String str = stringBuilder.toString();
+        String expectedString = "\n  IndentLevel1\n  IndentLevel1\nIndentLevel0,\n";
+
+        assertThat(str).isEqualTo(expectedString);
+    }
+
+    @Test
+    public void testDecreaseIndentLevel_throwsException() {
+        IndentingStringBuilder stringBuilder = new IndentingStringBuilder();
+
+        Exception e =
+                assertThrows(
+                        IllegalStateException.class, () -> stringBuilder.decreaseIndentLevel());
+        assertThat(e).hasMessageThat().contains("Cannot set indent level below 0.");
+    }
+
+    @Test
+    public void testAppendIndentedObjects() {
+        IndentingStringBuilder stringBuilder = new IndentingStringBuilder();
+        Object stringProperty = "String";
+        Object longProperty = 1L;
+        Object booleanProperty = true;
+
+        stringBuilder
+                .append(stringProperty)
+                .append("\n")
+                .increaseIndentLevel()
+                .append(longProperty)
+                .append("\n")
+                .decreaseIndentLevel()
+                .append(booleanProperty);
+
+        String str = stringBuilder.toString();
+        String expectedString = "String\n  1\ntrue";
+
+        assertThat(str).isEqualTo(expectedString);
+    }
+
+    @Test
+    public void testAppendIndentedStrings_doesNotIndentLineBreak() {
+        IndentingStringBuilder stringBuilder = new IndentingStringBuilder();
+
+        stringBuilder
+                .append("\n")
+                .increaseIndentLevel()
+                .append("\n\n")
+                .decreaseIndentLevel()
+                .append("\n");
+
+        String str = stringBuilder.toString();
+        String expectedString = "\n\n\n\n";
+
+        assertThat(str).isEqualTo(expectedString);
+    }
+}
diff --git a/core/tests/coretests/src/android/os/VibratorInfoTest.java b/core/tests/coretests/src/android/os/VibratorInfoTest.java
index 8c7d10c..6e07fa2 100644
--- a/core/tests/coretests/src/android/os/VibratorInfoTest.java
+++ b/core/tests/coretests/src/android/os/VibratorInfoTest.java
@@ -87,14 +87,14 @@
     public void testIsPrimitiveSupported() {
         VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
                 .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
-                .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
                 .build();
         assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
         assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_TICK));
 
         // Returns false when there is no compose capability.
         info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
-                .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
                 .build();
         assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
     }
@@ -103,8 +103,7 @@
     public void testGetPrimitiveDuration() {
         VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
                 .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
-                .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
-                .setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
                 .build();
         assertEquals(20, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
         assertEquals(0, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_TICK));
@@ -113,6 +112,26 @@
     }
 
     @Test
+    public void testCompositionLimits() {
+        VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
+                .setPrimitiveDelayMax(100)
+                .setCompositionSizeMax(10)
+                .setPwlePrimitiveDurationMax(50)
+                .setPwleSizeMax(20)
+                .build();
+        assertEquals(100, info.getPrimitiveDelayMax());
+        assertEquals(10, info.getCompositionSizeMax());
+        assertEquals(50, info.getPwlePrimitiveDurationMax());
+        assertEquals(20, info.getPwleSizeMax());
+
+        VibratorInfo emptyInfo = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
+        assertEquals(0, emptyInfo.getPrimitiveDelayMax());
+        assertEquals(0, emptyInfo.getCompositionSizeMax());
+        assertEquals(0, emptyInfo.getPwlePrimitiveDurationMax());
+        assertEquals(0, emptyInfo.getPwleSizeMax());
+    }
+
+    @Test
     public void testGetDefaultBraking_returnsFirstSupportedBraking() {
         assertEquals(Braking.NONE, new VibratorInfo.Builder(
                 TEST_VIBRATOR_ID).build().getDefaultBraking());
@@ -263,8 +282,12 @@
         VibratorInfo.Builder completeBuilder = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
                 .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
                 .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
-                .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
-                .setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
+                .setPrimitiveDelayMax(100)
+                .setCompositionSizeMax(10)
+                .setSupportedBraking(Braking.CLAB)
+                .setPwlePrimitiveDurationMax(50)
+                .setPwleSizeMax(20)
                 .setQFactor(2f)
                 .setFrequencyMapping(TEST_FREQUENCY_MAPPING);
         VibratorInfo complete = completeBuilder.build();
@@ -279,8 +302,7 @@
         assertNotEquals(complete, completeWithComposeControl);
 
         VibratorInfo completeWithNoEffects = completeBuilder
-                .setSupportedEffects()
-                .setSupportedPrimitives()
+                .setSupportedEffects(new int[0])
                 .build();
         assertNotEquals(complete, completeWithNoEffects);
 
@@ -289,13 +311,8 @@
                 .build();
         assertNotEquals(complete, completeWithUnknownEffects);
 
-        VibratorInfo completeWithUnknownPrimitives = completeBuilder
-                .setSupportedPrimitives(null)
-                .build();
-        assertNotEquals(complete, completeWithUnknownPrimitives);
-
         VibratorInfo completeWithDifferentPrimitiveDuration = completeBuilder
-                .setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
                 .build();
         assertNotEquals(complete, completeWithDifferentPrimitiveDuration);
 
@@ -321,12 +338,17 @@
                 .build();
         assertNotEquals(complete, completeWithDifferentQFactor);
 
-        VibratorInfo empty = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
-        VibratorInfo emptyWithKnownSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
-                .setSupportedEffects()
-                .setSupportedPrimitives()
+        VibratorInfo unknownEffectSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
+        VibratorInfo knownEmptyEffectSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
+                .setSupportedEffects(new int[0])
                 .build();
-        assertNotEquals(empty, emptyWithKnownSupport);
+        assertNotEquals(unknownEffectSupport, knownEmptyEffectSupport);
+
+        VibratorInfo unknownBrakingSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
+        VibratorInfo knownEmptyBrakingSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
+                .setSupportedBraking(new int[0])
+                .build();
+        assertNotEquals(unknownBrakingSupport, knownEmptyBrakingSupport);
     }
 
     @Test
@@ -334,8 +356,7 @@
         VibratorInfo original = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
                 .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
                 .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
-                .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
-                .setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
+                .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
                 .setQFactor(Float.NaN)
                 .setFrequencyMapping(TEST_FREQUENCY_MAPPING)
                 .build();
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
index 3e620c2..fedbf7a 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
@@ -24,6 +24,8 @@
 
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import android.os.BatteryConsumer;
 import android.os.BatteryUsageStats;
@@ -47,7 +49,9 @@
 import java.nio.charset.StandardCharsets;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -64,13 +68,15 @@
     }
 
     @Test
-    public void testParcelability() {
+    public void testParcelability_smallNumberOfUids() {
         final BatteryUsageStats outBatteryUsageStats = buildBatteryUsageStats1(true).build();
         final Parcel outParcel = Parcel.obtain();
         outParcel.writeParcelable(outBatteryUsageStats, 0);
         final byte[] bytes = outParcel.marshall();
         outParcel.recycle();
 
+        assertThat(bytes.length).isLessThan(2000);
+
         final Parcel inParcel = Parcel.obtain();
         inParcel.unmarshall(bytes, 0, bytes.length);
         inParcel.setDataPosition(0);
@@ -80,6 +86,46 @@
         assertBatteryUsageStats1(inBatteryUsageStats, true);
     }
 
+    @Test
+    public void testParcelability_largeNumberOfUids() {
+        final BatteryUsageStats.Builder builder =
+                new BatteryUsageStats.Builder(new String[0]);
+
+        // Without the use of a blob, this BatteryUsageStats object would generate a Parcel
+        // larger than 64 Kb
+        final int uidCount = 200;
+        for (int i = 0; i < uidCount; i++) {
+            BatteryStatsImpl.Uid mockUid = mock(BatteryStatsImpl.Uid.class);
+            when(mockUid.getUid()).thenReturn(i);
+            builder.getOrCreateUidBatteryConsumerBuilder(mockUid)
+                    .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, i * 100);
+        }
+
+        BatteryUsageStats outBatteryUsageStats = builder.build();
+
+        final Parcel parcel = Parcel.obtain();
+        parcel.writeParcelable(outBatteryUsageStats, 0);
+
+        assertThat(parcel.dataSize()).isLessThan(2000);
+
+        // This parcel cannot be marshaled because it contains a file descriptor.
+        // Assuming that parcel marshaling works fine, let's just rewind the parcel.
+        parcel.setDataPosition(0);
+
+        final BatteryUsageStats inBatteryUsageStats =
+                parcel.readParcelable(getClass().getClassLoader());
+        parcel.recycle();
+
+        assertThat(inBatteryUsageStats.getUidBatteryConsumers()).hasSize(uidCount);
+        final Map<Integer, UidBatteryConsumer> consumersByUid =
+                inBatteryUsageStats.getUidBatteryConsumers().stream().collect(
+                        Collectors.toMap(UidBatteryConsumer::getUid, c -> c));
+        for (int i = 0; i < uidCount; i++) {
+            final UidBatteryConsumer uidBatteryConsumer = consumersByUid.get(i);
+            assertThat(uidBatteryConsumer).isNotNull();
+            assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(i * 100);
+        }
+    }
 
     @Test
     public void testDefaultSessionDuration() {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index 89d2b74..72a145f 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -151,7 +151,7 @@
      * Gets the {@link KeyStore} operation handle corresponding to the provided JCA crypto
      * primitive.
      *
-     * <p>The following primitives are supported: {@link Cipher} and {@link Mac}.
+     * <p>The following primitives are supported: {@link Cipher}, {@link Signature} and {@link Mac}.
      *
      * @return KeyStore operation handle or {@code 0} if the provided primitive's KeyStore operation
      *         is not in progress.
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 70f03d2..f28ee82 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -117,7 +117,9 @@
     <!-- This should be at least the size of bubble_expanded_view_padding; it is used to include
          a slight touch slop around the expanded view. -->
     <dimen name="bubble_expanded_view_slop">8dp</dimen>
-    <!-- Default (and minimum) height of the expanded view shown when the bubble is expanded -->
+    <!-- Default (and minimum) height of the expanded view shown when the bubble is expanded.
+         If this value changes then R.dimen.bubble_expanded_view_min_height in CtsVerifier
+         should also be updated. -->
     <dimen name="bubble_expanded_default_height">180dp</dimen>
     <!-- On large screens the width of the expanded view is restricted to this size. -->
     <dimen name="bubble_expanded_view_tablet_width">412dp</dimen>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
index a525c2c..3253bb0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
@@ -87,4 +87,9 @@
      * Notifies when user switch complete
      */
     void onUserSwitch(int userId);
+
+    /**
+     * Notifies when keyguard visibility changed
+     */
+    void onKeyguardVisibilityChanged(boolean showing);
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index b43daa0..b5c5402 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -81,6 +81,7 @@
     private volatile boolean mIsSwipeToNotificationEnabled;
     private boolean mTaskChangeToExit;
     private boolean mLockedDisabled;
+    private boolean mKeyguardShowing;
     private int mUserId;
     private float mOffSetFraction;
 
@@ -357,7 +358,7 @@
 
     @VisibleForTesting
     void startOneHanded() {
-        if (isLockedDisabled()) {
+        if (isLockedDisabled() || mKeyguardShowing) {
             Slog.d(TAG, "Temporary lock disabled");
             return;
         }
@@ -481,7 +482,9 @@
 
     @VisibleForTesting
     void notifyExpandNotification() {
-        mMainExecutor.execute(() -> mEventCallback.notifyExpandNotification());
+        if (mEventCallback != null) {
+            mMainExecutor.execute(() -> mEventCallback.notifyExpandNotification());
+        }
     }
 
     @VisibleForTesting
@@ -692,6 +695,10 @@
         mTutorialHandler.onConfigurationChanged();
     }
 
+    private void onKeyguardVisibilityChanged(boolean showing) {
+        mKeyguardShowing = showing;
+    }
+
     private void onUserSwitch(int newUserId) {
         unregisterSettingObservers();
         mUserId = newUserId;
@@ -838,6 +845,13 @@
                 OneHandedController.this.onUserSwitch(userId);
             });
         }
+
+        @Override
+        public void onKeyguardVisibilityChanged(boolean showing) {
+            mMainExecutor.execute(() -> {
+                OneHandedController.this.onKeyguardVisibilityChanged(showing);
+            });
+        }
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
index 1a365fe..9986154 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
@@ -235,7 +235,8 @@
         }
 
         void onAnimationProgress(float linearProgress) {
-            if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid()) {
+            if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid()
+                    || !mSplashScreenView.isAttachedToWindow()) {
                 return;
             }
 
@@ -267,15 +268,20 @@
                 return;
             }
             final SurfaceControl.Transaction tx = mTransactionPool.acquire();
-            tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
+            if (mSplashScreenView.isAttachedToWindow()) {
+                tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
 
-            SyncRtSurfaceTransactionApplier.SurfaceParams
-                    params = new SyncRtSurfaceTransactionApplier.SurfaceParams
-                    .Builder(mFirstWindowSurface)
-                    .withWindowCrop(null)
-                    .withMergeTransaction(tx)
-                    .build();
-            mApplier.scheduleApply(params);
+                SyncRtSurfaceTransactionApplier.SurfaceParams
+                        params = new SyncRtSurfaceTransactionApplier.SurfaceParams
+                        .Builder(mFirstWindowSurface)
+                        .withWindowCrop(null)
+                        .withMergeTransaction(tx)
+                        .build();
+                mApplier.scheduleApply(params);
+            } else {
+                tx.setWindowCrop(mFirstWindowSurface, null);
+                tx.apply();
+            }
             mTransactionPool.release(tx);
 
             Choreographer.getSfInstance().postCallback(CALLBACK_COMMIT,
@@ -287,13 +293,14 @@
         if (DEBUG_EXIT_ANIMATION) {
             Slog.v(TAG, "vanish animation finished");
         }
-        mSplashScreenView.post(() -> {
+
+        if (mSplashScreenView.isAttachedToWindow()) {
             mSplashScreenView.setVisibility(GONE);
             if (mFinishCallback != null) {
                 mFinishCallback.run();
                 mFinishCallback = null;
             }
-        });
+        }
         if (mShiftUpAnimation != null) {
             mShiftUpAnimation.finish();
         }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index 47789b7..95090033 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -435,4 +435,17 @@
 
         verify(mSpiedOneHandedController).notifyShortcutState(anyInt());
     }
+
+    @Test
+    public void testNotifyExpandNotification_withNullCheckProtection() {
+        when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(false);
+        when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE);
+        when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
+        when(mSpiedOneHandedController.isSwipeToNotificationEnabled()).thenReturn(true);
+        mSpiedOneHandedController.setOneHandedEnabled(true);
+        mSpiedOneHandedController.notifyExpandNotification();
+
+        // Verify no NPE crash and mMockShellMainExecutor never be execute.
+        verify(mMockShellMainExecutor, never()).execute(any());
+    }
 }
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index c770150..45a4f6c 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -331,6 +331,8 @@
             mSkiaLayer.reset();
         }
 
+        mProperties.mutateLayerProperties().mutableStretchEffect().clear();
+        mStretchMask.clear();
         // Clear out the previous snapshot and the image filter the previous
         // snapshot was created with whenever the layer changes.
         mSnapshotResult.snapshot = nullptr;
diff --git a/libs/hwui/effects/StretchEffect.cpp b/libs/hwui/effects/StretchEffect.cpp
index 43f805d..17cd3ce 100644
--- a/libs/hwui/effects/StretchEffect.cpp
+++ b/libs/hwui/effects/StretchEffect.cpp
@@ -186,6 +186,7 @@
 
 static const float ZERO = 0.f;
 static const float INTERPOLATION_STRENGTH_VALUE = 0.7f;
+static const char CONTENT_TEXTURE[] = "uContentTexture";
 
 sk_sp<SkShader> StretchEffect::getShader(float width, float height,
                                          const sk_sp<SkImage>& snapshotImage,
@@ -207,7 +208,7 @@
         mBuilder = std::make_unique<SkRuntimeShaderBuilder>(getStretchEffect());
     }
 
-    mBuilder->child("uContentTexture") =
+    mBuilder->child(CONTENT_TEXTURE) =
             snapshotImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
                                       SkSamplingOptions(SkFilterMode::kLinear), matrix);
     mBuilder->uniform("uInterpolationStrength").set(&INTERPOLATION_STRENGTH_VALUE, 1);
@@ -226,7 +227,9 @@
     mBuilder->uniform("viewportWidth").set(&width, 1);
     mBuilder->uniform("viewportHeight").set(&height, 1);
 
-    return mBuilder->makeShader(nullptr, false);
+    auto result = mBuilder->makeShader(nullptr, false);
+    mBuilder->child(CONTENT_TEXTURE) = nullptr;
+    return result;
 }
 
 sk_sp<SkRuntimeEffect> StretchEffect::getStretchEffect() {
diff --git a/libs/hwui/effects/StretchEffect.h b/libs/hwui/effects/StretchEffect.h
index 25777c2..3eab9f0 100644
--- a/libs/hwui/effects/StretchEffect.h
+++ b/libs/hwui/effects/StretchEffect.h
@@ -113,6 +113,10 @@
         return !isEmpty();
     }
 
+    void clear() {
+        mBuilder = nullptr;
+    }
+
 private:
     static sk_sp<SkRuntimeEffect> getStretchEffect();
     mutable SkVector mStretchDirection{0, 0};
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index 4cd3616..ecdd4b6 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -214,6 +214,25 @@
      *
      * <p> When this bit is unset, the {@link #getAccumulatedDeltaRangeMeters()} corresponds to the
      * carrier phase measurement plus an accumulated integer number of carrier half cycles.
+     *
+     * <p> For signals that have databits, the carrier phase tracking loops typically use a costas
+     * loop discriminator.  This type of tracking loop introduces a half-cycle ambiguity that is
+     * resolved by searching through the received data for known patterns of databits (e.g. GPS uses
+     * the TLM word) which then determines the polarity of the incoming data and resolves the
+     * half-cycle ambiguity.
+     *
+     * <p>Before the half-cycle ambiguity has been resolved it is possible that the ADR_STATE_VALID
+     * flag is set:
+     *
+     * <ul>
+     *   <li> In cases where ADR_STATE_HALF_CYCLE_REPORTED is not set, the
+     *   ADR_STATE_HALF_CYCLE_RESOLVED flag will not be available. Here, a half wave length will be
+     *   added to the returned accumulated delta range uncertainty to indicate the half cycle
+     *   ambiguity.
+     *   <li> In cases where ADR_STATE_HALF_CYCLE_REPORTED is set, half cycle ambiguity will be
+     *   indicated via both the ADR_STATE_HALF_CYCLE_RESOLVED flag and as well a half wave length
+     *   added to the returned accumulated delta range uncertainty.
+     * </ul>
      */
     public static final int ADR_STATE_HALF_CYCLE_RESOLVED = (1<<3);
 
@@ -1039,9 +1058,6 @@
      * with integer ambiguity resolution, to determine highly precise relative location between
      * receivers.
      *
-     * <p>This includes ensuring that all half-cycle ambiguities are resolved before this value is
-     * reported as {@link #ADR_STATE_VALID}.
-     *
      * <p>The alignment of the phase measurement will not be adjusted by the receiver so the
      * in-phase and quadrature phase components will have a quarter cycle offset as they do when
      * transmitted from the satellites. If the measurement is from a combination of the in-phase
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index c9e4e0a..5d5c0fc 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -122,6 +122,9 @@
     boolean isLocationEnabledForUser(int userId);
     void setLocationEnabledForUser(boolean enabled, int userId);
 
+    boolean isAdasGnssLocationEnabledForUser(int userId);
+    void setAdasGnssLocationEnabledForUser(boolean enabled, int userId);
+
     void addTestProvider(String name, in ProviderProperties properties,
         in List<String> locationTags, String packageName, @nullable String attributionTag);
     void removeTestProvider(String provider, String packageName, @nullable String attributionTag);
diff --git a/location/java/android/location/LastLocationRequest.java b/location/java/android/location/LastLocationRequest.java
index 9ea8048..0970c1c 100644
--- a/location/java/android/location/LastLocationRequest.java
+++ b/location/java/android/location/LastLocationRequest.java
@@ -34,12 +34,15 @@
 public final class LastLocationRequest implements Parcelable {
 
     private final boolean mHiddenFromAppOps;
+    private final boolean mAdasGnssBypass;
     private final boolean mLocationSettingsIgnored;
 
     private LastLocationRequest(
             boolean hiddenFromAppOps,
+            boolean adasGnssBypass,
             boolean locationSettingsIgnored) {
         mHiddenFromAppOps = hiddenFromAppOps;
+        mAdasGnssBypass = adasGnssBypass;
         mLocationSettingsIgnored = locationSettingsIgnored;
     }
 
@@ -56,6 +59,21 @@
     }
 
     /**
+     * Returns true if this request may access GNSS even if location settings would normally deny
+     * this, in order to enable automotive safety features. This field is only respected on
+     * automotive devices, and only if the client is recognized as a legitimate ADAS (Advanced
+     * Driving Assistance Systems) application.
+     *
+     * @return true if all limiting factors will be ignored to satisfy GNSS request
+     * @hide
+     */
+    // TODO: make this system api
+    public boolean isAdasGnssBypass() {
+        return mAdasGnssBypass;
+    }
+
+
+    /**
      * Returns true if location settings, throttling, background location limits, and any other
      * possible limiting factors will be ignored in order to satisfy this last location request.
      *
@@ -65,12 +83,22 @@
         return mLocationSettingsIgnored;
     }
 
+    /**
+     * Returns true if any bypass flag is set on this request. For internal use only.
+     *
+     * @hide
+     */
+    public boolean isBypass() {
+        return mAdasGnssBypass || mLocationSettingsIgnored;
+    }
+
     public static final @NonNull Parcelable.Creator<LastLocationRequest> CREATOR =
             new Parcelable.Creator<LastLocationRequest>() {
                 @Override
                 public LastLocationRequest createFromParcel(Parcel in) {
                     return new LastLocationRequest(
                             /* hiddenFromAppOps= */ in.readBoolean(),
+                            /* adasGnssBypass= */ in.readBoolean(),
                             /* locationSettingsIgnored= */ in.readBoolean());
                 }
                 @Override
@@ -86,6 +114,7 @@
     @Override
     public void writeToParcel(@NonNull Parcel parcel, int flags) {
         parcel.writeBoolean(mHiddenFromAppOps);
+        parcel.writeBoolean(mAdasGnssBypass);
         parcel.writeBoolean(mLocationSettingsIgnored);
     }
 
@@ -99,12 +128,13 @@
         }
         LastLocationRequest that = (LastLocationRequest) o;
         return mHiddenFromAppOps == that.mHiddenFromAppOps
+                && mAdasGnssBypass == that.mAdasGnssBypass
                 && mLocationSettingsIgnored == that.mLocationSettingsIgnored;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mHiddenFromAppOps, mLocationSettingsIgnored);
+        return Objects.hash(mHiddenFromAppOps, mAdasGnssBypass, mLocationSettingsIgnored);
     }
 
     @NonNull
@@ -115,8 +145,11 @@
         if (mHiddenFromAppOps) {
             s.append("hiddenFromAppOps, ");
         }
+        if (mAdasGnssBypass) {
+            s.append("adasGnssBypass, ");
+        }
         if (mLocationSettingsIgnored) {
-            s.append("locationSettingsIgnored, ");
+            s.append("settingsBypass, ");
         }
         if (s.length() > "LastLocationRequest[".length()) {
             s.setLength(s.length() - 2);
@@ -131,6 +164,7 @@
     public static final class Builder {
 
         private boolean mHiddenFromAppOps;
+        private boolean mAdasGnssBypass;
         private boolean mLocationSettingsIgnored;
 
         /**
@@ -138,6 +172,7 @@
          */
         public Builder() {
             mHiddenFromAppOps = false;
+            mAdasGnssBypass = false;
             mLocationSettingsIgnored = false;
         }
 
@@ -146,6 +181,7 @@
          */
         public Builder(@NonNull LastLocationRequest lastLocationRequest) {
             mHiddenFromAppOps = lastLocationRequest.mHiddenFromAppOps;
+            mAdasGnssBypass = lastLocationRequest.mAdasGnssBypass;
             mLocationSettingsIgnored = lastLocationRequest.mLocationSettingsIgnored;
         }
 
@@ -164,6 +200,25 @@
         }
 
         /**
+         * If set to true, indicates that the client is an ADAS (Advanced Driving Assistance
+         * Systems) client, which requires access to GNSS even if location settings would normally
+         * deny this, in order to enable auto safety features. This field is only respected on
+         * automotive devices, and only if the client is recognized as a legitimate ADAS
+         * application. Defaults to false.
+         *
+         * <p>Permissions enforcement occurs when resulting location request is actually used, not
+         * when this method is invoked.
+         *
+         * @hide
+         */
+        // TODO: make this system api
+        @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+        public @NonNull LastLocationRequest.Builder setAdasGnssBypass(boolean adasGnssBypass) {
+            mAdasGnssBypass = adasGnssBypass;
+            return this;
+        }
+
+        /**
          * If set to true, indicates that location settings, throttling, background location limits,
          * and any other possible limiting factors should be ignored in order to satisfy this
          * last location request. This is only intended for use in user initiated emergency
@@ -186,6 +241,7 @@
         public @NonNull LastLocationRequest build() {
             return new LastLocationRequest(
                     mHiddenFromAppOps,
+                    mAdasGnssBypass,
                     mLocationSettingsIgnored);
         }
     }
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index ae44c5e..526b84e 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -315,6 +315,33 @@
     public static final String EXTRA_LOCATION_ENABLED = "android.location.extra.LOCATION_ENABLED";
 
     /**
+     * Broadcast intent action when the ADAS (Advanced Driving Assistance Systems) GNSS location
+     * enabled state changes. Includes a boolean intent extra, {@link #EXTRA_ADAS_GNSS_ENABLED},
+     * with the enabled state of ADAS GNSS location. This broadcast only has meaning on automotive
+     * devices.
+     *
+     * @see #EXTRA_ADAS_GNSS_ENABLED
+     * @see #isAdasGnssLocationEnabled()
+     *
+     * @hide
+     */
+    // TODO: @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_ADAS_GNSS_ENABLED_CHANGED =
+            "android.location.action.ADAS_GNSS_ENABLED_CHANGED";
+
+    /**
+     * Intent extra included with {@link #ACTION_ADAS_GNSS_ENABLED_CHANGED} broadcasts, containing
+     * the boolean enabled state of ADAS GNSS location.
+     *
+     * @see #ACTION_ADAS_GNSS_ENABLED_CHANGED
+     *
+     * @hide
+     */
+    // TODO: @SystemApi
+    public static final String EXTRA_ADAS_GNSS_ENABLED = "android.location.extra.ADAS_GNSS_ENABLED";
+
+    /**
      * Broadcast intent action indicating that a high power location requests
      * has either started or stopped being active.  The current state of
      * active location requests should be read from AppOpsManager using
@@ -621,6 +648,42 @@
     }
 
     /**
+     * Returns the current enabled/disabled state of ADAS (Advanced Driving Assistance Systems)
+     * GNSS location access for the given user. This controls safety critical automotive access to
+     * GNSS location. This only has meaning on automotive devices.
+     *
+     * @return true if ADAS location is enabled and false if ADAS location is disabled.
+     *
+     * @hide
+     */
+    //TODO: @SystemApi
+    public boolean isAdasGnssLocationEnabled() {
+        try {
+            return mService.isAdasGnssLocationEnabledForUser(mContext.getUser().getIdentifier());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Enables or disables ADAS (Advanced Driving Assistance Systems) GNSS location access for the
+     * given user. This only has meaning on automotive devices.
+     *
+     * @param enabled true to enable ADAS location and false to disable ADAS location.
+     *
+     * @hide
+     */
+    // TODO: @SystemApi
+    @RequiresPermission(WRITE_SECURE_SETTINGS)
+    public void setAdasGnssLocationEnabled(boolean enabled) {
+        try {
+            mService.setAdasGnssLocationEnabledForUser(enabled, mContext.getUser().getIdentifier());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns the current enabled/disabled status of the given provider. To listen for changes, see
      * {@link #PROVIDERS_CHANGED_ACTION}.
      *
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index a3842a1..b48e596 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -194,6 +194,7 @@
     private float mMinUpdateDistanceMeters;
     private final long mMaxUpdateDelayMillis;
     private boolean mHideFromAppOps;
+    private final boolean mAdasGnssBypass;
     private boolean mLocationSettingsIgnored;
     private boolean mLowPower;
     private @Nullable WorkSource mWorkSource;
@@ -236,7 +237,7 @@
         if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
             quality = POWER_NONE;
         } else if (LocationManager.GPS_PROVIDER.equals(provider)) {
-            quality = ACCURACY_FINE;
+            quality = QUALITY_HIGH_ACCURACY;
         } else {
             quality = POWER_LOW;
         }
@@ -289,6 +290,7 @@
             float minUpdateDistanceMeters,
             long maxUpdateDelayMillis,
             boolean hiddenFromAppOps,
+            boolean adasGnssBypass,
             boolean locationSettingsIgnored,
             boolean lowPower,
             WorkSource workSource) {
@@ -302,8 +304,9 @@
         mMinUpdateDistanceMeters = minUpdateDistanceMeters;
         mMaxUpdateDelayMillis = maxUpdateDelayMillis;
         mHideFromAppOps = hiddenFromAppOps;
-        mLowPower = lowPower;
+        mAdasGnssBypass = adasGnssBypass;
         mLocationSettingsIgnored = locationSettingsIgnored;
+        mLowPower = lowPower;
         mWorkSource = Objects.requireNonNull(workSource);
     }
 
@@ -339,15 +342,15 @@
         switch (quality) {
             case POWER_HIGH:
                 // fall through
-            case ACCURACY_FINE:
+            case QUALITY_HIGH_ACCURACY:
                 mQuality = QUALITY_HIGH_ACCURACY;
                 break;
-            case ACCURACY_BLOCK:
+            case QUALITY_BALANCED_POWER_ACCURACY:
                 mQuality = QUALITY_BALANCED_POWER_ACCURACY;
                 break;
             case POWER_LOW:
                 // fall through
-            case ACCURACY_CITY:
+            case QUALITY_LOW_POWER:
                 mQuality = QUALITY_LOW_POWER;
                 break;
             case POWER_NONE:
@@ -648,6 +651,21 @@
     }
 
     /**
+     * Returns true if this request may access GNSS even if location settings would normally deny
+     * this, in order to enable automotive safety features. This field is only respected on
+     * automotive devices, and only if the client is recognized as a legitimate ADAS (Advanced
+     * Driving Assistance Systems) application.
+     *
+     * @return true if all limiting factors will be ignored to satisfy GNSS request
+     *
+     * @hide
+     */
+    // TODO: @SystemApi
+    public boolean isAdasGnssBypass() {
+        return mAdasGnssBypass;
+    }
+
+    /**
      * @hide
      * @deprecated LocationRequests should be treated as immutable.
      */
@@ -673,6 +691,15 @@
     }
 
     /**
+     * Returns true if any bypass flag is set on this request. For internal use only.
+     *
+     * @hide
+     */
+    public boolean isBypass() {
+        return mAdasGnssBypass || mLocationSettingsIgnored;
+    }
+
+    /**
      * @hide
      * @deprecated LocationRequests should be treated as immutable.
      */
@@ -749,6 +776,7 @@
                             /* minUpdateDistanceMeters= */ in.readFloat(),
                             /* maxUpdateDelayMillis= */ in.readLong(),
                             /* hiddenFromAppOps= */ in.readBoolean(),
+                            /* adasGnssBypass= */ in.readBoolean(),
                             /* locationSettingsIgnored= */ in.readBoolean(),
                             /* lowPower= */ in.readBoolean(),
                             /* workSource= */ in.readTypedObject(WorkSource.CREATOR));
@@ -777,6 +805,7 @@
         parcel.writeFloat(mMinUpdateDistanceMeters);
         parcel.writeLong(mMaxUpdateDelayMillis);
         parcel.writeBoolean(mHideFromAppOps);
+        parcel.writeBoolean(mAdasGnssBypass);
         parcel.writeBoolean(mLocationSettingsIgnored);
         parcel.writeBoolean(mLowPower);
         parcel.writeTypedObject(mWorkSource, 0);
@@ -801,6 +830,7 @@
                 && Float.compare(that.mMinUpdateDistanceMeters, mMinUpdateDistanceMeters) == 0
                 && mMaxUpdateDelayMillis == that.mMaxUpdateDelayMillis
                 && mHideFromAppOps == that.mHideFromAppOps
+                && mAdasGnssBypass == that.mAdasGnssBypass
                 && mLocationSettingsIgnored == that.mLocationSettingsIgnored
                 && mLowPower == that.mLowPower
                 && Objects.equals(mProvider, that.mProvider)
@@ -866,8 +896,11 @@
         if (mHideFromAppOps) {
             s.append(", hiddenFromAppOps");
         }
+        if (mAdasGnssBypass) {
+            s.append(", adasGnssBypass");
+        }
         if (mLocationSettingsIgnored) {
-            s.append(", locationSettingsIgnored");
+            s.append(", settingsBypass");
         }
         if (mWorkSource != null && !mWorkSource.isEmpty()) {
             s.append(", ").append(mWorkSource);
@@ -889,6 +922,7 @@
         private float mMinUpdateDistanceMeters;
         private long mMaxUpdateDelayMillis;
         private boolean mHiddenFromAppOps;
+        private boolean mAdasGnssBypass;
         private boolean mLocationSettingsIgnored;
         private boolean mLowPower;
         @Nullable private WorkSource mWorkSource;
@@ -908,6 +942,7 @@
             mMinUpdateDistanceMeters = 0;
             mMaxUpdateDelayMillis = 0;
             mHiddenFromAppOps = false;
+            mAdasGnssBypass = false;
             mLocationSettingsIgnored = false;
             mLowPower = false;
             mWorkSource = null;
@@ -925,6 +960,7 @@
             mMinUpdateDistanceMeters = locationRequest.mMinUpdateDistanceMeters;
             mMaxUpdateDelayMillis = locationRequest.mMaxUpdateDelayMillis;
             mHiddenFromAppOps = locationRequest.mHideFromAppOps;
+            mAdasGnssBypass = locationRequest.mAdasGnssBypass;
             mLocationSettingsIgnored = locationRequest.mLocationSettingsIgnored;
             mLowPower = locationRequest.mLowPower;
             mWorkSource = locationRequest.mWorkSource;
@@ -977,10 +1013,10 @@
         public @NonNull Builder setQuality(@NonNull Criteria criteria) {
             switch (criteria.getAccuracy()) {
                 case Criteria.ACCURACY_COARSE:
-                    mQuality = ACCURACY_BLOCK;
+                    mQuality = QUALITY_BALANCED_POWER_ACCURACY;
                     break;
                 case Criteria.ACCURACY_FINE:
-                    mQuality = ACCURACY_FINE;
+                    mQuality = QUALITY_HIGH_ACCURACY;
                     break;
                 default: {
                     if (criteria.getPowerRequirement() == Criteria.POWER_HIGH) {
@@ -1092,6 +1128,25 @@
         }
 
         /**
+         * If set to true, indicates that the client is an ADAS (Advanced Driving Assistance
+         * Systems) client, which requires access to GNSS even if location settings would normally
+         * deny this, in order to enable auto safety features. This field is only respected on
+         * automotive devices, and only if the client is recognized as a legitimate ADAS
+         * application. Defaults to false.
+         *
+         * <p>Permissions enforcement occurs when resulting location request is actually used, not
+         * when this method is invoked.
+         *
+         * @hide
+         */
+        // TODO: @SystemApi
+        @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+        public @NonNull Builder setAdasGnssBypass(boolean adasGnssBypass) {
+            mAdasGnssBypass = adasGnssBypass;
+            return this;
+        }
+
+        /**
          * If set to true, indicates that location settings, throttling, background location limits,
          * and any other possible limiting factors should be ignored in order to satisfy this
          * request. This is only intended for use in user initiated emergency situations, and
@@ -1171,6 +1226,7 @@
                     mMinUpdateDistanceMeters,
                     mMaxUpdateDelayMillis,
                     mHiddenFromAppOps,
+                    mAdasGnssBypass,
                     mLocationSettingsIgnored,
                     mLowPower,
                     new WorkSource(mWorkSource));
diff --git a/location/java/android/location/provider/ProviderRequest.java b/location/java/android/location/provider/ProviderRequest.java
index b72d365..4f33a52 100644
--- a/location/java/android/location/provider/ProviderRequest.java
+++ b/location/java/android/location/provider/ProviderRequest.java
@@ -44,12 +44,19 @@
     public static final long INTERVAL_DISABLED = Long.MAX_VALUE;
 
     public static final @NonNull ProviderRequest EMPTY_REQUEST = new ProviderRequest(
-            INTERVAL_DISABLED, QUALITY_BALANCED_POWER_ACCURACY, 0, false, false, new WorkSource());
+            INTERVAL_DISABLED,
+            QUALITY_BALANCED_POWER_ACCURACY,
+            0,
+            false,
+            false,
+            false,
+            new WorkSource());
 
     private final long mIntervalMillis;
     private final @Quality int mQuality;
     private final long mMaxUpdateDelayMillis;
     private final boolean mLowPower;
+    private final boolean mAdasGnssBypass;
     private final boolean mLocationSettingsIgnored;
     private final WorkSource mWorkSource;
 
@@ -72,12 +79,14 @@
             @Quality int quality,
             long maxUpdateDelayMillis,
             boolean lowPower,
+            boolean adasGnssBypass,
             boolean locationSettingsIgnored,
             @NonNull WorkSource workSource) {
         mIntervalMillis = intervalMillis;
         mQuality = quality;
         mMaxUpdateDelayMillis = maxUpdateDelayMillis;
         mLowPower = lowPower;
+        mAdasGnssBypass = adasGnssBypass;
         mLocationSettingsIgnored = locationSettingsIgnored;
         mWorkSource = Objects.requireNonNull(workSource);
     }
@@ -126,6 +135,18 @@
     }
 
     /**
+     * Returns true if this request may access GNSS even if location settings would normally deny
+     * this, in order to enable automotive safety features. This field is only respected on
+     * automotive devices, and only if the client is recognized as a legitimate ADAS (Advanced
+     * Driving Assistance Systems) application.
+     *
+     * @hide
+     */
+    public boolean isAdasGnssBypass() {
+        return mAdasGnssBypass;
+    }
+
+    /**
      * Whether the provider should ignore all location settings, user consents, power restrictions
      * or any other restricting factors and always satisfy this request to the best of their
      * ability. This should only be used in case of a user initiated emergency.
@@ -135,6 +156,15 @@
     }
 
     /**
+     * Returns true if any bypass flag is set on this request.
+     *
+     * @hide
+     */
+    public boolean isBypass() {
+        return mAdasGnssBypass || mLocationSettingsIgnored;
+    }
+
+    /**
      * The power blame for this provider request.
      */
     public @NonNull WorkSource getWorkSource() {
@@ -153,6 +183,7 @@
                         /* quality= */ in.readInt(),
                         /* maxUpdateDelayMillis= */ in.readLong(),
                         /* lowPower= */ in.readBoolean(),
+                        /* adasGnssBypass= */ in.readBoolean(),
                         /* locationSettingsIgnored= */ in.readBoolean(),
                         /* workSource= */ in.readTypedObject(WorkSource.CREATOR));
             }
@@ -176,6 +207,7 @@
             parcel.writeInt(mQuality);
             parcel.writeLong(mMaxUpdateDelayMillis);
             parcel.writeBoolean(mLowPower);
+            parcel.writeBoolean(mAdasGnssBypass);
             parcel.writeBoolean(mLocationSettingsIgnored);
             parcel.writeTypedObject(mWorkSource, flags);
         }
@@ -198,6 +230,7 @@
                     && mQuality == that.mQuality
                     && mMaxUpdateDelayMillis == that.mMaxUpdateDelayMillis
                     && mLowPower == that.mLowPower
+                    && mAdasGnssBypass == that.mAdasGnssBypass
                     && mLocationSettingsIgnored == that.mLocationSettingsIgnored
                     && mWorkSource.equals(that.mWorkSource);
         }
@@ -229,8 +262,11 @@
             if (mLowPower) {
                 s.append(", lowPower");
             }
+            if (mAdasGnssBypass) {
+                s.append(", adasGnssBypass");
+            }
             if (mLocationSettingsIgnored) {
-                s.append(", locationSettingsIgnored");
+                s.append(", settingsBypass");
             }
             if (!mWorkSource.isEmpty()) {
                 s.append(", ").append(mWorkSource);
@@ -246,10 +282,12 @@
      * A Builder for {@link ProviderRequest}s.
      */
     public static final class Builder {
+
         private long mIntervalMillis = INTERVAL_DISABLED;
         private int mQuality = QUALITY_BALANCED_POWER_ACCURACY;
         private long mMaxUpdateDelayMillis = 0;
         private boolean mLowPower;
+        private boolean mAdasGnssBypass;
         private boolean mLocationSettingsIgnored;
         private WorkSource mWorkSource = new WorkSource();
 
@@ -299,6 +337,16 @@
         }
 
         /**
+         * Sets whether this ADAS request should bypass GNSS settings. False by default.
+         *
+         * @hide
+         */
+        public @NonNull Builder setAdasGnssBypass(boolean adasGnssBypass) {
+            this.mAdasGnssBypass = adasGnssBypass;
+            return this;
+        }
+
+        /**
          * Sets whether location settings should be ignored. False by default.
          */
         public @NonNull Builder setLocationSettingsIgnored(boolean locationSettingsIgnored) {
@@ -326,6 +374,7 @@
                         mQuality,
                         mMaxUpdateDelayMillis,
                         mLowPower,
+                        mAdasGnssBypass,
                         mLocationSettingsIgnored,
                         mWorkSource);
             }
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index 860d88a..d8f48c2 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -101,6 +101,8 @@
     private int mBatteryLevel;
     private int mBatteryScale;
     private int mDeviceType;
+    private String mHostType;
+    private boolean mSkipThumbForHost = false;
 
     private MtpServer mServer;
     private MtpStorageManager mManager;
@@ -192,6 +194,7 @@
             MtpConstants.DEVICE_PROPERTY_IMAGE_SIZE,
             MtpConstants.DEVICE_PROPERTY_BATTERY_LEVEL,
             MtpConstants.DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE,
+            MtpConstants.DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO,
     };
 
     @VisibleForNative
@@ -408,6 +411,8 @@
             }
             context.deleteDatabase(devicePropertiesName);
         }
+        mHostType = "";
+        mSkipThumbForHost = false;
     }
 
     @VisibleForNative
@@ -672,12 +677,24 @@
 
     @VisibleForNative
     private int getDeviceProperty(int property, long[] outIntValue, char[] outStringValue) {
+        int length;
+        String value;
+
         switch (property) {
             case MtpConstants.DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
             case MtpConstants.DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
                 // writable string properties kept in shared preferences
-                String value = mDeviceProperties.getString(Integer.toString(property), "");
-                int length = value.length();
+                value = mDeviceProperties.getString(Integer.toString(property), "");
+                length = value.length();
+                if (length > 255) {
+                    length = 255;
+                }
+                value.getChars(0, length, outStringValue, 0);
+                outStringValue[length] = 0;
+                return MtpConstants.RESPONSE_OK;
+            case MtpConstants.DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO:
+                value = mHostType;
+                length = value.length();
                 if (length > 255) {
                     length = 255;
                 }
@@ -717,6 +734,14 @@
                 e.putString(Integer.toString(property), stringValue);
                 return (e.commit() ? MtpConstants.RESPONSE_OK
                         : MtpConstants.RESPONSE_GENERAL_ERROR);
+            case MtpConstants.DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO:
+                mHostType = stringValue;
+                if (stringValue.startsWith("Android/")) {
+                    Log.d(TAG, "setDeviceProperty." + Integer.toHexString(property)
+                            + "=" + stringValue);
+                    mSkipThumbForHost = true;
+                }
+                return MtpConstants.RESPONSE_OK;
         }
 
         return MtpConstants.RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
@@ -838,6 +863,10 @@
                     outLongs[0] = thumbOffsetAndSize != null ? thumbOffsetAndSize[1] : 0;
                     outLongs[1] = exif.getAttributeInt(ExifInterface.TAG_PIXEL_X_DIMENSION, 0);
                     outLongs[2] = exif.getAttributeInt(ExifInterface.TAG_PIXEL_Y_DIMENSION, 0);
+                    if (mSkipThumbForHost) {
+                        Log.d(TAG, "getThumbnailInfo: Skip runtime thumbnail.");
+                        return true;
+                    }
                     if (exif.getThumbnailRange() != null) {
                         if ((outLongs[0] == 0) || (outLongs[1] == 0) || (outLongs[2] == 0)) {
                             Log.d(TAG, "getThumbnailInfo: check thumb info:"
@@ -880,6 +909,10 @@
                 try {
                     ExifInterface exif = new ExifInterface(path);
 
+                    if (mSkipThumbForHost) {
+                        Log.d(TAG, "getThumbnailData: Skip runtime thumbnail.");
+                        return exif.getThumbnail();
+                    }
                     if (exif.getThumbnailRange() != null)
                         return exif.getThumbnail();
                 } catch (IOException e) {
diff --git a/media/java/android/mtp/MtpDevice.java b/media/java/android/mtp/MtpDevice.java
index e8b04ed..ec92591 100644
--- a/media/java/android/mtp/MtpDevice.java
+++ b/media/java/android/mtp/MtpDevice.java
@@ -170,6 +170,18 @@
     }
 
     /**
+     * Set device property SESSION_INITIATOR_VERSION_INFO
+     *
+     * @param propertyStr string value for device property SESSION_INITIATOR_VERSION_INFO
+     * @return -1 for error, 0 for success
+     *
+     * {@hide}
+     */
+    public int setDevicePropertyInitVersion(@NonNull String propertyStr) {
+        return native_set_device_property_init_version(propertyStr);
+    }
+
+    /**
      * Returns the list of IDs for all storage units on this device
      * Information about each storage unit can be accessed via {@link #getStorageInfo}.
      *
@@ -421,6 +433,7 @@
     private native boolean native_open(String deviceName, int fd);
     private native void native_close();
     private native MtpDeviceInfo native_get_device_info();
+    private native int native_set_device_property_init_version(String propertyStr);
     private native int[] native_get_storage_ids();
     private native MtpStorageInfo native_get_storage_info(int storageId);
     private native int[] native_get_object_handles(int storageId, int format, int objectHandle);
diff --git a/media/java/android/mtp/MtpDeviceInfo.java b/media/java/android/mtp/MtpDeviceInfo.java
index 0304ee3..8851451 100644
--- a/media/java/android/mtp/MtpDeviceInfo.java
+++ b/media/java/android/mtp/MtpDeviceInfo.java
@@ -31,6 +31,7 @@
     private String mSerialNumber;
     private int[] mOperationsSupported;
     private int[] mEventsSupported;
+    private int[] mDevicePropertySupported;
 
     // only instantiated via JNI
     private MtpDeviceInfo() {
@@ -144,6 +145,21 @@
     }
 
     /**
+     * Returns Device property code supported by the device.
+     *
+     * @return supported Device property code. Can be null if device does not provide the property.
+     *
+     * @see MtpConstants#DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER
+     * @see MtpConstants#DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME
+     * @see MtpConstants#DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO
+     *
+     * {@hide}
+     */
+    public final @NonNull int[] getDevicePropertySupported() {
+        return mDevicePropertySupported;
+    }
+
+    /**
      * Returns if the given operation is supported by the device or not.
      * @param code Operation code.
      * @return If the given operation is supported by the device or not.
@@ -162,6 +178,17 @@
     }
 
     /**
+     * Returns if the given Device property is supported by the device or not.
+     * @param code Device property code.
+     * @return If the given Device property is supported by the device or not.
+     *
+     * {@hide}
+     */
+    public boolean isDevicePropertySupported(int code) {
+        return isSupported(mDevicePropertySupported, code);
+    }
+
+    /**
      * Returns if the code set contains code.
      * @hide
      */
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index ffed474..a77bc9f 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -1131,6 +1131,7 @@
     {   MTP_DEVICE_PROPERTY_IMAGE_SIZE,                 MTP_TYPE_STR },
     {   MTP_DEVICE_PROPERTY_BATTERY_LEVEL,              MTP_TYPE_UINT8 },
     {   MTP_DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE,      MTP_TYPE_UINT32 },
+    {   MTP_DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO,     MTP_TYPE_STR },
 };
 
 bool MtpDatabase::getObjectPropertyInfo(MtpObjectProperty property, int& type) {
@@ -1289,6 +1290,7 @@
         switch (property) {
             case MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
             case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
+            case MTP_DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO:
                 writable = true;
                 // fall through
                 FALLTHROUGH_INTENDED;
diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp
index 3d2b00fe..ac89fecd 100644
--- a/media/jni/android_mtp_MtpDevice.cpp
+++ b/media/jni/android_mtp_MtpDevice.cpp
@@ -72,6 +72,7 @@
 static jfieldID field_deviceInfo_serialNumber;
 static jfieldID field_deviceInfo_operationsSupported;
 static jfieldID field_deviceInfo_eventsSupported;
+static jfieldID field_deviceInfo_devicePropertySupported;
 
 // MtpStorageInfo fields
 static jfieldID field_storageInfo_storageId;
@@ -129,6 +130,8 @@
             GetFieldIDOrDie(env, clazz_deviceInfo, "mOperationsSupported", "[I");
         field_deviceInfo_eventsSupported =
             GetFieldIDOrDie(env, clazz_deviceInfo, "mEventsSupported", "[I");
+        field_deviceInfo_devicePropertySupported =
+            GetFieldIDOrDie(env, clazz_deviceInfo, "mDevicePropertySupported", "[I");
 
         clazz_storageInfo =
             (jclass)env->NewGlobalRef(FindClassOrDie(env, "android/mtp/MtpStorageInfo"));
@@ -377,9 +380,65 @@
         }
     }
 
+    assert(deviceInfo->mDeviceProperties);
+    {
+        const size_t size = deviceInfo->mDeviceProperties->size();
+        ScopedLocalRef<jintArray> events(env, static_cast<jintArray>(env->NewIntArray(size)));
+        {
+            ScopedIntArrayRW elements(env, events.get());
+            if (elements.get() == NULL) {
+                ALOGE("Could not create devicePropertySupported element.");
+                return NULL;
+            }
+            for (size_t i = 0; i < size; ++i) {
+                elements[i] = static_cast<int>(deviceInfo->mDeviceProperties->at(i));
+            }
+            env->SetObjectField(info, field_deviceInfo_devicePropertySupported, events.get());
+        }
+    }
+
     return info;
 }
 
+static jint
+android_mtp_MtpDevice_set_device_property_init_version(JNIEnv *env, jobject thiz,
+                                                       jstring property_str) {
+    MtpDevice* const device = get_device_from_object(env, thiz);
+
+    if (!device) {
+        ALOGD("%s device is null\n", __func__);
+        env->ThrowNew(clazz_io_exception, "Failed to obtain MtpDevice.");
+        return -1;
+    }
+
+    const char *propertyStr = env->GetStringUTFChars(property_str, NULL);
+    if (propertyStr == NULL) {
+        return -1;
+    }
+
+    MtpProperty* property = new MtpProperty(MTP_DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO,
+                                            MTP_TYPE_STR, true);
+    if (!property) {
+        env->ThrowNew(clazz_io_exception, "Failed to obtain property.");
+        return -1;
+    }
+
+    if (property->getDataType() != MTP_TYPE_STR) {
+        env->ThrowNew(clazz_io_exception, "Unexpected property data type.");
+        return -1;
+    }
+
+    property->setCurrentValue(propertyStr);
+    if (!device->setDevicePropValueStr(property)) {
+        env->ThrowNew(clazz_io_exception, "Failed to obtain property value.");
+        return -1;
+    }
+
+    env->ReleaseStringUTFChars(property_str, propertyStr);
+
+    return 0;
+}
+
 static jintArray
 android_mtp_MtpDevice_get_storage_ids(JNIEnv *env, jobject thiz)
 {
@@ -847,6 +906,8 @@
     {"native_close",            "()V",  (void *)android_mtp_MtpDevice_close},
     {"native_get_device_info",  "()Landroid/mtp/MtpDeviceInfo;",
                                         (void *)android_mtp_MtpDevice_get_device_info},
+    {"native_set_device_property_init_version",  "(Ljava/lang/String;)I",
+                        (void *)android_mtp_MtpDevice_set_device_property_init_version},
     {"native_get_storage_ids",  "()[I", (void *)android_mtp_MtpDevice_get_storage_ids},
     {"native_get_storage_info", "(I)Landroid/mtp/MtpStorageInfo;",
                                         (void *)android_mtp_MtpDevice_get_storage_info},
diff --git a/obex/javax/obex/ObexHelper.java b/obex/javax/obex/ObexHelper.java
index 478297f..843793a 100644
--- a/obex/javax/obex/ObexHelper.java
+++ b/obex/javax/obex/ObexHelper.java
@@ -34,6 +34,8 @@
 
 package javax.obex;
 
+import android.util.Log;
+
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
@@ -43,7 +45,6 @@
 import java.util.Date;
 import java.util.TimeZone;
 
-import android.util.Log;
 
 /**
  * This class defines a set of helper methods for the implementation of Obex.
@@ -1083,11 +1084,12 @@
     }
 
     private static int validateMaxPacketSize(int size) {
-        if(VDBG && (size > MAX_PACKET_SIZE_INT)) Log.w(TAG,
-                "The packet size supported for the connection (" + size + ") is larger"
-                + " than the configured OBEX packet size: " + MAX_PACKET_SIZE_INT);
-        if(size != -1) {
-            if(size < LOWER_LIMIT_MAX_PACKET_SIZE) {
+        if (VDBG && (size > MAX_PACKET_SIZE_INT)) {
+            Log.w(TAG, "The packet size supported for the connection (" + size + ") is larger"
+                    + " than the configured OBEX packet size: " + MAX_PACKET_SIZE_INT);
+        }
+        if (size != -1 && size < MAX_PACKET_SIZE_INT) {
+            if (size < LOWER_LIMIT_MAX_PACKET_SIZE) {
                 throw new IllegalArgumentException(size + " is less that the lower limit: "
                         + LOWER_LIMIT_MAX_PACKET_SIZE);
             }
diff --git a/obex/javax/obex/ObexTransport.java b/obex/javax/obex/ObexTransport.java
index a5a75f5..4cef0b3 100644
--- a/obex/javax/obex/ObexTransport.java
+++ b/obex/javax/obex/ObexTransport.java
@@ -81,6 +81,8 @@
      * size. Therefore this value shall not change.
      * For RFCOMM or other transport types where the OBEX packets size
      * is unrelated to the transport packet size, return -1;
+     * Exception can be made (like PBAP transport) with a smaller value
+     * to avoid bad effect on other profiles using the RFCOMM;
      * @return the maximum allowed OBEX packet that can be send over
      *         the transport. Or -1 in case of don't care.
      */
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
index 42c1997..bfc00bb 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
@@ -52,6 +52,7 @@
 import java.io.OutputStream;
 import java.lang.ref.WeakReference;
 import java.util.Arrays;
+import java.util.NoSuchElementException;
 
 public final class RemotePrintDocument {
     private static final String LOG_TAG = "RemotePrintDocument";
@@ -441,7 +442,12 @@
             // Keep going - best effort...
         }
 
-        mPrintDocumentAdapter.asBinder().unlinkToDeath(mDeathRecipient, 0);
+        try {
+            mPrintDocumentAdapter.asBinder().unlinkToDeath(mDeathRecipient, 0);
+        } catch (NoSuchElementException e) {
+            Log.w(LOG_TAG, "Error unlinking print document adapter death recipient.");
+            // Keep going - best effort...
+        }
     }
 
     private void scheduleCommand(AsyncCommand command) {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 59f272f..d25d5dc 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -61,7 +61,6 @@
 import android.print.PrinterInfo;
 import android.printservice.PrintService;
 import android.printservice.PrintServiceInfo;
-import android.provider.DocumentsContract;
 import android.text.Editable;
 import android.text.TextUtils;
 import android.text.TextWatcher;
diff --git a/packages/SettingsLib/ActionButtonsPreference/Android.bp b/packages/SettingsLib/ActionButtonsPreference/Android.bp
index 51c9c39..b6e1677 100644
--- a/packages/SettingsLib/ActionButtonsPreference/Android.bp
+++ b/packages/SettingsLib/ActionButtonsPreference/Android.bp
@@ -14,7 +14,8 @@
     resource_dirs: ["res"],
 
     static_libs: [
-          "androidx.preference_preference",
+        "androidx.preference_preference",
+        "SettingsLibUtils",
     ],
 
     sdk_version: "system_current",
diff --git a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
index 9aa511d..029c919 100644
--- a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
+++ b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
@@ -27,10 +27,11 @@
 
 import androidx.annotation.DrawableRes;
 import androidx.annotation.StringRes;
-import androidx.core.os.BuildCompat;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceViewHolder;
 
+import com.android.settingslib.utils.BuildCompatUtils;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -94,7 +95,7 @@
     public void onBindViewHolder(PreferenceViewHolder holder) {
         super.onBindViewHolder(holder);
 
-        final boolean allowedDivider = !BuildCompat.isAtLeastS();
+        final boolean allowedDivider = !BuildCompatUtils.isAtLeastS();
         holder.setDividerAllowedAbove(allowedDivider);
         holder.setDividerAllowedBelow(allowedDivider);
 
diff --git a/packages/SettingsLib/BannerMessagePreference/Android.bp b/packages/SettingsLib/BannerMessagePreference/Android.bp
index c6a9562..0f7a451 100644
--- a/packages/SettingsLib/BannerMessagePreference/Android.bp
+++ b/packages/SettingsLib/BannerMessagePreference/Android.bp
@@ -16,6 +16,7 @@
     static_libs: [
         "androidx.preference_preference",
         "SettingsLibSettingsTheme",
+        "SettingsLibUtils",
     ],
 
     sdk_version: "system_current",
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-bn/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-bn/strings.xml
new file mode 100644
index 0000000..e0dfcf2
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-bn/strings.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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"বাতিল করুন"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-de/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-de/strings.xml
new file mode 100644
index 0000000..006301b
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-de/strings.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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Schließen"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-mr/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-mr/strings.xml
new file mode 100644
index 0000000..4bd4485
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-mr/strings.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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"डिसमिस करा"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ne/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ne/strings.xml
new file mode 100644
index 0000000..1510254
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ne/strings.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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"हटाउनुहोस्"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-or/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-or/strings.xml
new file mode 100644
index 0000000..36e7d3b
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-or/strings.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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ଖାରଜ କରନ୍ତୁ"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
index 9e39355..3a70d65 100644
--- a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
+++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
@@ -35,10 +35,11 @@
 import androidx.annotation.ColorRes;
 import androidx.annotation.RequiresApi;
 import androidx.annotation.StringRes;
-import androidx.core.os.BuildCompat;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceViewHolder;
 
+import com.android.settingslib.utils.BuildCompatUtils;
+
 /**
  * Banner message is a banner displaying important information (permission request, page error etc),
  * and provide actions for user to address. It requires a user action to be dismissed.
@@ -83,7 +84,7 @@
     }
 
     private static final String TAG = "BannerPreference";
-    private static final boolean IS_AT_LEAST_S = BuildCompat.isAtLeastS();
+    private static final boolean IS_AT_LEAST_S = BuildCompatUtils.isAtLeastS();
 
     private final BannerMessagePreference.ButtonInfo mPositiveButtonInfo =
             new BannerMessagePreference.ButtonInfo();
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
index e6ca2e0..4ae120a 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
@@ -23,8 +23,11 @@
 import android.view.ViewGroup;
 import android.widget.Toolbar;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
 
+import com.google.android.material.appbar.AppBarLayout;
 import com.google.android.material.appbar.CollapsingToolbarLayout;
 import com.google.android.material.resources.TextAppearanceConfig;
 
@@ -35,6 +38,7 @@
 public class CollapsingToolbarBaseActivity extends SettingsTransitionActivity {
 
     private CollapsingToolbarLayout mCollapsingToolbarLayout;
+    private AppBarLayout mAppBarLayout;
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -43,6 +47,8 @@
         TextAppearanceConfig.setShouldLoadFontSynchronously(true);
         super.setContentView(R.layout.collapsing_toolbar_base_layout);
         mCollapsingToolbarLayout = findViewById(R.id.collapsing_toolbar);
+        mAppBarLayout = findViewById(R.id.app_bar);
+        disableCollapsingToolbarLayoutScrollingBehavior();
 
         final Toolbar toolbar = findViewById(R.id.action_bar);
         setActionBar(toolbar);
@@ -107,4 +113,18 @@
     public CollapsingToolbarLayout getCollapsingToolbarLayout() {
         return mCollapsingToolbarLayout;
     }
+
+    private void disableCollapsingToolbarLayoutScrollingBehavior() {
+        final CoordinatorLayout.LayoutParams params =
+                (CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams();
+        final AppBarLayout.Behavior behavior = new AppBarLayout.Behavior();
+        behavior.setDragCallback(
+                new AppBarLayout.Behavior.DragCallback() {
+                    @Override
+                    public boolean canDrag(@NonNull AppBarLayout appBarLayout) {
+                        return false;
+                    }
+                });
+        params.setBehavior(behavior);
+    }
 }
diff --git a/packages/SettingsLib/FooterPreference/res/drawable/ic_info_outline_24.xml b/packages/SettingsLib/FooterPreference/res/drawable/settingslib_ic_info_outline_24.xml
similarity index 100%
rename from packages/SettingsLib/FooterPreference/res/drawable/ic_info_outline_24.xml
rename to packages/SettingsLib/FooterPreference/res/drawable/settingslib_ic_info_outline_24.xml
diff --git a/packages/SettingsLib/FooterPreference/res/values-bn/strings.xml b/packages/SettingsLib/FooterPreference/res/values-bn/strings.xml
new file mode 100644
index 0000000..c58142d
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-bn/strings.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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="settingslib_learn_more_text" msgid="7385478101223578464">"আরও জানুন"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-de/strings.xml b/packages/SettingsLib/FooterPreference/res/values-de/strings.xml
new file mode 100644
index 0000000..fe885aa
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-de/strings.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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Weitere Informationen"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-mr/strings.xml b/packages/SettingsLib/FooterPreference/res/values-mr/strings.xml
new file mode 100644
index 0000000..4538720
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-mr/strings.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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="settingslib_learn_more_text" msgid="7385478101223578464">"अधिक जाणून घ्या"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-ne/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ne/strings.xml
new file mode 100644
index 0000000..ecfec36
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-ne/strings.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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="settingslib_learn_more_text" msgid="7385478101223578464">"थप जान्नुहोस्"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-or/strings.xml b/packages/SettingsLib/FooterPreference/res/values-or/strings.xml
new file mode 100644
index 0000000..e7924d6
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-or/strings.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 xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ଅଧିକ ଜାଣନ୍ତୁ"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
index 005c75c..8216edf 100644
--- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
+++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
@@ -149,7 +149,7 @@
     private void init() {
         setLayoutResource(R.layout.preference_footer);
         if (getIcon() == null) {
-            setIcon(R.drawable.ic_info_outline_24);
+            setIcon(R.drawable.settingslib_ic_info_outline_24);
         }
         setOrder(ORDER_FOOTER);
         if (TextUtils.isEmpty(getKey())) {
diff --git a/packages/SettingsLib/MainSwitchPreference/Android.bp b/packages/SettingsLib/MainSwitchPreference/Android.bp
index 23ee49e..76d1ea7 100644
--- a/packages/SettingsLib/MainSwitchPreference/Android.bp
+++ b/packages/SettingsLib/MainSwitchPreference/Android.bp
@@ -16,8 +16,9 @@
     static_libs: [
         "androidx.preference_preference",
         "SettingsLibSettingsTheme",
+        "SettingsLibUtils",
     ],
 
     sdk_version: "system_current",
-    min_sdk_version: "21",
+    min_sdk_version: "28",
 }
diff --git a/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml b/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml
index 2607529..5817f77 100644
--- a/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml
+++ b/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml
@@ -19,7 +19,7 @@
           package="com.android.settingslib.widget">
 
     <uses-sdk
-        android:minSdkVersion="21"
+        android:minSdkVersion="28"
         android:targetSdkVersion="31"/>
 
 </manifest>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml b/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml
index 0c95a9e..57b8446 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml
@@ -16,7 +16,6 @@
   -->
 
 <resources xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
-    <color name="settingslib_switchbar_background_color">@*android:color/material_grey_600</color>
     <color name="settingslib_switchbar_switch_track_tint">#BFFFFFFF</color>
     <color name="settingslib_switchbar_switch_thumb_tint">@android:color/white</color>
 </resources>
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 91e1b93..5f47be4 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
@@ -30,7 +30,8 @@
 import android.widget.TextView;
 
 import androidx.annotation.ColorInt;
-import androidx.core.os.BuildCompat;
+
+import com.android.settingslib.utils.BuildCompatUtils;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -74,11 +75,11 @@
 
         LayoutInflater.from(context).inflate(R.layout.settingslib_main_switch_bar, this);
 
-        if (!BuildCompat.isAtLeastS()) {
+        if (!BuildCompatUtils.isAtLeastS()) {
             final TypedArray a = context.obtainStyledAttributes(
                     new int[]{android.R.attr.colorAccent});
             mBackgroundActivatedColor = a.getColor(0, 0);
-            mBackgroundColor = context.getColor(R.color.settingslib_switchbar_background_color);
+            mBackgroundColor = context.getColor(R.color.material_grey_600);
             a.recycle();
         }
 
@@ -88,11 +89,12 @@
         mFrameView = findViewById(R.id.frame);
         mTextView = (TextView) findViewById(R.id.switch_text);
         mSwitch = (Switch) findViewById(android.R.id.switch_widget);
-        mBackgroundOn = getContext().getDrawable(R.drawable.settingslib_switch_bar_bg_on);
-        mBackgroundOff = getContext().getDrawable(R.drawable.settingslib_switch_bar_bg_off);
-        mBackgroundDisabled = getContext().getDrawable(
-                R.drawable.settingslib_switch_bar_bg_disabled);
-
+        if (BuildCompatUtils.isAtLeastS()) {
+            mBackgroundOn = getContext().getDrawable(R.drawable.settingslib_switch_bar_bg_on);
+            mBackgroundOff = getContext().getDrawable(R.drawable.settingslib_switch_bar_bg_off);
+            mBackgroundDisabled = getContext().getDrawable(
+                    R.drawable.settingslib_switch_bar_bg_disabled);
+        }
         addOnSwitchChangeListener((switchView, isChecked) -> setChecked(isChecked));
 
         setChecked(mSwitch.isChecked());
@@ -194,9 +196,7 @@
      * Remove a listener for switch changes
      */
     public void removeOnSwitchChangeListener(OnMainSwitchChangeListener listener) {
-        if (mSwitchChangeListeners.contains(listener)) {
-            mSwitchChangeListeners.remove(listener);
-        }
+        mSwitchChangeListeners.remove(listener);
     }
 
     /**
@@ -207,7 +207,7 @@
         mTextView.setEnabled(enabled);
         mSwitch.setEnabled(enabled);
 
-        if (BuildCompat.isAtLeastS()) {
+        if (BuildCompatUtils.isAtLeastS()) {
             if (enabled) {
                 mFrameView.setBackground(isChecked() ? mBackgroundOn : mBackgroundOff);
             } else {
@@ -226,7 +226,7 @@
     }
 
     private void setBackground(boolean isChecked) {
-        if (!BuildCompat.isAtLeastS()) {
+        if (!BuildCompatUtils.isAtLeastS()) {
             setBackgroundColor(isChecked ? mBackgroundActivatedColor : mBackgroundColor);
         } else {
             mFrameView.setBackground(isChecked ? mBackgroundOn : mBackgroundOff);
@@ -267,10 +267,12 @@
 
         public static final Parcelable.Creator<SavedState> CREATOR =
                 new Parcelable.Creator<SavedState>() {
+                    @Override
                     public SavedState createFromParcel(Parcel in) {
                         return new SavedState(in);
                     }
 
+                    @Override
                     public SavedState[] newArray(int size) {
                         return new SavedState[size];
                     }
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 304c343..d993e44 100644
--- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
+++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
@@ -22,6 +22,7 @@
 import android.widget.AdapterView;
 
 import androidx.preference.Preference;
+import androidx.preference.Preference.OnPreferenceClickListener;
 import androidx.preference.PreferenceViewHolder;
 
 import com.android.settingslib.widget.settingsspinner.SettingsSpinner;
@@ -31,12 +32,12 @@
  * This preference uses SettingsSpinner & SettingsSpinnerAdapter which provide default layouts for
  * both view and drop down view of the Spinner.
  */
-public class SettingsSpinnerPreference extends Preference {
+public class SettingsSpinnerPreference extends Preference implements OnPreferenceClickListener {
 
     private SettingsSpinnerAdapter mAdapter;
     private AdapterView.OnItemSelectedListener mListener;
-    private int mPosition; //Default 0 for internal shard storage.
-    private boolean mIsClickable = true;
+    private int mPosition;
+    private boolean mShouldPerformClick;
 
     /**
      * Perform inflation from XML and apply a class-specific base style.
@@ -51,7 +52,7 @@
     public SettingsSpinnerPreference(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
         setLayoutResource(R.layout.settings_spinner_preference);
-        setSelectable(false);
+        setOnPreferenceClickListener(this);
     }
 
     /**
@@ -64,7 +65,7 @@
     public SettingsSpinnerPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
         setLayoutResource(R.layout.settings_spinner_preference);
-        setSelectable(false);
+        setOnPreferenceClickListener(this);
     }
 
     /**
@@ -76,6 +77,13 @@
         this(context, null);
     }
 
+    @Override
+    public boolean onPreferenceClick(Preference preference) {
+        mShouldPerformClick = true;
+        notifyChanged();
+        return true;
+    }
+
     /** Sets adapter of the spinner. */
     public <T extends SettingsSpinnerAdapter> void setAdapter(T adapter) {
         mAdapter = adapter;
@@ -101,24 +109,19 @@
         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);
+        if (mShouldPerformClick) {
+            mShouldPerformClick = false;
+            // To show dropdown view.
+            spinner.performClick();
+        }
     }
 
     private final AdapterView.OnItemSelectedListener mOnSelectedListener =
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
index aed6338..b78da90 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
@@ -34,7 +34,7 @@
     <!-- Dialog accent color -->
     <color name="settingslib_dialog_accent">@android:color/system_accent1_600</color>
     <!-- Dialog background color -->
-    <color name="settingslib_dialog_background">@android:color/system_neutral1_800</color>
+    <color name="settingslib_dialog_background">@*android:color/surface_light</color>
     <!-- Dialog error color. -->
     <color name="settingslib_dialog_colorError">#d93025</color> <!-- Red 600 -->
 
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
index 9610c94..46f1e03 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
@@ -25,6 +25,7 @@
 
     <style name="Switch.SettingsLib" parent="@android:style/Widget.Material.CompoundButton.Switch">
         <item name="android:switchMinWidth">52dp</item>
+        <item name="android:minHeight">@dimen/settingslib_preferred_minimum_touch_target</item>
         <item name="android:track">@drawable/settingslib_switch_track</item>
         <item name="android:thumb">@drawable/settingslib_switch_thumb</item>
     </style>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
index 6b18f28..8034710 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
@@ -42,7 +42,7 @@
     </style>
 
     <style name="Theme.AlertDialog.SettingsLib" parent="@style/Theme.AppCompat.DayNight.Dialog.Alert">
-        <item name="colorAccent">@*android:color/accent_device_default_light</item>
+        <item name="colorAccent">@color/settingslib_dialog_accent</item>
         <item name="android:colorError">@color/settingslib_dialog_colorError</item>
         <item name="android:colorBackground">@color/settingslib_dialog_background</item>
 
diff --git a/packages/SettingsLib/Utils/AndroidManifest.xml b/packages/SettingsLib/Utils/AndroidManifest.xml
index 96d9e51..fd89676 100644
--- a/packages/SettingsLib/Utils/AndroidManifest.xml
+++ b/packages/SettingsLib/Utils/AndroidManifest.xml
@@ -16,7 +16,7 @@
   -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.settingslib.widget">
+          package="com.android.settingslib.utils">
 
     <uses-sdk android:minSdkVersion="21" />
 
diff --git a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java
new file mode 100644
index 0000000..44f6f54
--- /dev/null
+++ b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java
@@ -0,0 +1,61 @@
+/*
+ * 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.utils;
+
+import android.os.Build.VERSION;
+
+/**
+ * An util class to check whether the current OS version is higher or equal to sdk version of
+ * device.
+ */
+public final class BuildCompatUtils {
+
+    /**
+     * Implementation of BuildCompat.isAtLeast*() suitable for use in Settings
+     *
+     * <p>This still should try using BuildCompat.isAtLeastR() as source of truth, but also checking
+     * for VERSION_SDK_INT and VERSION.CODENAME in case when BuildCompat implementation returned
+     * false. Note that both checks should be >= and not = to make sure that when Android version
+     * increases (i.e., from R to S), this does not stop working.
+     *
+     * <p>Supported configurations:
+     *
+     * <ul>
+     *   <li>For current Android release: when new API is not finalized yet (CODENAME = "S", SDK_INT
+     *       = 30|31)
+     *   <li>For current Android release: when new API is finalized (CODENAME = "REL", SDK_INT = 31)
+     *   <li>For next Android release (CODENAME = "T", SDK_INT = 30+)
+     * </ul>
+     *
+     * <p>Note that Build.VERSION_CODES.S cannot be used here until final SDK is available, because
+     * it is equal to Build.VERSION_CODES.CUR_DEVELOPMENT before API finalization.
+     *
+     * @return Whether the current OS version is higher or equal to S.
+     */
+    public static boolean isAtLeastS() {
+        if (VERSION.SDK_INT < 30) {
+            return false;
+        }
+
+        return (VERSION.CODENAME.equals("REL") && VERSION.SDK_INT >= 31)
+                || (VERSION.CODENAME.length() == 1
+                && VERSION.CODENAME.compareTo("S") >= 0
+                && VERSION.CODENAME.compareTo("Z") <= 0);
+    }
+
+    private BuildCompatUtils() {}
+}
diff --git a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java
index 4505dad..5dc0b72 100644
--- a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java
+++ b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java
@@ -22,7 +22,7 @@
 import android.os.UserManager;
 import android.util.Log;
 
-import com.android.settingslib.widget.R;
+import com.android.settingslib.utils.R;
 
 public class AppUtils {
 
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 89bb9e8..fa3f34c 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Bynaam"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Voeg gas by"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Verwyder gas"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Stel gassessie terug"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gas"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Neem \'n foto"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Kies \'n prent"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index bc1bd16..7b4c832 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"ቅጽል ስም"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"እንግዳን አክል"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"እንግዳን አስወግድ"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"እንግዳን ዳግም አስጀምር"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"እንግዳ"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"ፎቶ አንሳ"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"ምስል ይምረጡ"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index f04aaac..611f2c4 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"উপনাম"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ কৰক"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি আঁতৰাওক"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"অতিথিৰ ছেশ্বন ৰিছেট কৰক"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"অতিথি"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"এখন ফট’ তোলক"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"এখন প্ৰতিচ্ছবি বাছনি কৰক"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index b7cf11a..c50a22d 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Ləqəb"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Qonaq əlavə edin"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Qonağı silin"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Qonaq sessiyasını sıfırlayın"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Qonaq"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Foto çəkin"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Şəkil seçin"</string>
@@ -576,7 +575,7 @@
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Deaktiv"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiv"</string>
     <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Bu dəyişikliyin tətbiq edilməsi üçün cihaz yenidən başladılmalıdır. İndi yenidən başladın və ya ləğv edin."</string>
-    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Simli qulaqlıq"</string>
+    <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Naqilli qulaqlıq"</string>
     <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aktiv"</string>
     <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Deaktiv"</string>
     <string name="carrier_network_change_mode" msgid="4257621815706644026">"Operator şəbəkəsinin dəyişilməsi"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 97c281a..e26d065 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -567,8 +567,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Nadimak"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Resetuj sesiju gosta"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Slikaj"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberi sliku"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 09e5843..7899fa6 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Псевдоним"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Добавяне на гост"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Премахване на госта"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Нулиране на сесията като гост"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Гост"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Правене на снимка"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Избиране на изображение"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 88abe33..9eb20f9 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -567,8 +567,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Nadimak"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Poništi sesiju gosta"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Snimite fotografiju"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberite sliku"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index f499ab6..d3590bf 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Àlies"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Afegeix un convidat"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Suprimeix el convidat"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Restableix el convidat"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Convidat"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Fes una foto"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Tria una imatge"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 4d07eff..5c664b9 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -568,8 +568,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Přezdívka"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Přidat hosta"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Odstranit hosta"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Resetovat hosta"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Host"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Pořídit fotku"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Vybrat obrázek"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 98f5301..59bfab8 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Ψευδώνυμο"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Προσθήκη επισκέπτη"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Κατάργηση επισκέπτη"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Επαναφορά περιόδου επισκέπτη"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Επισκέπτης"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Λήψη φωτογραφίας"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Επιλογή εικόνας"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 6ff4854..26c7b2c 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Apodo"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Añadir invitado"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Restablecer invitado"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Invitado"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Hacer foto"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Seleccionar una imagen"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index ef7b7db..194aeaf 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Hüüdnimi"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Lisa külaline"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Eemalda külaline"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Lähtesta külastajaseanss"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Külaline"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Pildistage"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Valige pilt"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 9350610..33e2314 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -528,7 +528,7 @@
     <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Audio-gailu kableduna"</string>
     <string name="help_label" msgid="3528360748637781274">"Laguntza eta iritziak"</string>
     <string name="storage_category" msgid="2287342585424631813">"Biltegiratzea"</string>
-    <string name="shared_data_title" msgid="1017034836800864953">"Partekatutako datuak"</string>
+    <string name="shared_data_title" msgid="1017034836800864953">"Datu partekatuak"</string>
     <string name="shared_data_summary" msgid="5516326713822885652">"Ikusi eta aldatu partekatutako datuak"</string>
     <string name="shared_data_no_blobs_text" msgid="3108114670341737434">"Ez dago erabiltzaile honen datu partekaturik."</string>
     <string name="shared_data_query_failure_text" msgid="3489828881998773687">"Errore bat gertatu da datu partekatuak eskuratzean. Saiatu berriro."</string>
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Goitizena"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Gehitu gonbidatua"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Kendu gonbidatua"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Berrezarri gonbidatuentzako saioa"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gonbidatua"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Atera argazki bat"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Aukeratu irudi bat"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 964162b..a11a00a 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Lempinimi"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Lisää vieras"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Poista vieras"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Nollaa vieras"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Vieras"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Ota kuva"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Valitse kuva"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 73f98ec..dba912e 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Alcume"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Engadir convidado"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar convidado"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Restablecer sesión de convidado"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Tirar foto"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Escoller imaxe"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 5620d94..bc74092 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"प्रचलित नाम"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"मेहमान जोड़ें"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"मेहमान हटाएं"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"मेहमान के तौर पर ब्राउज़ करने का सेशन रीसेट करें"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"मेहमान"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"फ़ोटो खींचें"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"कोई इमेज चुनें"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 0ff35f8..d18d34c 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -567,8 +567,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Nadimak"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Dodavanje gosta"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Uklanjanje gosta"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Poništi gostujuću sesiju"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Fotografiraj"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberi sliku"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 9bd4d12..3ad3590 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Becenév"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Vendég hozzáadása"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Vendég munkamenet eltávolítása"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Vendég munkamenet visszaállítása"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Vendég"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Fotó készítése"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Kép kiválasztása"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 662a673..da92e7d 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Gælunafn"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Bæta gesti við"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Fjarlægja gest"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Endurstilla gestastillingu"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gestur"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Taka mynd"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Velja mynd"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 2c2d11f..6f7565e 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Aggiungi ospite"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Rimuovi ospite"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Reimposta sessione Ospite"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Ospite"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Scatta una foto"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Scegli un\'immagine"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index ddae3c7..70dd9e7 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Лақап ат"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Қонақ қосу"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Қонақты жою"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Қонақ сеансын әдепкі күйге қайтару"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Қонақ"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Фотосуретке түсіру"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Сурет таңдау"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index d970e95..26595f5 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"ឈ្មោះ​ហៅក្រៅ"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"បញ្ចូល​ភ្ញៀវ"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"ដកភ្ញៀវចេញ"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"កំណត់​ភ្ញៀវឡើង​វិញ"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"ភ្ញៀវ"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"ថតរូប"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"ជ្រើសរើស​រូបភាព"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 8bad320..b36ef8d 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"ಅಡ್ಡ ಹೆಸರು"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"ಅತಿಥಿಯನ್ನು ಸೇರಿಸಿ"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"ಅತಿಥಿಯನ್ನು ತೆಗೆದುಹಾಕಿ"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"ಅತಿಥಿಯನ್ನು ಮರುಹೊಂದಿಸಿ"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"ಅತಿಥಿ"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"ಫೋಟೋ ತೆಗೆದುಕೊಳ್ಳಿ"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"ಚಿತ್ರವನ್ನು ಆರಿಸಿ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 3383602..ad0ad1fb 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -568,8 +568,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Slapyvardis"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Pridėti svečią"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Pašalinti svečią"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Iš naujo nustatyti svečią"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Svečias"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Fotografuoti"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Pasirinkti vaizdą"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index d655afc..586de6fc 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -567,8 +567,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Segvārds"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Pievienot viesi"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Noņemt viesi"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Atiestatīt viesa sesiju"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Viesis"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Uzņemt fotoattēlu"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Izvēlēties attēlu"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index bd7f074..0d0f345 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Прекар"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Додајте гостин"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Отстрани гостин"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Ресетирајте го гостинот"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Гостин"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Фотографирајте"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Одберете слика"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 1e80ccd..655be1a7 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"വിളിപ്പേര്"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"അതിഥിയെ ചേർക്കുക"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"അതിഥിയെ നീക്കം ചെയ്യുക"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"അതിഥിയെ റീസെറ്റ് ചെയ്യുക"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"അതിഥി"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"ഒരു ഫോട്ടോ എടുക്കുക"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"ഒരു ചിത്രം തിരഞ്ഞെടുക്കുക"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 40b65ef..07ed369 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Хоч"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Зочин нэмэх"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Зочин хасах"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Зочныг шинэчлэх"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Зочин"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Зураг авах"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Зураг сонгох"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 6a46ede..fe8e837 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"टोपणनाव"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"अतिथी जोडा"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"अतिथी काढून टाका"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"अतिथी सेशन रीसेट करा"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"अतिथी"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"फोटो काढा"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"इमेज निवडा"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 7156a8e..b383ad8 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"နာမည်ပြောင်"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"ဧည့်သည့် ထည့်ရန်"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"ဧည့်သည်ကို ဖယ်ထုတ်ရန်"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"ဧည့်သည်ကို ပြင်ဆင်သတ်မှတ်ရန်"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"ဧည့်သည်"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"ဓာတ်ပုံရိုက်ရန်"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"ပုံရွေးရန်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index bb93a29..84c231f 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Kallenavn"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Legg til en gjest"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gjesten"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Tilbakestill gjest"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gjest"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Ta et bilde"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Velg et bilde"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index a33bbf3..1837eab 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Bijnaam"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Gast toevoegen"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Gast verwijderen"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Gastsessie resetten"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gast"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Foto maken"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Afbeelding kiezen"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 5f410a8..bd77d88 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -568,8 +568,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Pseudonim"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gościa"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Usuń gościa"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Resetuj sesję gościa"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gość"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Zrób zdjęcie"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Wybierz obraz"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 3c8d928..e39b58b 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Apelido"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Redefinir sessão de visitante"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index fc98aa7..a5c0d1d 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Alcunha"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Repor convidado"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 3c8d928..e39b58b 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Apelido"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Redefinir sessão de visitante"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 4db2f31..e1b0390 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -567,8 +567,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Pseudonim"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Adăugați un invitat"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Ștergeți invitatul"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Resetați sesiunea pentru invitați"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Invitat"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Faceți o fotografie"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Alegeți o imagine"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 0bcdc23..1e5fce5 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -568,8 +568,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Псевдоним"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Добавить гостя"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Удалить аккаунт гостя"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Сбросить гостевой сеанс"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Гость"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Сделать снимок"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Выбрать фото"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index ef12ad3..09c0bf9 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -568,8 +568,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Prezývka"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Pridať hosťa"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Odobrať hosťa"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Obnoviť reláciu hosťa"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Hosť"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Odfotiť"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Vybrať obrázok"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index cbdfefc..b698c19 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -568,8 +568,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Vzdevek"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Dodajanje gosta"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Odstranitev gosta"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Ponastavi gosta"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Fotografiranje"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Izberi sliko"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 3859be9..b3c23b6 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -567,8 +567,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Надимак"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Додај госта"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Уклони госта"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Ресетуј сесију госта"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Гост"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Сликај"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Одабери слику"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 7dcca0d..124c063 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Smeknamn"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Lägg till gäst"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Ta bort gäst"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Återställ gästsession"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gäst"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Ta ett foto"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Välj en bild"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index b7aff72..7f67b88 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -452,7 +452,7 @@
     <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7703677921000858479">"แท็บเล็ตอาจปิดเครื่องในไม่ช้า (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
     <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="4374784375644214578">"อุปกรณ์อาจปิดเครื่องในไม่ช้า (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
-    <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"อีก <xliff:g id="TIME">%1$s</xliff:g> จึงจะเต็ม"</string>
+    <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"อีก <xliff:g id="TIME">%1$s</xliff:g>จึงจะเต็ม"</string>
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - อีก <xliff:g id="TIME">%2$s</xliff:g> จึงจะเต็ม"</string>
     <string name="power_charging_limited" msgid="7956120998372505295">"<xliff:g id="LEVEL">%1$s</xliff:g> - จำกัดการชาร์จชั่วคราว"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"ไม่ทราบ"</string>
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"ชื่อเล่น"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"เพิ่มผู้ใช้ชั่วคราว"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"นำผู้ใช้ชั่วคราวออก"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"รีเซ็ตผู้เข้าร่วม"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"ผู้ใช้ชั่วคราว"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"ถ่ายรูป"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"เลือกรูปภาพ"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 6d1f6ab..cc7db29 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Magdagdag ng bisita"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Alisin ang bisita"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"I-reset ang bisita"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Bisita"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Kumuha ng larawan"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Pumili ng larawan"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 82e481b..2cb1076 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -568,8 +568,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Псевдонім"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Додати гостя"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Видалити гостя"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Скинути сеанс у режимі \"Гість\""</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Гість"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Зробити фотографію"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Вибрати зображення"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 7f4e30a..3e11124 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Nik"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Mehmon kiritish"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Mehmonni olib tashlash"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Mehmon seansini tiklash"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Mehmon"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Suratga olish"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Rasm tanlash"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 2fe791a..0fa97e2 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -566,8 +566,7 @@
     <string name="user_nickname" msgid="262624187455825083">"Isiteketiso"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Engeza isivakashi"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Susa isihambeli"</string>
-    <!-- no translation found for guest_reset_guest (6110013010356013758) -->
-    <skip />
+    <string name="guest_reset_guest" msgid="6110013010356013758">"Setha kabusha isivakashi"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Isihambeli"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Thatha isithombe"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Khetha isithombe"</string>
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 b0c5314..53a382a 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,24 +89,4 @@
         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/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 8c092ae..604310a 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -454,6 +454,14 @@
                   android:finishOnCloseSystemDialogs="true">
         </activity>
 
+        <!-- started from SensoryPrivacyService -->
+        <activity android:name=".sensorprivacy.television.TvUnblockSensorActivity"
+                  android:exported="true"
+                  android:permission="android.permission.MANAGE_SENSOR_PRIVACY"
+                  android:theme="@style/BottomSheet"
+                  android:finishOnCloseSystemDialogs="true">
+        </activity>
+
 
         <!-- started from UsbDeviceSettingsManager -->
         <activity android:name=".usb.UsbAccessoryUriActivity"
diff --git a/packages/SystemUI/docs/qs-tiles.md b/packages/SystemUI/docs/qs-tiles.md
index 89c28a0..efcb2de 100644
--- a/packages/SystemUI/docs/qs-tiles.md
+++ b/packages/SystemUI/docs/qs-tiles.md
@@ -306,6 +306,7 @@
     * Add a case to the `switch` with a unique String spec for the chosen tile.
 5. In [SystemUI/res/values/config.xml](/packages/SystemUI/res/values/config.xml), modify `quick_settings_tiles_stock` and add the spec defined in the previous step. If necessary, add it also to `quick_settings_tiles_default`. The first one contains a list of all the tiles that SystemUI knows how to create (to show to the user in the customization screen). The second one contains only the default tiles that the user will experience on a fresh boot or after they reset their tiles.
 6. In [SystemUI/res/values/tiles_states_strings.xml](/packages/SystemUI/res/values/tiles_states_strings.xml), add a new array for your tile. The name has to be `tile_states_<spec>`. Use a good description to help the translators.
+7. In [`SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt`](/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt), add a new element to the map in `SubtitleArrayMapping` corresponding to the resource created in the previous step.
 
 #### Abstract methods in QSTileImpl
 
diff --git a/packages/SystemUI/res/anim/tv_bottom_sheet_button_state_list_animator.xml b/packages/SystemUI/res/anim/tv_bottom_sheet_button_state_list_animator.xml
new file mode 100644
index 0000000..fc3b4ed
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_bottom_sheet_button_state_list_animator.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.
+  -->
+
+<selector
+    xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_focused="true">
+        <set>
+            <objectAnimator
+                android:duration="200"
+                android:propertyName="scaleX"
+                android:valueFrom="1.0"
+                android:valueTo="@dimen/bottom_sheet_button_selection_scaled"
+                android:valueType="floatType"/>
+            <objectAnimator
+                android:duration="200"
+                android:propertyName="scaleY"
+                android:valueFrom="1.0"
+                android:valueTo="@dimen/bottom_sheet_button_selection_scaled"
+                android:valueType="floatType"/>
+        </set>
+    </item>
+    <item android:state_focused="false">
+        <set>
+            <objectAnimator
+                android:duration="200"
+                android:propertyName="scaleX"
+                android:valueFrom="@dimen/bottom_sheet_button_selection_scaled"
+                android:valueTo="1.0"
+                android:valueType="floatType"/>
+            <objectAnimator
+                android:duration="200"
+                android:propertyName="scaleY"
+                android:valueFrom="@dimen/bottom_sheet_button_selection_scaled"
+                android:valueTo="1.0"
+                android:valueType="floatType"/>
+        </set>
+    </item>
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/tv_bottom_sheet_enter.xml b/packages/SystemUI/res/anim/tv_bottom_sheet_enter.xml
new file mode 100644
index 0000000..cace36d
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_bottom_sheet_enter.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.
+  -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:interpolator="@android:interpolator/decelerate_quint">
+    <translate android:fromYDelta="100%"
+               android:toYDelta="0"
+               android:duration="900"/>
+</set>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/tv_bottom_sheet_exit.xml b/packages/SystemUI/res/anim/tv_bottom_sheet_exit.xml
new file mode 100644
index 0000000..f7efe7cd
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_bottom_sheet_exit.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.
+  -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:interpolator="@android:interpolator/decelerate_quint">
+    <translate android:fromYDelta="0"
+               android:toYDelta="100%"
+               android:duration="500"/>
+</set>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/bottom_sheet_button_background_color.xml b/packages/SystemUI/res/color/bottom_sheet_button_background_color.xml
new file mode 100644
index 0000000..9b0bae0
--- /dev/null
+++ b/packages/SystemUI/res/color/bottom_sheet_button_background_color.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.
+  -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_focused="true"
+          android:color="@color/bottom_sheet_button_background_color_focused"/>
+    <item android:color="@color/bottom_sheet_button_background_color_unfocused"/>
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/bottom_sheet_button_text_color.xml b/packages/SystemUI/res/color/bottom_sheet_button_text_color.xml
new file mode 100644
index 0000000..05248f1
--- /dev/null
+++ b/packages/SystemUI/res/color/bottom_sheet_button_text_color.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.
+  -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_focused="true"
+          android:color="@color/bottom_sheet_button_text_color_focused"/>
+    <item android:color="@color/bottom_sheet_button_text_color_unfocused"/>
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/bottom_sheet_background.xml b/packages/SystemUI/res/drawable/bottom_sheet_background.xml
new file mode 100644
index 0000000..87850a0
--- /dev/null
+++ b/packages/SystemUI/res/drawable/bottom_sheet_background.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.
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+    <solid android:color="@color/bottom_sheet_background_color"/>
+    <corners android:radius="@dimen/bottom_sheet_corner_radius"/>
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/bottom_sheet_background_with_blur.xml b/packages/SystemUI/res/drawable/bottom_sheet_background_with_blur.xml
new file mode 100644
index 0000000..cd2aa9c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/bottom_sheet_background_with_blur.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.
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+    <solid android:color="@color/bottom_sheet_background_color_with_blur"/>
+    <corners android:radius="@dimen/bottom_sheet_corner_radius"/>
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/bottom_sheet_button_background.xml b/packages/SystemUI/res/drawable/bottom_sheet_button_background.xml
new file mode 100644
index 0000000..585a6bc
--- /dev/null
+++ b/packages/SystemUI/res/drawable/bottom_sheet_button_background.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.
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+    <solid android:color="@color/bottom_sheet_button_background_color"/>
+    <corners android:radius="@dimen/bottom_sheet_button_corner_radius"/>
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/rounded_bg_full.xml b/packages/SystemUI/res/drawable/rounded_bg_full.xml
index e0d3f63..967c57b 100644
--- a/packages/SystemUI/res/drawable/rounded_bg_full.xml
+++ b/packages/SystemUI/res/drawable/rounded_bg_full.xml
@@ -1,7 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <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="?android:attr/colorBackgroundFloating" />
+    <solid android:color="?androidprv:attr/colorSurface" />
     <corners
         android:bottomLeftRadius="?android:attr/dialogCornerRadius"
         android:topLeftRadius="?android:attr/dialogCornerRadius"
diff --git a/packages/SystemUI/res/layout/media_output_list_item.xml b/packages/SystemUI/res/layout/media_output_list_item.xml
index b563633..16c03e1 100644
--- a/packages/SystemUI/res/layout/media_output_list_item.xml
+++ b/packages/SystemUI/res/layout/media_output_list_item.xml
@@ -81,6 +81,7 @@
                 android:visibility="gone"/>
             <SeekBar
                 android:id="@+id/volume_seekbar"
+                style="@*android:style/Widget.DeviceDefault.SeekBar"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_alignParentBottom="true"/>
diff --git a/packages/SystemUI/res/layout/notif_half_shelf.xml b/packages/SystemUI/res/layout/notif_half_shelf.xml
index 42f14f3..6bc0138 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf.xml
@@ -64,10 +64,12 @@
                     android:layout_gravity="center"
                     android:padding="8dp"
                     android:gravity="center_vertical|start"
-                    android:textSize="15sp"
                     android:ellipsize="end"
-                    android:maxLines="1"
-                    style="@style/TextAppearance.NotificationImportanceChannel" />
+                    android:maxLines="2"
+                    android:textColor="?android:attr/textColorPrimary"
+                    android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+                    android:textSize="16sp"
+                />
 
                 <Switch
                     android:id="@+id/toggle"
@@ -80,18 +82,13 @@
             <View
                 android:layout_width="match_parent"
                 android:layout_height="1dp"
-                android:background="?android:attr/colorAccent"
+                android:background="@*android:color/background_device_default_light"
             />
 
             <!-- ChannelRows get added dynamically -->
 
         </com.android.systemui.statusbar.notification.row.ChannelEditorListView>
-        <!-- divider view -->
-        <View
-            android:layout_width="match_parent"
-            android:layout_height="1dp"
-            android:background="?android:attr/colorAccent"
-        />
+
         <RelativeLayout
             android:id="@+id/bottom_actions"
             android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/notif_half_shelf_row.xml b/packages/SystemUI/res/layout/notif_half_shelf_row.xml
index 87de286..245d157 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf_row.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf_row.xml
@@ -21,61 +21,76 @@
     android:layout_height="wrap_content"
     android:padding="8dp"
     android:clickable="true"
-    android:orientation="horizontal"
-    android:foreground="?android:attr/selectableItemBackground" >
-
-    <!-- This is where an icon would go *if we wanted one* **wink** -->
-    <Space
-        android:id="@+id/icon"
-        android:layout_height="48dp"
-        android:layout_width="48dp"
-        android:layout_gravity="center_vertical"
-        android:padding="8dp"
-    />
-
-    <RelativeLayout
-        android:id="@+id/description_container"
-        android:layout_height="wrap_content"
-        android:layout_width="0dp"
-        android:layout_weight="1"
-        android:layout_gravity="center_vertical"
-        android:gravity="left|center_vertical"
-        android:orientation="vertical"
+    android:orientation="vertical"
+    android:foreground="?android:attr/selectableItemBackground"
     >
-        <TextView
-            android:id="@+id/channel_name"
-            android:layout_height="wrap_content"
-            android:layout_width="wrap_content"
-            android:paddingBottom="0dp"
-            android:paddingStart="8dp"
-            android:paddingEnd="8dp"
-            android:gravity="center_vertical|start"
-            android:textSize="14sp"
-            android:ellipsize="end"
-            android:maxLines="1"
-            style="@style/TextAppearance.NotificationImportanceChannel"
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+
+        <!-- This is where an icon would go *if we wanted one* **wink** -->
+        <Space
+            android:id="@+id/icon"
+            android:layout_height="48dp"
+            android:layout_width="48dp"
+            android:layout_gravity="center_vertical"
+            android:padding="8dp"
         />
 
-        <TextView
-            android:id="@+id/channel_description"
+        <RelativeLayout
+            android:id="@+id/description_container"
             android:layout_height="wrap_content"
-            android:layout_width="wrap_content"
-            android:paddingStart="8dp"
-            android:paddingEnd="8dp"
-            android:gravity="center_vertical|start"
-            android:textSize="14sp"
-            android:ellipsize="end"
-            android:maxLines="1"
-            android:layout_below="@id/channel_name"
-            style="@style/TextAppearance.NotificationImportanceApp"
-        />
-    </RelativeLayout>
+            android:layout_width="0dp"
+            android:layout_weight="1"
+            android:layout_gravity="center_vertical"
+            android:gravity="left|center_vertical"
+            android:orientation="vertical"
+        >
+            <TextView
+                android:id="@+id/channel_name"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:paddingBottom="0dp"
+                android:paddingStart="8dp"
+                android:paddingEnd="8dp"
+                android:gravity="center_vertical|start"
+                android:ellipsize="end"
+                android:maxLines="1"
+                android:fontFamily="@*android:string/config_headlineFontFamily"
+                android:textColor="?android:attr/textColorPrimary"
+                android:textSize="16sp"
+            />
 
-    <Switch
-        android:id="@+id/toggle"
-        android:layout_height="48dp"
-        android:layout_width="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:padding="8dp"
+            <TextView
+                android:id="@+id/channel_description"
+                android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:paddingStart="8dp"
+                android:paddingEnd="8dp"
+                android:gravity="center_vertical|start"
+                android:ellipsize="end"
+                android:maxLines="1"
+                android:layout_below="@id/channel_name"
+                android:fontFamily="@*android:string/config_bodyFontFamily"
+                android:textColor="?android:attr/textColorSecondary"
+                android:textSize="14sp"
+            />
+        </RelativeLayout>
+
+        <Switch
+            android:id="@+id/toggle"
+            android:layout_height="48dp"
+            android:layout_width="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:padding="8dp"
+        />
+    </LinearLayout>
+    <!-- divider view -->
+    <View
+        android:layout_width="match_parent"
+        android:layout_height=".5dp"
+        android:background="@*android:color/background_device_default_light"
     />
 </com.android.systemui.statusbar.notification.row.ChannelRow>
diff --git a/packages/SystemUI/res/layout/tv_bottom_sheet.xml b/packages/SystemUI/res/layout/tv_bottom_sheet.xml
new file mode 100644
index 0000000..b69cdc7
--- /dev/null
+++ b/packages/SystemUI/res/layout/tv_bottom_sheet.xml
@@ -0,0 +1,87 @@
+<?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/bottom_sheet"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical"
+    android:orientation="horizontal"
+    android:minHeight="@dimen/bottom_sheet_min_height"
+    android:paddingHorizontal="@dimen/bottom_sheet_padding_horizontal"
+    android:paddingVertical="@dimen/bottom_sheet_padding_vertical">
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:minWidth="80dp"
+        android:gravity="center"
+        android:orientation="horizontal">
+        <ImageView
+            android:id="@+id/bottom_sheet_icon"
+            android:layout_width="@dimen/bottom_sheet_icon_size"
+            android:layout_height="@dimen/bottom_sheet_icon_size"
+            android:layout_gravity="center_vertical"
+            android:tint="@color/bottom_sheet_icon_color"/>
+        <ImageView
+            android:id="@+id/bottom_sheet_second_icon"
+            android:layout_width="@dimen/bottom_sheet_icon_size"
+            android:layout_height="@dimen/bottom_sheet_icon_size"
+            android:layout_marginStart="@dimen/bottom_sheet_icon_margin"
+            android:layout_gravity="center_vertical"
+            android:tint="@color/bottom_sheet_icon_color"/>
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/bottom_sheet_padding_horizontal"
+        android:layout_weight="1"
+        android:orientation="vertical">
+        <TextView
+            android:id="@+id/bottom_sheet_title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="@dimen/bottom_sheet_title_margin_bottom"
+            android:textAppearance="@style/BottomSheet.TitleText"/>
+
+        <TextView
+            android:id="@+id/bottom_sheet_body"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="@dimen/bottom_sheet_details_margin_bottom"
+            android:textAppearance="@style/BottomSheet.BodyText" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:orientation="vertical"
+        android:layout_width="@dimen/bottom_sheet_actions_width"
+        android:layout_height="match_parent"
+        android:gravity="center">
+        <Button
+            android:id="@+id/bottom_sheet_positive_button"
+            style="@style/BottomSheet.ActionItem" />
+        <Space
+            android:layout_width="0dp"
+            android:layout_height="@dimen/bottom_sheet_actions_spacing" />
+        <Button
+            android:id="@+id/bottom_sheet_negative_button"
+            style="@style/BottomSheet.ActionItem" />
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index c3b96a4..9502454 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -1035,7 +1035,7 @@
     <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Tam ekranı böyüdün"</string>
     <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekran hissəsinin böyüdülməsi"</string>
     <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Dəyişdirici"</string>
-    <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Əlçatımlılıq düyməsi əlçatımlılıq jestini əvəz etdi\n\n"<annotation id="link">"Ayarlara baxın"</annotation></string>
+    <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Jest xüsusi imkanlar düyməsinə dəyişdirildi\n\n"<annotation id="link">"Ayarlara baxın"</annotation></string>
     <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Əlçatımlılıq jestindən düymə\n\n"<annotation id="link">"Ayarlarına"</annotation>" keçə bilərsiniz"</string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Düyməni müvəqqəti gizlətmək üçün kənara çəkin"</string>
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Yuxarıya sola köçürün"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 2e98ad9..5afd979 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -484,12 +484,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"Дадаць карыстальніка"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Новы карыстальнік"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Выдаліць госця?"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Скінуць гасцявы сеанс?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усе праграмы і даныя гэтага сеанса будуць выдалены."</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Выдаліць"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Скінуць"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"З вяртаннем, госць!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Хочаце працягнуць сеанс?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Пачаць зноў"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 9f71f43..0eedd1ca1 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -1038,8 +1038,7 @@
     <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"স্ক্রিনের কিছুটা অংশ বড় করুন"</string>
     <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"বদল করুন"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"অ্যাক্সেসিবিলিটি জেসচার পরিবর্তন করে অ্যাক্সেসেবিলিটি বোতাম করা হয়েছে\n\n"<annotation id="link">"সেটিংস দেখুন"</annotation></string>
-    <!-- no translation found for accessibility_floating_button_switch_migration_tooltip (6248529129221218770) -->
-    <skip />
+    <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"আপনি অ্যাক্সেসিবিলিটি জেসচারের বদলে \n\n"<annotation id="link">"সেটিংস"</annotation>" বোতামে সুইচ করতে পারেন"</string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"এটি অস্থায়ীভাবে লুকাতে বোতামটি কোণে সরান"</string>
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"উপরে বাঁদিকে সরান"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"উপরে ডানদিকে সরান"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 17dbbe9..615d346 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -482,12 +482,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"Dodaj korisnika"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Novi korisnik"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Ukloniti gosta?"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Poništiti gostujuću sesiju?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci iz ove sesije će se izbrisati."</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ukloni"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Poništi"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Zdravo! Lijepo je opet vidjeti goste."</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li nastaviti sesiju?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni ispočetka"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 274ca3a..17cb3b9 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -1038,8 +1038,7 @@
     <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Teil des Bildschirms vergrößern"</string>
     <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Schalter"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Die Schaltfläche „Bedienungshilfen“ ersetzt die Touch-Geste für Bedienungshilfen\n\n"<annotation id="link">"Einstellungen aufrufen"</annotation></string>
-    <!-- no translation found for accessibility_floating_button_switch_migration_tooltip (6248529129221218770) -->
-    <skip />
+    <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Du kannst von der barrierefreien Geste zu einer Schaltfläche wechseln\n\n"<annotation id="link">"Einstellungen"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Durch Ziehen an den Rand wird die Schaltfläche zeitweise ausgeblendet"</string>
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Nach oben links verschieben"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Nach rechts oben verschieben"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 6622821..49784570 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -480,12 +480,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"Lisa kasutaja"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Uus kasutaja"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Kas eemaldada külaline?"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Kas lähtestada külastajaseanss?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Seansi kõik rakendused ja andmed kustutatakse."</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Eemalda"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Lähtesta"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Tere tulemast tagasi, külaline!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Kas soovite seansiga jätkata?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Alusta uuesti"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 6f7aee2..abbbae7 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -482,12 +482,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"Dodavanje korisnika"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Novi korisnik"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Ukloniti gosta?"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Poništiti gostujuću sesiju?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci u ovoj sesiji bit će izbrisani."</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ukloni"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Poništi"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Dobro došli natrag, gostu!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li nastaviti sesiju?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni ispočetka"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 800c9dc..15106bb 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -480,12 +480,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"Felhasználó hozzáadása"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Új felhasználó"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Eltávolítja a vendég munkamenetet?"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Visszaállítja a vendég munkamenetet?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"A munkamenetben található összes alkalmazás és adat törlődni fog."</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Eltávolítás"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Visszaállítás"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Örülünk, hogy visszatért, vendég!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Folytatja a munkamenetet?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Újrakezdés"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index deeeb20..2e2141e 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -480,12 +480,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"Ավելացնել օգտատեր"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Նոր օգտատեր"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Հեռացնե՞լ հյուրին"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Վերակայե՞լ հյուրի աշխատաշրջանը"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Այս աշխատաշրջանի բոլոր ծրագրերն ու տվյալները կջնջվեն:"</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Հեռացնել"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Վերակայել"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Բարի վերադարձ, հյուր"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Շարունակե՞լ աշխատաշրջանը։"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Վերսկսել"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index a6cb798..53a3165 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -480,12 +480,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"Tambahkan pengguna"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Pengguna baru"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Hapus tamu?"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Reset sesi tamu?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua aplikasi dan data di sesi ini akan dihapus."</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Hapus"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Reset"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Selamat datang kembali, tamu!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Lanjutkan sesi Anda?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Mulai ulang"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 066cd93..454b5bb 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -480,12 +480,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"Aggiungi utente"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Nuovo utente"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Rimuovere l\'ospite?"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Reimpostare sessione Ospite?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tutte le app e i dati di questa sessione verranno eliminati."</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Rimuovi"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Reimposta"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Ti ridiamo il benvenuto alla sessione Ospite."</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vuoi continuare la sessione?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Ricomincia"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index b2f0fd2..32c386d 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -725,7 +725,7 @@
     <string name="notification_channel_unsilenced" msgid="94878840742161152">"ההודעות אלה יישלחו כהתראות"</string>
     <string name="inline_blocking_helper" msgid="2891486013649543452">"ההתראות האלה בדרך כלל נדחות על ידך. \nלהמשיך להציג אותן?"</string>
     <string name="inline_done_button" msgid="6043094985588909584">"סיום"</string>
-    <string name="inline_ok_button" msgid="603075490581280343">"החלה"</string>
+    <string name="inline_ok_button" msgid="603075490581280343">"אישור"</string>
     <string name="inline_keep_showing" msgid="8736001253507073497">"שנמשיך להציג לך את ההתראות האלה?"</string>
     <string name="inline_stop_button" msgid="2453460935438696090">"לא, אל תמשיכו"</string>
     <string name="inline_deliver_silently_button" msgid="2714314213321223286">"הצגה ללא צליל"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index b173653..dacdf21 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -480,12 +480,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"ユーザーを追加"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"新しいユーザー"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ゲストを削除しますか?"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"ゲストをリセットしますか?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"このセッションでのアプリとデータはすべて削除されます。"</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"削除"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"リセット"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"おかえりなさい、ゲストさん"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"セッションを続行しますか?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"最初から開始"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 09d5f9f..8366b2f 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -480,12 +480,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"Пайдаланушы қосу"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Жаңа пайдаланушы"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Қонақты жою керек пе?"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Қонақ сеансы бастапқы күйге қайтарылсын ба?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Осы сеанстағы барлық қолданбалар мен деректер жойылады."</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Алып тастау"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Бастапқы күйге қайтару"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Қош келдіңіз, қонақ!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Сеансты жалғастыру керек пе?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Қайта бастау"</string>
@@ -668,7 +666,7 @@
     <string name="enable_demo_mode" msgid="3180345364745966431">"Демо режимін қосу"</string>
     <string name="show_demo_mode" msgid="3677956462273059726">"Демо режимін көрсету"</string>
     <string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
-    <string name="status_bar_alarm" msgid="87160847643623352">"Дабыл"</string>
+    <string name="status_bar_alarm" msgid="87160847643623352">"Оятқыш"</string>
     <string name="wallet_title" msgid="5369767670735827105">"Әмиян"</string>
     <string name="wallet_empty_state_label" msgid="7776761245237530394">"Телефоныңызбен бұрынғыдан да жылдам әрі қауіпсіз сатып алу үшін параметрлерді орнатыңыз."</string>
     <string name="wallet_app_button_label" msgid="7123784239111190992">"Барлығын көрсету"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 065a499..3baa4e1 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -480,12 +480,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"បញ្ចូល​អ្នកប្រើ"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"អ្នកប្រើ​ថ្មី"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ដកភ្ញៀវចេញឬ?"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"កំណត់​ភ្ញៀវឡើង​វិញឬ?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"កម្មវិធី និងទិន្នន័យ​ទាំងអស់​ក្នុង​វគ្គ​នេះ​នឹង​ត្រូវ​លុប។"</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ដកចេញ"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"កំណត់​ឡើងវិញ"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"សូម​ស្វាគមន៍​ការ​ត្រឡប់​មកវិញ, ភ្ញៀវ!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"តើ​អ្នក​ចង់​បន្ត​វគ្គ​របស់​អ្នក​ទេ?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ចាប់ផ្ដើមសាជាថ្មី"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 80415d5..ab80f7f 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -480,12 +480,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"사용자 추가"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"신규 사용자"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"게스트를 삭제하시겠습니까?"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"게스트를 초기화하시겠습니까?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"이 세션에 있는 모든 앱과 데이터가 삭제됩니다."</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"삭제"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"초기화"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"게스트 세션 진행"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"세션을 계속 진행하시겠습니까?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"다시 시작"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 2474fe1..037a765 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -480,12 +480,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"Колдонуучу кошуу"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Жаңы колдонуучу"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Конокту алып саласызбы?"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Конок сеансын баштапкы абалга келтиресизби?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Бул сеанстагы бардык колдонмолор жана маалыматтар өчүрүлөт."</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Өчүрүү"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Баштапкы абалга келтирүү"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Кайтып келишиңиз менен!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Сеансыңызды улантасызбы?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Кайра баштоо"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 106f7f2..4cdc8a5 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -480,12 +480,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"ເພີ່ມຜູ້ໃຊ້"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"ຜູ່ໃຊ້ໃໝ່"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ລຶບ​ແຂກ​ບໍ?"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"ຣີເຊັດແຂກບໍ?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ແອັບຯ​ແລະ​ຂໍ້​ມູນ​ທັງ​ໝົດ​ໃນ​ເຊດ​ຊັນ​ນີ້​ຈະ​ຖືກ​ລຶບ​ອອກ."</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ລຶບ​"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"ຣີເຊັດ"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"ຍິນ​ດີ​ຕ້ອນ​ຮັບ​ກັບ​ມາ, ຜູ້ຢ້ຽມຢາມ!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"ທ່ານ​ຕ້ອງ​ການ​ສືບ​ຕໍ່​ເຊດ​ຊັນ​ຂອງ​ທ່ານບໍ່?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ເລີ່ມຕົ້ນໃຫມ່"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 535da7a..90f775b 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -480,12 +480,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"Додај корисник"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Нов корисник"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Да се отстрани гостинот?"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Да се ресетира гостинот?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Сите апликации и податоци во сесијата ќе се избришат."</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Отстрани"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Ресетирај"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Добре дојде пак, гостине!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Дали сакате да продолжите со сесијата?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почни одново"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 8dae31d..976c2dd 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -480,12 +480,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"Хэрэглэгч нэмэх"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Шинэ хэрэглэгч"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Зочныг хасах уу?"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Зочныг шинэчлэх үү?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Энэ харилцан үйлдлийн бүх апп болон дата устах болно."</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Хасах"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Шинэчлэх"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Эргэн тавтай морилно уу!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Та үргэлжлүүлэхийг хүсэж байна уу?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Дахин эхлүүлэх"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 3aae064..84b5594 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -1038,8 +1038,7 @@
     <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रीनचा काही भाग मॅग्निफाय करा"</string>
     <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"स्विच करा"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"अ‍ॅक्सेसिबिलिटी जेश्चर हे आता अ‍ॅक्सेसिबिलिटी बटण आहे \n\n"<annotation id="link">"सेटिंग्ज पाहा"</annotation></string>
-    <!-- no translation found for accessibility_floating_button_switch_migration_tooltip (6248529129221218770) -->
-    <skip />
+    <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"तुम्ही अ‍ॅक्सेसिबिलिटी जेश्चरवरून बटणवर स्विच करू शकता \n\n"<annotation id="link">"सेटिंग्ज"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"बटण तात्पुरते लपवण्यासाठी ते कोपर्‍यामध्ये हलवा"</string>
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"वर डावीकडे हलवा"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"वर उजवीकडे हलवा"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 9a8fc20..efe716e 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -480,12 +480,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"အသုံးပြုသူ ထည့်ရန်"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"အသုံးပြုသူ အသစ်"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ဧည့်သည်ကို ဖယ်မလား။"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"ဧည့်သည်ကို ပြင်ဆင်သတ်မှတ်မလား။"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ဒီချိတ်ဆက်မှု ထဲက အက်ပ်များ အားလုံး နှင့် ဒေတာကို ဖျက်ပစ်မည်။"</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ဖယ်ထုတ်ပါ"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"ပြင်ဆင်သတ်မှတ်ရန်"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"ဧည့်သည်ကို ပြန်လည် ကြိုဆိုပါသည်။"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"သင်၏ စက်ရှင်ကို ဆက်လုပ်လိုပါသလား။"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ပြန်စပါ"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 0006f7f..52e6155 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -1038,8 +1038,7 @@
     <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रिनको केही भाग म्याग्निफाइ गर्नुहोस्"</string>
     <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"बदल्नुहोस्"</string>
     <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"एक्सेसिबिलिटी इसाराका स्थानमा एक्सेसिबिलिटी बटन प्रयोग हुन थालेको छ\n\n"<annotation id="link">"सेटिङ हेर्नुहोस्"</annotation></string>
-    <!-- no translation found for accessibility_floating_button_switch_migration_tooltip (6248529129221218770) -->
-    <skip />
+    <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"तपाईं एक्सेसिबिलिटी जेस्चरको साटो बटन प्रयोग गर्न सक्नुहुन्छ\n\n"<annotation id="link">"सेटिङ"</annotation></string>
     <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"यो बटन केही बेर नदेखिने पार्न किनारातिर सार्नुहोस्"</string>
     <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"सिरानको बायाँतिर सार्नुहोस्"</string>
     <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"सिरानको दायाँतिर सार्नुहोस्"</string>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 8e6293a..b98694e 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -21,8 +21,8 @@
     It's fine to override this color since at that point the shade was dark. -->
     <color name="notification_legacy_background_color">@color/GM2_grey_900</color>
 
-    <!-- The color of the dividing line between grouped notifications while . -->
-    <color name="notification_divider_color">#212121</color>
+    <!-- The color of the dividing line between grouped notifications. -->
+    <color name="notification_divider_color">@*android:color/background_device_default_dark</color>
 
     <!-- The color of the gear shown behind a notification -->
     <color name="notification_gear_color">@color/GM2_grey_500</color>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 944753a..8bd41de 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -480,12 +480,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"Gebruiker toevoegen"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Nieuwe gebruiker"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Gast verwijderen?"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Gastsessie resetten?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps en gegevens in deze sessie worden verwijderd."</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Verwijderen"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Resetten"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welkom terug, gast!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Wil je doorgaan met je sessie?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Opnieuw starten"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 1dcb143..003ce4c 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -480,12 +480,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"ଉପଯୋଗକର୍ତ୍ତାଙ୍କୁ ଯୋଗ କରନ୍ତୁ"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"ନୂଆ ଉପଯୋଗକର୍ତ୍ତା"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ଅତିଥିଙ୍କୁ କାଢ଼ିଦେବେ?"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"ଅତିଥିଙ୍କୁ ରିସେଟ୍ କରିବେ?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ଏହି ସେସନର ସମସ୍ତ ଆପ୍‌ ଓ ଡାଟା ଡିଲିଟ୍‌ ହୋଇଯିବ।"</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"କାଢ଼ିଦିଅନ୍ତୁ"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"ରିସେଟ୍ କରନ୍ତୁ"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"ପୁଣି ସ୍ୱାଗତ, ଅତିଥି!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"ଆପଣ ନିଜର ସେସନ୍ ଜାରି ରଖିବାକୁ ଚାହାଁନ୍ତି କି?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ଆରମ୍ଭ କରନ୍ତୁ"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index ad41e55..e4dbca5 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -480,12 +480,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"Adicionar usuário"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Novo usuário"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remover visitante?"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Redefinir sessão de visitante?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remover"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Redefinir"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Você voltou, visitante!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Quer continuar a sessão?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 738e916..a9cb3dc 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -480,12 +480,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"Adicionar utilizador"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Novo utilizador"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remover o convidado?"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Pretende repor a sessão de convidado?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todas as apps e dados desta sessão serão eliminados."</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remover"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Repor"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bem-vindo de volta, convidado!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Pretende continuar a sessão?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index ad41e55..e4dbca5 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -480,12 +480,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"Adicionar usuário"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Novo usuário"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remover visitante?"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Redefinir sessão de visitante?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remover"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Redefinir"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Você voltou, visitante!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Quer continuar a sessão?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 3a8b51c..6c82ad5 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -480,12 +480,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"පරිශීලකයෙක් එක් කරන්න"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"නව පරිශීලකයා"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"අමුත්තාන් ඉවත් කරන්නද?"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"ආගන්තුකයා යළි සකසන්නද?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"මෙම සැසියේ සියළුම යෙදුම් සහ දත්ත මකාවී."</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ඉවත් කරන්න"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"යළි සකසන්න"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"නැවත සාදරයෙන් පිළිගනිමු, අමුත්තා!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"ඔබගේ සැසිය දිගටම කරගෙන යෑමට ඔබට අවශ්‍යද?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"යළි මුල සිට අරඹන්න"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 3ceef49..390a7bc 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -480,12 +480,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"Lägg till användare"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Ny användare"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vill du ta bort gästen?"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Vill du återställa gästsessionen?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alla appar och data i denna session kommer att raderas."</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ta bort"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Återställ"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Välkommen tillbaka som gäst!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vill du fortsätta sessionen?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Börja om"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 0a4e059..99bf399 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -480,12 +480,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"Ongeza mtumiaji"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Mtumiaji mpya"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Ungependa kumwondoa mgeni?"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Ungependa kubadilisha kipindi cha mgeni?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Data na programu zote katika kipindi hiki zitafutwa."</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ondoa"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Badilisha"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Karibu tena mgeni!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Je, unataka kuendelea na kipindi chako?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Anza upya"</string>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 9f8e636..da80b85 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -36,10 +36,6 @@
     <!-- On tablets this is just the close_handle_height -->
     <dimen name="peek_height">@dimen/close_handle_height</dimen>
 
-    <!-- The margin between the clock and the notifications on Keyguard. See
-         keyguard_clock_height_fraction_* for the difference between min and max.-->
-    <dimen name="keyguard_clock_notifications_margin">44dp</dimen>
-
     <!-- Height of the status bar header bar when on Keyguard -->
     <dimen name="status_bar_header_height_keyguard">60dp</dimen>
 
diff --git a/packages/SystemUI/res/values-television/colors.xml b/packages/SystemUI/res/values-television/colors.xml
index e5f3b47..e13c42e 100644
--- a/packages/SystemUI/res/values-television/colors.xml
+++ b/packages/SystemUI/res/values-television/colors.xml
@@ -19,4 +19,19 @@
 <resources>
     <color name="volume_dialog_background_color">#E61F232B</color>
     <color name="volume_dialog_background_color_above_blur">#C71F232B</color>
+
+    <color name="bottom_sheet_icon_color">#D2E3FC</color>
+
+    <color name="bottom_sheet_title_color">#E8F0FE</color>
+    <color name="bottom_sheet_body_color">#D2E3FC</color>
+
+    <color name="bottom_sheet_background_color">#1F232C</color>
+    <color name="bottom_sheet_background_color_with_blur">#AA1A2734</color>
+
+    <color name="bottom_sheet_button_background_color_focused">#E8F0FE</color>
+    <color name="bottom_sheet_button_background_color_unfocused">#0FE8EAED</color>
+
+    <color name="bottom_sheet_button_text_color_focused">#DB202124</color>
+    <color name="bottom_sheet_button_text_color_unfocused">#B5E8EAED</color>
+
 </resources>
diff --git a/packages/SystemUI/res/values-television/dimens.xml b/packages/SystemUI/res/values-television/dimens.xml
index 7626db9..3a1a3d9 100644
--- a/packages/SystemUI/res/values-television/dimens.xml
+++ b/packages/SystemUI/res/values-television/dimens.xml
@@ -21,4 +21,27 @@
     <dimen name="privacy_chip_icon_margin">3dp</dimen>
     <dimen name="privacy_chip_icon_padding">8dp</dimen>
     <dimen name="privacy_chip_icon_size">13dp</dimen>
+
+    <dimen name="bottom_sheet_padding_horizontal">32dp</dimen>
+    <dimen name="bottom_sheet_padding_vertical">24dp</dimen>
+
+    <dimen name="bottom_sheet_icon_size">42dp</dimen>
+    <dimen name="bottom_sheet_icon_margin">8dp</dimen>
+    <dimen name="bottom_sheet_title_margin_bottom">18dp</dimen>
+    <dimen name="bottom_sheet_details_margin_bottom">8dp</dimen>
+
+    <dimen name="bottom_sheet_actions_width">296dp</dimen>
+    <dimen name="bottom_sheet_actions_spacing">12dp</dimen>
+    <item name="bottom_sheet_button_selection_scaled" format="float" type="dimen">1.1</item>
+    <dimen name="bottom_sheet_button_width">232dp</dimen>
+    <dimen name="bottom_sheet_button_padding_horizontal">20dp</dimen>
+    <dimen name="bottom_sheet_button_padding_vertical">16dp</dimen>
+
+    <dimen name="bottom_sheet_corner_radius">24dp</dimen>
+    <dimen name="bottom_sheet_button_corner_radius">10dp</dimen>
+
+    <dimen name="bottom_sheet_min_height">208dp</dimen>
+    <dimen name="bottom_sheet_margin">24dp</dimen>
+    <dimen name="bottom_sheet_background_blur_radius">120dp</dimen>
+
 </resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-television/styles.xml b/packages/SystemUI/res/values-television/styles.xml
index 00217fb..0fb7898 100644
--- a/packages/SystemUI/res/values-television/styles.xml
+++ b/packages/SystemUI/res/values-television/styles.xml
@@ -28,4 +28,34 @@
         <item name="android:textColorPrimaryInverse">@color/tv_volume_dialog_accent</item>
         <item name="android:dialogCornerRadius">@dimen/volume_dialog_panel_width_half</item>
     </style>
+
+    <style name="BottomSheet" parent="Theme.Leanback">
+        <item name="android:windowIsFloating">true</item>
+        <item name="android:windowActivityTransitions">true</item>
+        <item name="android:windowNoTitle">true</item>
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:backgroundDimAmount">0.2</item>
+    </style>
+
+    <style name="BottomSheet.TitleText">
+        <item name="android:textSize">28sp</item>
+        <item name="android:textColor">@color/bottom_sheet_title_color</item>
+    </style>
+
+    <style name="BottomSheet.BodyText">
+        <item name="android:textSize">16sp</item>
+        <item name="android:textColor">@color/bottom_sheet_body_color</item>
+    </style>
+
+    <style name="BottomSheet.ActionItem">
+        <item name="android:layout_width">@dimen/bottom_sheet_button_width</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:gravity">left|center_vertical</item>
+        <item name="android:textSize">16sp</item>
+        <item name="android:textColor">@color/bottom_sheet_button_text_color</item>
+        <item name="android:background">@drawable/bottom_sheet_button_background</item>
+        <item name="android:paddingHorizontal">@dimen/bottom_sheet_button_padding_horizontal</item>
+        <item name="android:paddingVertical">@dimen/bottom_sheet_button_padding_vertical</item>
+        <item name="android:stateListAnimator">@anim/tv_bottom_sheet_button_state_list_animator</item>
+    </style>
 </resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index d6eec3f..b21d22f 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -480,12 +480,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"เพิ่มผู้ใช้"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"ผู้ใช้ใหม่"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ต้องการนำผู้ใช้ชั่วคราวออกไหม"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"รีเซ็ตผู้เข้าร่วมไหม"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ระบบจะลบแอปและข้อมูลทั้งหมดในเซสชันนี้"</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"นำออก"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"รีเซ็ต"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"ยินดีต้อนรับผู้เข้าร่วมกลับมาอีกครั้ง"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"คุณต้องการอยู่ในเซสชันต่อไปไหม"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"เริ่มต้นใหม่"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 0a7bdac..5b48488 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -480,12 +480,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"Magdagdag ng user"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Bagong user"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Alisin ang bisita?"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"I-reset ang session ng bisita?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ide-delete ang lahat ng app at data sa session na ito."</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Alisin"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"I-reset"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welcome ulit, bisita!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Gusto mo bang ipagpatuloy ang iyong session?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Magsimulang muli"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 80e29fb..8a3d467 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -480,12 +480,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"Kullanıcı ekle"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Yeni kullanıcı"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Misafir oturumu kaldırılsın mı?"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Misafir oturumu sıfırlansın mı?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu oturumdaki tüm uygulamalar ve veriler silinecek."</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Kaldır"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Sıfırla"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Misafir kullanıcı, tekrar hoşgeldiniz"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Oturumunuza devam etmek istiyor musunuz?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Baştan başla"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 25cc331d..9592b2d 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -480,12 +480,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"添加用户"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"新用户"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"要移除访客吗?"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"要重置访客会话吗?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"此会话中的所有应用和数据都将被删除。"</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"移除"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"重置"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"访客,欢迎回来!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"要继续您的会话吗?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新开始"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 5c58081..7be08d3 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -480,12 +480,10 @@
     <string name="user_add_user" msgid="4336657383006913022">"加入使用者"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"新使用者"</string>
     <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"移除訪客?"</string>
-    <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"要重設訪客嗎?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會被刪除。"</string>
     <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"移除"</string>
-    <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) -->
-    <skip />
+    <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"重設"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"訪客您好,歡迎回來!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"您要繼續您的工作階段嗎?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新開始"</string>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 2cf3058..e7edb0e 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -93,7 +93,7 @@
     <color name="notification_material_background_dark_color">#ff333333</color>
 
     <!-- The color of the dividing line between grouped notifications. -->
-    <color name="notification_divider_color">#FF616161</color>
+    <color name="notification_divider_color">@*android:color/background_device_default_light</color>
 
     <!-- The color of the ripples on the untinted notifications -->
     <color name="notification_ripple_untinted_color">#28000000</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 9678aff..2a1bee5 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -445,6 +445,9 @@
     <!-- Adjust the theme on fully custom and decorated custom view notifications -->
     <bool name="config_adjustThemeOnNotificationCustomViews">false</bool>
 
+    <!-- Notifications are sized to match the width of two (of 4) qs tiles in landscape. -->
+    <bool name="config_skinnyNotifsInLandscape">true</bool>
+
     <!-- If true, enable the advance anti-falsing classifier on the lockscreen. On some devices it
          does not work well, particularly with noisy touchscreens. Note that disabling it may
          increase the rate of unintentional unlocks. -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 9b860c7..7525a9b 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -685,7 +685,7 @@
     <dimen name="notification_shadow_radius">0dp</dimen>
 
     <!-- The alpha of the dividing line between child notifications of a notification group. -->
-    <item name="notification_divider_alpha" format="float" type="dimen">0.5</item>
+    <item name="notification_divider_alpha" format="float" type="dimen">1</item>
 
     <!-- The height of the divider between the individual notifications in a notification
          group. -->
@@ -735,8 +735,8 @@
     <!-- Minimum distance the user has to drag down to go to the full shade. -->
     <dimen name="keyguard_drag_down_min_distance">100dp</dimen>
 
-    <!-- The margin between the clock and the notifications on Keyguard.-->
-    <dimen name="keyguard_clock_notifications_margin">30dp</dimen>
+    <!-- The margin between the status view and the notifications on Keyguard.-->
+    <dimen name="keyguard_status_view_bottom_margin">20dp</dimen>
     <!-- Minimum margin between clock and status bar -->
     <dimen name="keyguard_clock_top_margin">36dp</dimen>
     <!-- The margin between top of clock and bottom of lock icon. -->
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index ef6212d..a28d174 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -205,6 +205,10 @@
         }
     }
 
+    int getNotificationIconAreaHeight() {
+        return mNotificationIconAreaController.getHeight();
+    }
+
     @Override
     protected void onViewDetached() {
         if (CUSTOM_CLOCKS_ENABLED) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 123c0e6..2096c31 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -187,10 +187,11 @@
     }
 
     /**
-     * Get the height of the keyguard status view.
+     * Get the height of the keyguard status view without the notification icon area, as that's
+     * only visible on AOD.
      */
-    public int getHeight() {
-        return mView.getHeight();
+    public int getLockscreenHeight() {
+        return mView.getHeight() - mKeyguardClockSwitchController.getNotificationIconAreaHeight();
     }
 
     /**
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 5bb5522..e891e5b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.accessibility.floatingmenu;
 
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.util.MathUtils.constrain;
 import static android.util.MathUtils.sq;
 import static android.view.WindowInsets.Type.ime;
@@ -200,6 +201,7 @@
 
         mListView = listView;
         mWindowManager = context.getSystemService(WindowManager.class);
+        mLastConfiguration = new Configuration(getResources().getConfiguration());
         mAdapter = new AccessibilityTargetAdapter(mTargets);
         mUiHandler = createUiHandler();
         mPosition = position;
@@ -243,7 +245,6 @@
             }
         });
 
-        mLastConfiguration = new Configuration(getResources().getConfiguration());
 
         initListView();
         updateStrokeWith(getResources().getConfiguration().uiMode, mAlignment);
@@ -567,8 +568,10 @@
         final int currentX = (int) event.getX();
         final int currentY = (int) event.getY();
 
+        final int marginStartEnd = getMarginStartEndWith(mLastConfiguration);
         final Rect touchDelegateBounds =
-                new Rect(mMargin, mMargin, mMargin + getLayoutWidth(), mMargin + getLayoutHeight());
+                new Rect(marginStartEnd, mMargin, marginStartEnd + getLayoutWidth(),
+                        mMargin + getLayoutHeight());
         if (action == MotionEvent.ACTION_DOWN
                 && touchDelegateBounds.contains(currentX, currentY)) {
             mIsDownInEnlargedTouchArea = true;
@@ -682,15 +685,13 @@
         mListView.setLayoutManager(layoutManager);
         mListView.addOnItemTouchListener(this);
         mListView.animate().setInterpolator(new OvershootInterpolator());
-        updateListView();
+        updateListViewWith(mLastConfiguration);
 
         addView(mListView);
     }
 
-    private void updateListView() {
-        final LayoutParams layoutParams = (FrameLayout.LayoutParams) mListView.getLayoutParams();
-        layoutParams.setMargins(mMargin, mMargin, mMargin, mMargin);
-        mListView.setLayoutParams(layoutParams);
+    private void updateListViewWith(Configuration configuration) {
+        updateMarginWith(configuration);
 
         final int elevation =
                 getResources().getDimensionPixelSize(R.dimen.accessibility_floating_menu_elevation);
@@ -719,13 +720,15 @@
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
+        mLastConfiguration.setTo(newConfig);
+
         final int diff = newConfig.diff(mLastConfiguration);
         if ((diff & ActivityInfo.CONFIG_LOCALE) != 0) {
             updateAccessibilityTitle(mCurrentLayoutParams);
         }
 
         updateDimensions();
-        updateListView();
+        updateListViewWith(newConfig);
         updateItemViewWith(mSizeType);
         updateColor();
         updateStrokeWith(newConfig.uiMode, mAlignment);
@@ -733,8 +736,6 @@
         updateRadiusWith(mSizeType, mRadiusType, mTargets.size());
         updateScrollModeWith(hasExceededMaxLayoutHeight());
         setSystemGestureExclusion();
-
-        mLastConfiguration.setTo(newConfig);
     }
 
     @VisibleForTesting
@@ -756,11 +757,11 @@
     }
 
     private int getMinWindowX() {
-        return -mMargin;
+        return -getMarginStartEndWith(mLastConfiguration);
     }
 
     private int getMaxWindowX() {
-        return mScreenWidth - mMargin - getLayoutWidth();
+        return mScreenWidth - getMarginStartEndWith(mLastConfiguration) - getLayoutWidth();
     }
 
     private int getMaxWindowY() {
@@ -805,6 +806,15 @@
         return layoutBottomY > imeY ? (layoutBottomY - imeY) : 0;
     }
 
+    private void updateMarginWith(Configuration configuration) {
+        // Avoid overlapping with system bars under landscape mode, update the margins of the menu
+        // to align the edge of system bars.
+        final int marginStartEnd = getMarginStartEndWith(configuration);
+        final LayoutParams layoutParams = (FrameLayout.LayoutParams) mListView.getLayoutParams();
+        layoutParams.setMargins(marginStartEnd, mMargin, marginStartEnd, mMargin);
+        mListView.setLayoutParams(layoutParams);
+    }
+
     private void updateOffsetWith(@ShapeType int shapeType, @Alignment int side) {
         final float halfWidth = getLayoutWidth() / 2.0f;
         final float offset = (shapeType == ShapeType.OVAL) ? 0 : halfWidth;
@@ -896,6 +906,12 @@
         return (mPadding + mIconHeight) * mTargets.size() + mPadding;
     }
 
+    private int getMarginStartEndWith(Configuration configuration) {
+        return configuration != null
+                && configuration.orientation == ORIENTATION_PORTRAIT
+                ? mMargin : 0;
+    }
+
     private @DimenRes int getRadiusResId(@SizeType int sizeType, int itemCount) {
         return sizeType == SizeType.SMALL
                 ? getSmallSizeResIdWith(itemCount)
@@ -932,7 +948,7 @@
     }
 
     private int getWindowWidth() {
-        return mMargin * 2 + getLayoutWidth();
+        return getMarginStartEndWith(mLastConfiguration) * 2 + getLayoutWidth();
     }
 
     private int getWindowHeight() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index ec930b0..11412f4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -67,6 +67,7 @@
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -111,6 +112,7 @@
     @NonNull private final FalsingManager mFalsingManager;
     @NonNull private final PowerManager mPowerManager;
     @NonNull private final AccessibilityManager mAccessibilityManager;
+    @NonNull private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
     @Nullable private final UdfpsHbmProvider mHbmProvider;
     // 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.
@@ -507,6 +509,7 @@
             @NonNull FalsingManager falsingManager,
             @NonNull PowerManager powerManager,
             @NonNull AccessibilityManager accessibilityManager,
+            @NonNull LockscreenShadeTransitionController lockscreenShadeTransitionController,
             @NonNull ScreenLifecycle screenLifecycle,
             @Nullable Vibrator vibrator,
             @NonNull Optional<UdfpsHbmProvider> hbmProvider) {
@@ -529,6 +532,7 @@
         mFalsingManager = falsingManager;
         mPowerManager = powerManager;
         mAccessibilityManager = accessibilityManager;
+        mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
         mHbmProvider = hbmProvider.orElse(null);
         screenLifecycle.addObserver(mScreenObserver);
         mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON;
@@ -716,6 +720,7 @@
                         mFgExecutor,
                         mDumpManager,
                         mKeyguardViewMediator,
+                        mLockscreenShadeTransitionController,
                         this
                 );
             case IUdfpsOverlayController.REASON_AUTH_BP:
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 00888df..35ca470 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -31,6 +31,7 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -54,6 +55,7 @@
     @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @NonNull private final DelayableExecutor mExecutor;
     @NonNull private final KeyguardViewMediator mKeyguardViewMediator;
+    @NonNull private final LockscreenShadeTransitionController mLockScreenShadeTransitionController;
     @NonNull private final UdfpsController mUdfpsController;
 
     @Nullable private Runnable mCancelDelayedHintRunnable;
@@ -63,6 +65,7 @@
     private boolean mFaceDetectRunning;
     private boolean mHintShown;
     private int mStatusBarState;
+    private float mTransitionToFullShadeProgress;
 
     /**
      * hidden amount of pin/pattern/password bouncer
@@ -81,12 +84,14 @@
             @NonNull DelayableExecutor mainDelayableExecutor,
             @NonNull DumpManager dumpManager,
             @NonNull KeyguardViewMediator keyguardViewMediator,
+            @NonNull LockscreenShadeTransitionController transitionController,
             @NonNull UdfpsController udfpsController) {
         super(view, statusBarStateController, statusBar, dumpManager);
         mKeyguardViewManager = statusBarKeyguardViewManager;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mExecutor = mainDelayableExecutor;
         mKeyguardViewMediator = keyguardViewMediator;
+        mLockScreenShadeTransitionController = transitionController;
         mUdfpsController = udfpsController;
     }
 
@@ -116,6 +121,7 @@
         updatePauseAuth();
 
         mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
+        mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(this);
     }
 
     @Override
@@ -127,6 +133,9 @@
         mStatusBarStateController.removeCallback(mStateListener);
         mKeyguardViewManager.removeAlternateAuthInterceptor(mAlternateAuthInterceptor);
         mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false);
+        if (mLockScreenShadeTransitionController.getUdfpsKeyguardViewController() == this) {
+            mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(null);
+        }
 
         if (mCancelDelayedHintRunnable != null) {
             mCancelDelayedHintRunnable.run();
@@ -256,11 +265,21 @@
         }
     }
 
+    /**
+     * Set the progress we're currently transitioning to the full shade. 0.0f means we're not
+     * transitioning yet, while 1.0f means we've fully dragged down.
+     */
+    public void setTransitionToFullShadeProgress(float progress) {
+        mTransitionToFullShadeProgress = progress;
+        updateAlpha();
+    }
+
     private void updateAlpha() {
         // fade icon on transition to showing bouncer
         int alpha = mShowingUdfpsBouncer ? 255
                 : Math.abs((int) MathUtils.constrainedMap(0f, 255f, .4f, .7f,
                         mInputBouncerHiddenAmount));
+        alpha *= (1.0f - mTransitionToFullShadeProgress);
         mView.setUnpausedAlpha(alpha);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index 2dbf30f..de8ed70 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -25,6 +25,7 @@
 import com.android.systemui.screenrecord.ScreenRecordDialog;
 import com.android.systemui.screenshot.LongScreenshotActivity;
 import com.android.systemui.sensorprivacy.SensorUseStartedActivity;
+import com.android.systemui.sensorprivacy.television.TvUnblockSensorActivity;
 import com.android.systemui.settings.brightness.BrightnessDialog;
 import com.android.systemui.statusbar.tv.notifications.TvNotificationPanelActivity;
 import com.android.systemui.tuner.TunerActivity;
@@ -120,4 +121,10 @@
     @IntoMap
     @ClassKey(SensorUseStartedActivity.class)
     public abstract Activity bindSensorUseStartedActivity(SensorUseStartedActivity activity);
+
+    /** Inject into TvUnblockSensorActivity. */
+    @Binds
+    @IntoMap
+    @ClassKey(TvUnblockSensorActivity.class)
+    public abstract Activity bindTvUnblockSensorActivity(TvUnblockSensorActivity activity);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 746621d..d85c9a7 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -231,7 +231,8 @@
             @Main Handler mainHandler,
             UiEventLogger uiEventLogger,
             NavigationBarOverlayController navBarOverlayController,
-            ConfigurationController configurationController) {
+            ConfigurationController configurationController,
+            UserTracker userTracker) {
         return new NavigationBarController(context,
                 windowManager,
                 assistManagerLazy,
@@ -256,7 +257,8 @@
                 mainHandler,
                 uiEventLogger,
                 navBarOverlayController,
-                configurationController);
+                configurationController,
+                userTracker);
     }
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 053d75d..954ba79 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -63,6 +63,7 @@
 import android.telecom.TelecomManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.view.CrossWindowBlurListeners;
 import android.view.IWindowManager;
 import android.view.ViewConfiguration;
 import android.view.WindowManager;
@@ -139,6 +140,12 @@
     }
 
     @Provides
+    @Singleton
+    static CrossWindowBlurListeners provideCrossWindowBlurListeners() {
+        return CrossWindowBlurListeners.getInstance();
+    }
+
+    @Provides
     @DisplayId
     static int provideDisplayId(Context context) {
         return context.getDisplayId();
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 7b34e52..455f3c0 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -71,7 +71,6 @@
      * Assuming that the screen should start on.
      */
     private static boolean sWakeDisplaySensorState = true;
-    private Runnable mQuickPickupDozeCancellable;
 
     private static final int PROXIMITY_TIMEOUT_DELAY_MS = 500;
 
@@ -279,14 +278,14 @@
         boolean isWakeDisplayEvent = isQuickPickup || ((isWakeOnPresence || isWakeOnReach)
                 && rawValues != null && rawValues.length > 0 && rawValues[0] != 0);
 
-        if (isWakeOnPresence || isQuickPickup) {
-            onWakeScreen(isQuickPickup || isWakeDisplayEvent,
+        if (isWakeOnPresence) {
+            onWakeScreen(isWakeDisplayEvent,
                     mMachine.isExecutingTransition() ? null : mMachine.getState(),
                     pulseReason);
         } else if (isLongPress) {
             requestPulse(pulseReason, true /* alreadyPerformedProxCheck */,
                     null /* onPulseSuppressedListener */);
-        } else if (isWakeOnReach) {
+        } else if (isWakeOnReach || isQuickPickup) {
             if (isWakeDisplayEvent) {
                 requestPulse(pulseReason, true /* alreadyPerformedProxCheck */,
                         null /* onPulseSuppressedListener */);
@@ -388,11 +387,7 @@
      */
     private void onWakeScreen(boolean wake, @Nullable DozeMachine.State state, int reason) {
         mDozeLog.traceWakeDisplay(wake, reason);
-        final boolean isWakeOnPresence = reason == DozeLog.REASON_SENSOR_WAKE_UP;
-        final boolean isQuickPickup = reason == DozeLog.REASON_SENSOR_QUICK_PICKUP;
-        if (isWakeOnPresence) {
-            sWakeDisplaySensorState = wake;
-        }
+        sWakeDisplaySensorState = wake;
 
         if (wake) {
             proximityCheckThenCall((result) -> {
@@ -405,27 +400,13 @@
                     // Log sensor triggered
                     Optional.ofNullable(DozingUpdateUiEvent.fromReason(reason))
                             .ifPresent(mUiEventLogger::log);
-
-                    if (isQuickPickup) {
-                        // schedule runnable to go back to DOZE
-                        onQuickPickup();
-                    }
-                } else if (state == DozeMachine.State.DOZE_AOD && isQuickPickup) {
-                    // elongate time in DOZE_AOD, schedule new runnable to go back to DOZE
-                    onQuickPickup();
                 }
-            }, isQuickPickup /* alreadyPerformedProxCheck */, reason);
+            }, false /* alreadyPerformedProxCheck */, reason);
         } else {
             boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED);
             boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING);
-            boolean pulse = (state == DozeMachine.State.DOZE_REQUEST_PULSE)
-                    || (state == DozeMachine.State.DOZE_PULSING)
-                    || (state == DozeMachine.State.DOZE_PULSING_BRIGHT);
-            boolean docked = (state == DozeMachine.State.DOZE_AOD_DOCKED);
+
             if (!pausing && !paused) {
-                if (isQuickPickup && (pulse || docked)) {
-                    return;
-                }
                 mMachine.requestState(DozeMachine.State.DOZE);
                 // log wake timeout
                 mUiEventLogger.log(DozingUpdateUiEvent.DOZING_UPDATE_WAKE_TIMEOUT);
@@ -433,15 +414,6 @@
         }
     }
 
-    private void onQuickPickup() {
-        cancelQuickPickupDelayableDoze();
-        mQuickPickupDozeCancellable = mMainExecutor.executeDelayed(() -> {
-            onWakeScreen(false,
-                    mMachine.isExecutingTransition() ? null : mMachine.getState(),
-                    DozeLog.REASON_SENSOR_QUICK_PICKUP);
-        }, mDozeParameters.getQuickPickupAodDuration());
-    }
-
     @Override
     public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
         switch (newState) {
@@ -481,7 +453,6 @@
                 mDozeSensors.requestTemporaryDisable();
                 break;
             case FINISH:
-                cancelQuickPickupDelayableDoze();
                 mBroadcastReceiver.unregister(mBroadcastDispatcher);
                 mDozeHost.removeCallback(mHostCallback);
                 mDockManager.removeListener(mDockEventListener);
@@ -510,16 +481,6 @@
         }
     }
 
-    /**
-     * Cancels last scheduled Runnable that transitions to STATE_DOZE (blank screen) after
-     * going into STATE_AOD (AOD screen) from the quick pickup gesture.
-     */
-    private void cancelQuickPickupDelayableDoze() {
-        if (mQuickPickupDozeCancellable != null) {
-            mQuickPickupDozeCancellable.run();
-            mQuickPickupDozeCancellable = null;
-        }
-    }
 
     private void checkTriggersAtInit() {
         if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index 1004e25..3957a60 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -32,6 +32,7 @@
 
 private const val TAG = "MediaCarouselController"
 private val settingsIntent = Intent().setAction(ACTION_MEDIA_CONTROLS_SETTINGS)
+private const val DEBUG = false
 
 /**
  * Class that is responsible for keeping the view carousel up to date.
@@ -156,6 +157,12 @@
         }
     }
 
+    /**
+     * Update MediaCarouselScrollHandler.visibleToUser to reflect media card container visibility.
+     * It will be called when the container is out of view.
+     */
+    lateinit var updateUserVisibility: () -> Unit
+
     init {
         mediaFrame = inflateMediaCarousel()
         mediaCarousel = mediaFrame.requireViewById(R.id.media_carousel_scroller)
@@ -177,6 +184,12 @@
             keysNeedRemoval.forEach { removePlayer(it) }
             keysNeedRemoval.clear()
 
+            // Update user visibility so that no extra impression will be logged when
+            // activeMediaIndex resets to 0
+            if (this::updateUserVisibility.isInitialized) {
+                updateUserVisibility()
+            }
+
             // Let's reset our scroll position
             mediaCarouselScrollHandler.scrollToStart()
         }
@@ -187,16 +200,24 @@
                 key: String,
                 oldKey: String?,
                 data: MediaData,
-                immediately: Boolean
+                immediately: Boolean,
+                isSsReactivated: Boolean
             ) {
                 if (addOrUpdatePlayer(key, oldKey, data)) {
                     MediaPlayerData.getMediaPlayer(key, null)?.let {
                         logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED
                                 it.mInstanceId,
                                 /* isRecommendationCard */ false,
-                                it.surfaceForSmartspaceLogging)
+                                it.surfaceForSmartspaceLogging,
+                                rank = MediaPlayerData.getMediaPlayerIndex(key))
                     }
                 }
+                if (mediaCarouselScrollHandler.visibleToUser &&
+                        isSsReactivated && !mediaCarouselScrollHandler.qsExpanded) {
+                    // It could happen that reactived media player isn't visible to user because
+                    // of it is a resumption card.
+                    logSmartspaceImpression(mediaCarouselScrollHandler.qsExpanded)
+                }
                 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
@@ -217,17 +238,24 @@
                 data: SmartspaceMediaData,
                 shouldPrioritize: Boolean
             ) {
-                Log.d(TAG, "My Smartspace media update is here")
+                if (DEBUG) Log.d(TAG, "Loading Smartspace media update")
                 if (data.isActive) {
                     addSmartspaceMediaRecommendations(key, data, shouldPrioritize)
                     MediaPlayerData.getMediaPlayer(key, null)?.let {
                         logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED
                                 it.mInstanceId,
                                 /* isRecommendationCard */ true,
-                                it.surfaceForSmartspaceLogging)
-                    }
-                    if (mediaCarouselScrollHandler.visibleToUser) {
-                        logSmartspaceImpression()
+                                it.surfaceForSmartspaceLogging,
+                                rank = MediaPlayerData.getMediaPlayerIndex(key))
+
+                        if (mediaCarouselScrollHandler.visibleToUser &&
+                                mediaCarouselScrollHandler.visibleMediaIndex ==
+                                MediaPlayerData.getMediaPlayerIndex(key)) {
+                            logSmartspaceCardReported(800, // SMARTSPACE_CARD_SEEN
+                                    it.mInstanceId,
+                                    /* isRecommendationCard */ true,
+                                    it.surfaceForSmartspaceLogging)
+                        }
                     }
                 } else {
                     onSmartspaceMediaDataRemoved(data.targetId, immediately = true)
@@ -239,7 +267,7 @@
             }
 
             override fun onSmartspaceMediaDataRemoved(key: String, immediately: Boolean) {
-                Log.d(TAG, "My Smartspace media removal request is received")
+                if (DEBUG) Log.d(TAG, "My Smartspace media removal request is received")
                 if (immediately || visualStabilityManager.isReorderingAllowed) {
                     onMediaDataRemoved(key)
                 } else {
@@ -357,7 +385,7 @@
         data: SmartspaceMediaData,
         shouldPrioritize: Boolean
     ) {
-        Log.d(TAG, "Updating smartspace target in carousel")
+        if (DEBUG) Log.d(TAG, "Updating smartspace target in carousel")
         if (MediaPlayerData.getMediaPlayer(key, null) != null) {
             Log.w(TAG, "Skip adding smartspace target in carousel")
             return
@@ -644,17 +672,17 @@
     }
 
     /**
-     * Log the user impression for media card.
+     * Log the user impression for media card at visibleMediaIndex.
      */
-    fun logSmartspaceImpression() {
+    fun logSmartspaceImpression(qsExpanded: Boolean) {
         val visibleMediaIndex = mediaCarouselScrollHandler.visibleMediaIndex
         if (MediaPlayerData.players().size > visibleMediaIndex) {
             val mediaControlPanel = MediaPlayerData.players().elementAt(visibleMediaIndex)
-            val isMediaActive =
-                    MediaPlayerData.playerKeys().elementAt(visibleMediaIndex).data?.active
+            val hasActiveMediaOrRecommendationCard =
+                    MediaPlayerData.hasActiveMediaOrRecommendationCard()
             val isRecommendationCard = mediaControlPanel.recommendationViewHolder != null
-            if (!isRecommendationCard && !isMediaActive) {
-                // Media control card time out or swiped away
+            if (!hasActiveMediaOrRecommendationCard && !qsExpanded) {
+                // Skip logging if on LS or QQS, and there is no active media card
                 return
             }
             logSmartspaceCardReported(800, // SMARTSPACE_CARD_SEEN
@@ -672,6 +700,13 @@
         surface: Int,
         rank: Int = mediaCarouselScrollHandler.visibleMediaIndex
     ) {
+        // Only log media resume card when Smartspace data is available
+        if (!isRecommendationCard &&
+                        !mediaManager.smartspaceMediaData.isActive &&
+                                MediaPlayerData.smartspaceMediaData == null) {
+            return
+        }
+
         /* ktlint-disable max-line-length */
         SysUiStatsLog.write(SysUiStatsLog.SMARTSPACE_CARD_REPORTED,
                 eventId,
@@ -770,6 +805,16 @@
         return mediaData.get(key)?.let { mediaPlayers.get(it) }
     }
 
+    fun getMediaPlayerIndex(key: String): Int {
+        val sortKey = mediaData.get(key)
+        mediaPlayers.entries.forEachIndexed { index, e ->
+            if (e.key == sortKey) {
+                return index
+            }
+        }
+        return -1
+    }
+
     fun removeMediaPlayer(key: String) = mediaData.remove(key)?.let {
         if (it.isSsMediaRec) {
             smartspaceMediaData = null
@@ -808,4 +853,15 @@
         mediaData.clear()
         mediaPlayers.clear()
     }
+
+    /* Returns true if there is active media player card or recommendation card */
+    fun hasActiveMediaOrRecommendationCard(): Boolean {
+        if (smartspaceMediaData != null && smartspaceMediaData?.isActive!!) {
+            return true
+        }
+        if (firstActiveMediaIndex() != -1) {
+            return true
+        }
+        return false
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
index eb354978..b0d4cb1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
@@ -62,7 +62,7 @@
     private val closeGuts: (immediate: Boolean) -> Unit,
     private val falsingCollector: FalsingCollector,
     private val falsingManager: FalsingManager,
-    private val logSmartspaceImpression: () -> Unit
+    private val logSmartspaceImpression: (Boolean) -> Unit
 ) {
     /**
      * Is the view in RTL
@@ -195,18 +195,22 @@
             if (playerWidthPlusPadding == 0) {
                 return
             }
+
             val relativeScrollX = scrollView.relativeScrollX
             onMediaScrollingChanged(relativeScrollX / playerWidthPlusPadding,
                     relativeScrollX % playerWidthPlusPadding)
         }
     }
 
+    /**
+     * Whether the media card is visible to user if any
+     */
     var visibleToUser: Boolean = false
-        set(value) {
-            if (field != value) {
-                field = value
-            }
-        }
+
+    /**
+     * Whether the quick setting is expanded or not
+     */
+    var qsExpanded: Boolean = false
 
     init {
         gestureDetector = GestureDetectorCompat(scrollView.context, gestureListener)
@@ -471,7 +475,7 @@
             val oldIndex = visibleMediaIndex
             visibleMediaIndex = newIndex
             if (oldIndex != visibleMediaIndex && visibleToUser) {
-                logSmartspaceImpression()
+                logSmartspaceImpression(qsExpanded)
             }
             closeGuts(false)
             updatePlayerVisibilities()
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 19190cd..19a67e9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -266,7 +266,7 @@
         }
         mKey = key;
         MediaSession.Token token = data.getToken();
-        mInstanceId = data.getPackageName().hashCode();
+        mInstanceId = SmallHash.hash(data.getPackageName());
 
         mBackgroundColor = data.getBackgroundColor();
         if (mToken == null || !mToken.equals(token)) {
@@ -504,7 +504,7 @@
             return;
         }
 
-        mInstanceId = data.getTargetId().hashCode();
+        mInstanceId = SmallHash.hash(data.getTargetId());
         mBackgroundColor = data.getBackgroundColor();
         TransitionLayout recommendationCard = mRecommendationViewHolder.getRecommendations();
         recommendationCard.setBackgroundTintList(ColorStateList.valueOf(mBackgroundColor));
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
index ee1d3ea..296bfda 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
@@ -31,7 +31,8 @@
         key: String,
         oldKey: String?,
         data: MediaData,
-        immediately: Boolean
+        immediately: Boolean,
+        isSsReactivated: Boolean
     ) {
         if (oldKey != null && oldKey != key && entries.contains(oldKey)) {
             entries[key] = data to entries.remove(oldKey)?.second
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
index a611b60..c8deb01 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
@@ -83,7 +83,8 @@
         key: String,
         oldKey: String?,
         data: MediaData,
-        immediately: Boolean
+        immediately: Boolean,
+        isSsReactivated: Boolean
     ) {
         if (oldKey != null && oldKey != key) {
             allEntries.remove(oldKey)
@@ -101,7 +102,7 @@
 
         // Notify listeners
         listeners.forEach {
-            it.onMediaDataLoaded(key, oldKey, data)
+            it.onMediaDataLoaded(key, oldKey, data, isSsReactivated = isSsReactivated)
         }
     }
 
@@ -118,6 +119,8 @@
         // Override the pass-in value here, as the order of Smartspace card is only determined here.
         var shouldPrioritizeMutable = false
         smartspaceMediaData = data
+        // Override the pass-in value here, as the Smartspace reactivation could only happen here.
+        var isSsReactivated = false
 
         // Before forwarding the smartspace target, first check if we have recently inactive media
         val sorted = userEntries.toSortedMap(compareBy {
@@ -137,9 +140,13 @@
             // Notify listeners to consider this media active
             Log.d(TAG, "reactivating $lastActiveKey instead of smartspace")
             reactivatedKey = lastActiveKey
+            if (MediaPlayerData.firstActiveMediaIndex() == -1) {
+                isSsReactivated = true
+            }
             val mediaData = sorted.get(lastActiveKey)!!.copy(active = true)
             listeners.forEach {
-                it.onMediaDataLoaded(lastActiveKey, lastActiveKey, mediaData)
+                it.onMediaDataLoaded(lastActiveKey, lastActiveKey, mediaData,
+                        isSsReactivated = isSsReactivated)
             }
         } else {
             // Mark to prioritize Smartspace card if no recent media.
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 28d336e..df1b07f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -148,7 +148,7 @@
     private val internalListeners: MutableSet<Listener> = mutableSetOf()
     private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap()
     // There should ONLY be at most one Smartspace media recommendation.
-    private var smartspaceMediaData: SmartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA
+    var smartspaceMediaData: SmartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA
     private var smartspaceSession: SmartspaceSession? = null
     private var allowMediaRecommendations = Utils.allowMediaRecommendations(context)
 
@@ -453,7 +453,8 @@
         if (smartspaceMediaData.targetId != key) {
             return
         }
-        Log.d(TAG, "Dismissing Smartspace media target")
+
+        if (DEBUG) Log.d(TAG, "Dismissing Smartspace media target")
         if (smartspaceMediaData.isActive) {
             smartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA.copy(
                 targetId = smartspaceMediaData.targetId)
@@ -709,17 +710,19 @@
 
     override fun onSmartspaceTargetsUpdated(targets: List<Parcelable>) {
         if (!allowMediaRecommendations) {
+            if (DEBUG) Log.d(TAG, "Smartspace recommendation is disabled in Settings.")
             return
         }
 
         val mediaTargets = targets.filterIsInstance<SmartspaceTarget>()
         when (mediaTargets.size) {
             0 -> {
-                Log.d(TAG, "Empty Smartspace media target")
                 if (!smartspaceMediaData.isActive) {
                     return
                 }
-                Log.d(TAG, "Set Smartspace media to be inactive for the data update")
+                if (DEBUG) {
+                    Log.d(TAG, "Set Smartspace media to be inactive for the data update")
+                }
                 smartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA.copy(
                     targetId = smartspaceMediaData.targetId)
                 notifySmartspaceMediaDataRemoved(smartspaceMediaData.targetId, immediately = false)
@@ -728,13 +731,12 @@
                 val newMediaTarget = mediaTargets.get(0)
                 if (smartspaceMediaData.targetId == newMediaTarget.smartspaceTargetId) {
                     // The same Smartspace updates can be received. Skip the duplicate updates.
-                    Log.d(TAG, "Same Smartspace media update exists. Skip loading data.")
-                } else {
-                    Log.d(TAG, "Forwarding Smartspace media update.")
-                    smartspaceMediaData = toSmartspaceMediaData(newMediaTarget, isActive = true)
-                    notifySmartspaceMediaDataLoaded(
-                        smartspaceMediaData.targetId, smartspaceMediaData)
+                    return
                 }
+                if (DEBUG) Log.d(TAG, "Forwarding Smartspace media update.")
+                smartspaceMediaData = toSmartspaceMediaData(newMediaTarget, isActive = true)
+                notifySmartspaceMediaDataLoaded(
+                    smartspaceMediaData.targetId, smartspaceMediaData)
             }
             else -> {
                 // There should NOT be more than 1 Smartspace media update. When it happens, it
@@ -824,12 +826,16 @@
          * @param immediately indicates should apply the UI changes immediately, otherwise wait
          * until the next refresh-round before UI becomes visible. True by default to take in place
          * immediately.
+         *
+         * @param isSsReactivated indicates transition from a state with no active media players to
+         * a state with active media players upon receiving Smartspace media data.
          */
         fun onMediaDataLoaded(
             key: String,
             oldKey: String?,
             data: MediaData,
-            immediately: Boolean = true
+            immediately: Boolean = true,
+            isSsReactivated: Boolean = false
         ) {}
 
         /**
@@ -879,7 +885,7 @@
     private fun packageName(target: SmartspaceTarget): String? {
         val recommendationList = target.iconGrid
         if (recommendationList == null || recommendationList.isEmpty()) {
-            Log.d(TAG, "Empty or media recommendation list.")
+            Log.w(TAG, "Empty or null media recommendation list.")
             return null
         }
         for (recommendation in recommendationList) {
@@ -889,7 +895,7 @@
                     packageName -> return packageName }
             }
         }
-        Log.d(TAG, "No valid package name is provided.")
+        Log.w(TAG, "No valid package name is provided.")
         return null
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
index 52ecbea..292b0e2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
@@ -67,7 +67,8 @@
         key: String,
         oldKey: String?,
         data: MediaData,
-        immediately: Boolean
+        immediately: Boolean,
+        isSsReactivated: Boolean
     ) {
         if (oldKey != null && oldKey != key) {
             val oldEntry = entries.remove(oldKey)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index edbf187..186f961 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -220,14 +220,11 @@
         set(value) {
             if (field != value) {
                 field = value
+                mediaCarouselController.mediaCarouselScrollHandler.qsExpanded = value
             }
             // qs is expanded on LS shade and HS shade
             if (value && (isLockScreenShadeVisibleToUser() || isHomeScreenShadeVisibleToUser())) {
-                mediaCarouselController.logSmartspaceImpression()
-            }
-            // Release shade and back to lock screen
-            if (isLockScreenVisibleToUser()) {
-                mediaCarouselController.logSmartspaceImpression()
+                mediaCarouselController.logSmartspaceImpression(value)
             }
             mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = isVisibleToUser()
         }
@@ -409,7 +406,7 @@
                 updateTargetState()
                 // Enters shade from lock screen
                 if (newState == StatusBarState.SHADE_LOCKED && isLockScreenShadeVisibleToUser()) {
-                    mediaCarouselController.logSmartspaceImpression()
+                    mediaCarouselController.logSmartspaceImpression(qsExpanded)
                 }
                 mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = isVisibleToUser()
             }
@@ -423,7 +420,7 @@
                     dozeAnimationRunning = false
                     // Enters lock screen from screen off
                     if (isLockScreenVisibleToUser()) {
-                        mediaCarouselController.logSmartspaceImpression()
+                        mediaCarouselController.logSmartspaceImpression(qsExpanded)
                     }
                 } else {
                     updateDesiredLocation()
@@ -436,11 +433,7 @@
             override fun onExpandedChanged(isExpanded: Boolean) {
                 // Enters shade from home screen
                 if (isHomeScreenShadeVisibleToUser()) {
-                    mediaCarouselController.logSmartspaceImpression()
-                }
-                // Back to lock screen from bouncer
-                if (isLockScreenVisibleToUser()) {
-                    mediaCarouselController.logSmartspaceImpression()
+                    mediaCarouselController.logSmartspaceImpression(qsExpanded)
                 }
                 mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = isVisibleToUser()
             }
@@ -465,6 +458,10 @@
                 goingToSleep = false
             }
         })
+
+        mediaCarouselController.updateUserVisibility = {
+            mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = isVisibleToUser()
+        }
     }
 
     private fun updateConfiguration() {
@@ -663,15 +660,11 @@
             return true
         }
 
-        if (statusbarState == StatusBarState.KEYGUARD) {
-            if (currentLocation == LOCATION_LOCKSCREEN &&
-                previousLocation == LOCATION_QS ||
-                (currentLocation == LOCATION_QS &&
-                    previousLocation == LOCATION_LOCKSCREEN)) {
-                // We're always fading from lockscreen to keyguard in situations where the player
-                // is already fully hidden
-                return false
-            }
+        if (statusbarState == StatusBarState.KEYGUARD && (currentLocation == LOCATION_LOCKSCREEN ||
+                        previousLocation == LOCATION_LOCKSCREEN)) {
+            // We're always fading from lockscreen to keyguard in situations where the player
+            // is already fully hidden
+            return false
         }
         return mediaFrame.isShownNotFaded || animator.isRunning || animationPending
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
index 43e2142..ff085c36 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
@@ -60,7 +60,8 @@
             key: String,
             oldKey: String?,
             data: MediaData,
-            immediately: Boolean
+            immediately: Boolean,
+            isSsReactivated: Boolean
         ) {
             if (immediately) {
                 updateViewVisibility()
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
index 0da84fb..ab568c8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
@@ -159,7 +159,8 @@
         key: String,
         oldKey: String?,
         data: MediaData,
-        immediately: Boolean
+        immediately: Boolean,
+        isSsReactivated: Boolean
     ) {
         if (useMediaResumption) {
             // If this had been started from a resume state, disconnect now that it's live
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt
index a4f33e3..8bddde8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt
@@ -95,7 +95,8 @@
         key: String,
         oldKey: String?,
         data: MediaData,
-        immediately: Boolean
+        immediately: Boolean,
+        isSsReactivated: Boolean
     ) {
         backgroundExecutor.execute {
             data.token?.let {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
index bbea140..9a39193 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
@@ -54,7 +54,8 @@
         key: String,
         oldKey: String?,
         data: MediaData,
-        immediately: Boolean
+        immediately: Boolean,
+        isSsReactivated: Boolean
     ) {
         var reusedListener: PlaybackStateListener? = null
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/SmallHash.java b/packages/SystemUI/src/com/android/systemui/media/SmallHash.java
new file mode 100644
index 0000000..de7aac6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/SmallHash.java
@@ -0,0 +1,44 @@
+/*
+ * 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.media;
+
+import java.util.Objects;
+
+/**
+ * A simple hash function for use in privacy-sensitive logging.
+ */
+public final class SmallHash {
+    // Hashes will be in the range [0, MAX_HASH).
+    public static final int MAX_HASH = (1 << 13);
+
+    /** Return Small hash of the string, if non-null, or 0 otherwise. */
+    public static int hash(String in) {
+        return hash(Objects.hashCode(in));
+    }
+
+    /**
+     * Maps in to the range [0, MAX_HASH), keeping similar values distinct.
+     *
+     * @param in An arbitrary integer.
+     * @return in mod MAX_HASH, signs chosen to stay in the range [0, MAX_HASH).
+     */
+    public static int hash(int in) {
+        return Math.abs(Math.floorMod(in, MAX_HASH));
+    }
+
+    private SmallHash() {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 0d5faff..1d6d1f2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -46,7 +46,7 @@
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private ViewGroup mConnectedItem;
-    private boolean mInclueDynamicGroup;
+    private boolean mIncludeDynamicGroup;
 
     public MediaOutputAdapter(MediaOutputController controller) {
         super(controller);
@@ -56,7 +56,6 @@
     public MediaDeviceBaseViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
             int viewType) {
         super.onCreateViewHolder(viewGroup, viewType);
-
         return new MediaDeviceViewHolder(mHolderView);
     }
 
@@ -66,7 +65,7 @@
         if (position == size && mController.isZeroMode()) {
             viewHolder.onBind(CUSTOMIZED_ITEM_PAIR_NEW, false /* topMargin */,
                     true /* bottomMargin */);
-        } else if (mInclueDynamicGroup) {
+        } else if (mIncludeDynamicGroup) {
             if (position == 0) {
                 viewHolder.onBind(CUSTOMIZED_ITEM_DYNAMIC_GROUP, true /* topMargin */,
                         false /* bottomMargin */);
@@ -76,11 +75,12 @@
                 // from "position - 1".
                 viewHolder.onBind(((List<MediaDevice>) (mController.getMediaDevices()))
                                 .get(position - 1),
-                        false /* topMargin */, position == size /* bottomMargin */);
+                        false /* topMargin */, position == size /* bottomMargin */, position);
             }
         } else if (position < size) {
             viewHolder.onBind(((List<MediaDevice>) (mController.getMediaDevices())).get(position),
-                    position == 0 /* topMargin */, position == (size - 1) /* bottomMargin */);
+                    position == 0 /* topMargin */, position == (size - 1) /* bottomMargin */,
+                    position);
         } else if (DEBUG) {
             Log.d(TAG, "Incorrect position: " + position);
         }
@@ -88,8 +88,8 @@
 
     @Override
     public int getItemCount() {
-        mInclueDynamicGroup = mController.getSelectedMediaDevice().size() > 1;
-        if (mController.isZeroMode() || mInclueDynamicGroup) {
+        mIncludeDynamicGroup = mController.getSelectedMediaDevice().size() > 1;
+        if (mController.isZeroMode() || mIncludeDynamicGroup) {
             // Add extra one for "pair new" or dynamic group
             return mController.getMediaDevices().size() + 1;
         }
@@ -120,15 +120,17 @@
         }
 
         @Override
-        void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) {
-            super.onBind(device, topMargin, bottomMargin);
-            final boolean currentlyConnected = !mInclueDynamicGroup && isCurrentlyConnected(device);
+        void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin, int position) {
+            super.onBind(device, topMargin, bottomMargin, position);
+            final boolean currentlyConnected = !mIncludeDynamicGroup
+                    && isCurrentlyConnected(device);
             if (currentlyConnected) {
                 mConnectedItem = mContainerLayout;
             }
             mBottomDivider.setVisibility(View.GONE);
             mCheckBox.setVisibility(View.GONE);
-            if (currentlyConnected && mController.isActiveRemoteDevice(device)) {
+            if (currentlyConnected && mController.isActiveRemoteDevice(device)
+                    && mController.getSelectableMediaDevice().size() > 0) {
                 // Init active device layout
                 mDivider.setVisibility(View.VISIBLE);
                 mDivider.setTransitionAlpha(1);
@@ -160,6 +162,7 @@
                     setTwoLineLayout(device, true /* bFocused */, true /* showSeekBar */,
                             false /* showProgressBar */, false /* showSubtitle */);
                     initSeekbar(device);
+                    mCurrentActivePosition = position;
                 } else {
                     setSingleLineLayout(getItemTitle(device), false /* bFocused */);
                     mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
@@ -186,11 +189,16 @@
                 mConnectedItem = mContainerLayout;
                 mBottomDivider.setVisibility(View.GONE);
                 mCheckBox.setVisibility(View.GONE);
-                mDivider.setVisibility(View.VISIBLE);
-                mDivider.setTransitionAlpha(1);
-                mAddIcon.setVisibility(View.VISIBLE);
-                mAddIcon.setTransitionAlpha(1);
-                mAddIcon.setOnClickListener(v -> onEndItemClick());
+                if (mController.getSelectableMediaDevice().size() > 0) {
+                    mDivider.setVisibility(View.VISIBLE);
+                    mDivider.setTransitionAlpha(1);
+                    mAddIcon.setVisibility(View.VISIBLE);
+                    mAddIcon.setTransitionAlpha(1);
+                    mAddIcon.setOnClickListener(v -> onEndItemClick());
+                } else {
+                    mDivider.setVisibility(View.GONE);
+                    mAddIcon.setVisibility(View.GONE);
+                }
                 mTitleIcon.setImageDrawable(getSpeakerDrawable());
                 final CharSequence sessionName = mController.getSessionName();
                 final CharSequence title = TextUtils.isEmpty(sessionName)
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index bcef43c..0890841 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -64,10 +64,12 @@
     Context mContext;
     View mHolderView;
     boolean mIsDragging;
+    int mCurrentActivePosition;
 
     public MediaOutputBaseAdapter(MediaOutputController controller) {
         mController = controller;
         mIsDragging = false;
+        mCurrentActivePosition = -1;
     }
 
     @Override
@@ -99,6 +101,10 @@
         return mIsAnimating;
     }
 
+    int getCurrentActivePosition() {
+        return mCurrentActivePosition;
+    }
+
     /**
      * ViewHolder for binding device view.
      */
@@ -136,7 +142,7 @@
             mCheckBox = view.requireViewById(R.id.check_box);
         }
 
-        void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) {
+        void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin, int position) {
             mDeviceId = device.getId();
             ThreadUtils.postOnBackgroundThread(() -> {
                 Icon icon = mController.getDeviceIconCompat(device).toIcon(mContext);
@@ -214,6 +220,9 @@
         }
 
         void initSeekbar(MediaDevice device) {
+            if (!mController.isVolumeControlEnabled(device)) {
+                disableSeekBar();
+            }
             mSeekBar.setMax(device.getMaxVolume());
             mSeekBar.setMin(0);
             final int currentVolume = device.getCurrentVolume();
@@ -242,6 +251,7 @@
         }
 
         void initSessionSeekbar() {
+            disableSeekBar();
             mSeekBar.setMax(mController.getSessionVolumeMax());
             mSeekBar.setMin(0);
             final int currentVolume = mController.getSessionVolume();
@@ -330,5 +340,10 @@
                     PorterDuff.Mode.SRC_IN));
             return BluetoothUtils.buildAdvancedDrawable(mContext, drawable);
         }
+
+        private void disableSeekBar() {
+            mSeekBar.setEnabled(false);
+            mSeekBar.setOnTouchListener((v, event) -> true);
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index 8a9a6e6..cdcdf9a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -174,7 +174,12 @@
             mHeaderTitle.setGravity(Gravity.NO_GRAVITY);
         }
         if (!mAdapter.isDragging() && !mAdapter.isAnimating()) {
-            mAdapter.notifyDataSetChanged();
+            int currentActivePosition = mAdapter.getCurrentActivePosition();
+            if (currentActivePosition >= 0) {
+                mAdapter.notifyItemChanged(currentActivePosition);
+            } else {
+                mAdapter.notifyDataSetChanged();
+            }
         }
         // Show when remote media session is available
         mStopButton.setVisibility(getStopButtonVisibility());
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 8fee925..5293c88 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -465,6 +465,10 @@
                 || features.contains(MediaRoute2Info.FEATURE_REMOTE_GROUP_PLAYBACK));
     }
 
+    boolean isVolumeControlEnabled(@NonNull MediaDevice device) {
+        return !device.getFeatures().contains(MediaRoute2Info.FEATURE_REMOTE_GROUP_PLAYBACK);
+    }
+
     private final MediaController.Callback mCb = new MediaController.Callback() {
         @Override
         public void onMetadataChanged(MediaMetadata metadata) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
index 24e076b..968c350 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
@@ -68,7 +68,7 @@
         final int size = mGroupMediaDevices.size();
         if (newPosition < size) {
             viewHolder.onBind(mGroupMediaDevices.get(newPosition), false /* topMargin */,
-                    newPosition == (size - 1) /* bottomMargin */);
+                    newPosition == (size - 1) /* bottomMargin */, position);
             return;
         }
         if (DEBUG) {
@@ -94,8 +94,8 @@
         }
 
         @Override
-        void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) {
-            super.onBind(device, topMargin, bottomMargin);
+        void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin, int position) {
+            super.onBind(device, topMargin, bottomMargin, position);
             mDivider.setVisibility(View.GONE);
             mAddIcon.setVisibility(View.GONE);
             mBottomDivider.setVisibility(View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index da09793..c6d7e7c 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -129,6 +129,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.UserTracker;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.AutoHideUiElement;
@@ -199,6 +200,7 @@
     private final Handler mHandler;
     private final NavigationBarOverlayController mNavbarOverlayController;
     private final UiEventLogger mUiEventLogger;
+    private final UserTracker mUserTracker;
 
     private Bundle mSavedState;
     private NavigationBarView mNavigationBarView;
@@ -459,7 +461,8 @@
             SystemActions systemActions,
             @Main Handler mainHandler,
             NavigationBarOverlayController navbarOverlayController,
-            UiEventLogger uiEventLogger) {
+            UiEventLogger uiEventLogger,
+            UserTracker userTracker) {
         mContext = context;
         mWindowManager = windowManager;
         mAccessibilityManager = accessibilityManager;
@@ -484,6 +487,7 @@
         mHandler = mainHandler;
         mNavbarOverlayController = navbarOverlayController;
         mUiEventLogger = uiEventLogger;
+        mUserTracker = userTracker;
 
         mNavBarMode = mNavigationModeController.addListener(this);
         mAccessibilityButtonModeObserver.addListener(this);
@@ -1450,12 +1454,14 @@
                 .getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
         boolean longPressDefault = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault);
-        mLongPressHomeEnabled = Settings.Secure.getInt(mContentResolver,
-                Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0) != 0;
+        mLongPressHomeEnabled = Settings.Secure.getIntForUser(mContentResolver,
+                Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0,
+                mUserTracker.getUserId()) != 0;
         boolean gestureDefault = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_assistTouchGestureEnabledDefault);
-        mAssistantTouchGestureEnabled = Settings.Secure.getInt(mContentResolver,
-                Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, gestureDefault ? 1 : 0) != 0;
+        mAssistantTouchGestureEnabled = Settings.Secure.getIntForUser(mContentResolver,
+                Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, gestureDefault ? 1 : 0,
+                mUserTracker.getUserId()) != 0;
         if (mOverviewProxyService.getProxy() != null) {
             try {
                 mOverviewProxyService.getProxy().onAssistantAvailable(mAssistantAvailable
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 8b5a537..5359210 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -57,6 +57,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.CommandQueue.Callbacks;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -116,6 +117,7 @@
     private final TaskbarDelegate mTaskbarDelegate;
     private int mNavMode;
     private boolean mIsTablet;
+    private final UserTracker mUserTracker;
 
     /** A displayId - nav bar maps. */
     @VisibleForTesting
@@ -151,7 +153,8 @@
             @Main Handler mainHandler,
             UiEventLogger uiEventLogger,
             NavigationBarOverlayController navBarOverlayController,
-            ConfigurationController configurationController) {
+            ConfigurationController configurationController,
+            UserTracker userTracker) {
         mContext = context;
         mWindowManager = windowManager;
         mAssistManagerLazy = assistManagerLazy;
@@ -184,6 +187,7 @@
         mNavigationModeController.addListener(this);
         mTaskbarDelegate = new TaskbarDelegate(mOverviewProxyService);
         mIsTablet = isTablet(mContext.getResources().getConfiguration());
+        mUserTracker = userTracker;
     }
 
     @Override
@@ -361,7 +365,8 @@
                 mSystemActions,
                 mHandler,
                 mNavBarOverlayController,
-                mUiEventLogger);
+                mUiEventLogger,
+                mUserTracker);
         mNavigationBars.put(displayId, navBar);
 
         View navigationBarView = navBar.createView(savedState);
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index e53cc0b..917a060 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -494,7 +494,8 @@
      * match the data by {@link ContactsContract.ContactsColumns#LOOKUP_KEY} key to ensure proper
      * matching across all the Contacts DB tables.
      */
-    private static List<String> getContactLookupKeysWithBirthdaysToday(Context context) {
+    @VisibleForTesting
+    public static List<String> getContactLookupKeysWithBirthdaysToday(Context context) {
         List<String> lookupKeysWithBirthdaysToday = new ArrayList<>(1);
         String today = new SimpleDateFormat("MM-dd").format(new Date());
         String[] projection = new String[]{
@@ -503,14 +504,20 @@
         String where =
                 ContactsContract.Data.MIMETYPE
                         + "= ? AND " + ContactsContract.CommonDataKinds.Event.TYPE + "="
-                        + ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY + " AND substr("
-                        + ContactsContract.CommonDataKinds.Event.START_DATE + ",6) = ?";
+                        + ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY + " AND (substr("
+                        // Birthdays stored with years will match this format
+                        + ContactsContract.CommonDataKinds.Event.START_DATE + ",6) = ? OR substr("
+                        // Birthdays stored without years will match this format
+                        + ContactsContract.CommonDataKinds.Event.START_DATE + ",3) = ? )";
         String[] selection =
-                new String[]{ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE, today};
+                new String[]{ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE, today,
+                        today};
         Cursor cursor = null;
         try {
-            cursor = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI,
-                    projection, where, selection, null);
+            cursor = context
+                    .getContentResolver()
+                    .query(ContactsContract.Data.CONTENT_URI,
+                            projection, where, selection, null);
             while (cursor != null && cursor.moveToNext()) {
                 String lookupKey = cursor.getString(
                         cursor.getColumnIndex(ContactsContract.CommonDataKinds.Event.LOOKUP_KEY));
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
index 32fdf0e..0e17c8b 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
@@ -265,7 +265,7 @@
 
     private RemoteViews createSuppressedView() {
         RemoteViews views;
-        if (mTile.isUserQuieted()) {
+        if (mTile != null && mTile.isUserQuieted()) {
             views = new RemoteViews(mContext.getPackageName(),
                     R.layout.people_tile_work_profile_quiet_layout);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
index 6888847..4085df9 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -22,6 +22,7 @@
 import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
 import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
 import static android.content.Intent.ACTION_BOOT_COMPLETED;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
 import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_ANYONE;
 
 import static com.android.systemui.people.NotificationHelper.getContactUri;
@@ -62,6 +63,7 @@
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -172,6 +174,7 @@
     public void init() {
         synchronized (mLock) {
             if (!mRegisteredReceivers) {
+                if (DEBUG) Log.d(TAG, "Register receivers");
                 IntentFilter filter = new IntentFilter();
                 filter.addAction(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
                 filter.addAction(ACTION_BOOT_COMPLETED);
@@ -185,6 +188,14 @@
                 mBroadcastDispatcher.registerReceiver(mBaseBroadcastReceiver, filter,
 
                         null /* executor */, UserHandle.ALL);
+                IntentFilter perAppFilter = new IntentFilter(ACTION_PACKAGE_REMOVED);
+                perAppFilter.addDataScheme("package");
+                // BroadcastDispatcher doesn't allow data schemes.
+                mContext.registerReceiver(mBaseBroadcastReceiver, perAppFilter);
+                IntentFilter bootComplete = new IntentFilter(ACTION_BOOT_COMPLETED);
+                bootComplete.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+                // BroadcastDispatcher doesn't allow priority.
+                mContext.registerReceiver(mBaseBroadcastReceiver, bootComplete);
                 mRegisteredReceivers = true;
             }
         }
@@ -303,10 +314,6 @@
 
     /** Updates tile in app widget options and the current view. */
     public void updateAppWidgetOptionsAndView(int appWidgetId, PeopleSpaceTile tile) {
-        if (tile == null) {
-            if (DEBUG) Log.w(TAG, "Requested to store null tile");
-            return;
-        }
         synchronized (mTiles) {
             mTiles.put(appWidgetId, tile);
         }
@@ -320,6 +327,17 @@
      */
     @Nullable
     public PeopleSpaceTile getTileForExistingWidget(int appWidgetId) {
+        try {
+            return getTileForExistingWidgetThrowing(appWidgetId);
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to retrieve conversation for tile: " + e);
+            return null;
+        }
+    }
+
+    @Nullable
+    private PeopleSpaceTile getTileForExistingWidgetThrowing(int appWidgetId) throws
+            PackageManager.NameNotFoundException {
         // First, check if tile is cached in memory.
         PeopleSpaceTile tile;
         synchronized (mTiles) {
@@ -348,7 +366,8 @@
      * If a {@link PeopleTileKey} is not provided, fetch one from {@link SharedPreferences}.
      */
     @Nullable
-    public PeopleSpaceTile getTileFromPersistentStorage(PeopleTileKey key, int appWidgetId) {
+    public PeopleSpaceTile getTileFromPersistentStorage(PeopleTileKey key, int appWidgetId) throws
+            PackageManager.NameNotFoundException {
         if (!key.isValid()) {
             Log.e(TAG, "PeopleTileKey invalid: " + key.toString());
             return null;
@@ -358,7 +377,6 @@
             Log.d(TAG, "System services are null");
             return null;
         }
-
         try {
             if (DEBUG) Log.d(TAG, "Retrieving Tile from storage: " + key.toString());
             ConversationChannel channel = mIPeopleManager.getConversation(
@@ -383,9 +401,9 @@
             }
 
             // Add current state.
-            return updateWithCurrentState(storedTile.build(), ACTION_BOOT_COMPLETED);
-        } catch (Exception e) {
-            Log.e(TAG, "Failed to retrieve conversation for tile: " + e);
+            return getTileWithCurrentState(storedTile.build(), ACTION_BOOT_COMPLETED);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not retrieve data: " + e);
             return null;
         }
     }
@@ -397,7 +415,6 @@
     public void updateWidgetsWithNotificationChanged(StatusBarNotification sbn,
             PeopleSpaceUtils.NotificationAction notificationAction) {
         if (DEBUG) {
-            Log.d(TAG, "updateWidgetsWithNotificationChanged called");
             if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) {
                 Log.d(TAG, "Notification posted, key: " + sbn.getKey());
             } else {
@@ -414,7 +431,6 @@
             PeopleTileKey key = new PeopleTileKey(
                     sbn.getShortcutId(), sbn.getUser().getIdentifier(), sbn.getPackageName());
             if (!key.isValid()) {
-                Log.d(TAG, "Sbn doesn't contain valid PeopleTileKey: " + key.toString());
                 return;
             }
             int[] widgetIds = mAppWidgetManager.getAppWidgetIds(
@@ -775,7 +791,13 @@
     /** Adds a widget based on {@code key} mapped to {@code appWidgetId}. */
     public void addNewWidget(int appWidgetId, PeopleTileKey key) {
         if (DEBUG) Log.d(TAG, "addNewWidget called with key for appWidgetId: " + appWidgetId);
-        PeopleSpaceTile tile = getTileFromPersistentStorage(key, appWidgetId);
+        PeopleSpaceTile tile = null;
+        try {
+            tile = getTileFromPersistentStorage(key, appWidgetId);
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "Cannot add widget since app was uninstalled");
+            return;
+        }
         if (tile == null) {
             return;
         }
@@ -1017,72 +1039,85 @@
 
         @Override
         public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (DEBUG) Log.d(TAG, "Update widgets from: " + action);
-            mBgExecutor.execute(() -> updateWidgetsOnStateChange(action));
+            if (DEBUG) Log.d(TAG, "Update widgets from: " + intent.getAction());
+            mBgExecutor.execute(() -> updateWidgetsFromBroadcastInBackground(intent.getAction()));
         }
     };
 
-    /** Updates any app widget based on the current state. */
+    /** Updates any app widget to the current state, triggered by a broadcast update. */
     @VisibleForTesting
-    void updateWidgetsOnStateChange(String entryPoint) {
+    void updateWidgetsFromBroadcastInBackground(String entryPoint) {
         int[] appWidgetIds = mAppWidgetManager.getAppWidgetIds(
                 new ComponentName(mContext, PeopleSpaceWidgetProvider.class));
         if (appWidgetIds == null) {
             return;
         }
-        synchronized (mLock) {
-            for (int appWidgetId : appWidgetIds) {
-                PeopleSpaceTile tile = getTileForExistingWidget(appWidgetId);
-                if (tile == null) {
-                    Log.e(TAG, "Matching conversation not found for shortcut ID");
-                } else {
-                    tile = updateWithCurrentState(tile, entryPoint);
+        for (int appWidgetId : appWidgetIds) {
+            PeopleSpaceTile existingTile = null;
+            PeopleSpaceTile updatedTile = null;
+            try {
+                synchronized (mLock) {
+                    existingTile = getTileForExistingWidgetThrowing(appWidgetId);
+                    if (existingTile == null) {
+                        Log.e(TAG, "Matching conversation not found for shortcut ID");
+                        return;
+                    }
+                    updatedTile = getTileWithCurrentState(existingTile, entryPoint);
+                    updateAppWidgetOptionsAndView(appWidgetId, updatedTile);
                 }
-                updateAppWidgetOptionsAndView(appWidgetId, tile);
+            } catch (PackageManager.NameNotFoundException e) {
+                // Delete data for uninstalled widgets.
+                Log.e(TAG, "Package no longer found for tile: " + e);
+                synchronized (mLock) {
+                    updateAppWidgetOptionsAndView(appWidgetId, updatedTile);
+                }
+                deleteWidgets(new int[]{appWidgetId});
             }
         }
     }
 
-    /** Checks the current state of {@code tile} dependencies, updating fields as necessary. */
+    /** Checks the current state of {@code tile} dependencies, modifying fields as necessary. */
     @Nullable
-    private PeopleSpaceTile updateWithCurrentState(PeopleSpaceTile tile,
-            String entryPoint) {
+    private PeopleSpaceTile getTileWithCurrentState(PeopleSpaceTile tile,
+            String entryPoint) throws
+            PackageManager.NameNotFoundException {
         PeopleSpaceTile.Builder updatedTile = tile.toBuilder();
-        try {
-            switch (entryPoint) {
-                case NotificationManager
-                        .ACTION_INTERRUPTION_FILTER_CHANGED:
-                    updatedTile.setNotificationPolicyState(getNotificationPolicyState());
-                    break;
-                case Intent.ACTION_PACKAGES_SUSPENDED:
-                case Intent.ACTION_PACKAGES_UNSUSPENDED:
-                    updatedTile.setIsPackageSuspended(getPackageSuspended(tile));
-                    break;
-                case Intent.ACTION_MANAGED_PROFILE_AVAILABLE:
-                case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE:
-                case Intent.ACTION_USER_UNLOCKED:
-                    updatedTile.setIsUserQuieted(getUserQuieted(tile));
-                    break;
-                case Intent.ACTION_LOCALE_CHANGED:
-                    break;
-                case ACTION_BOOT_COMPLETED:
-                default:
-                    updatedTile.setIsUserQuieted(getUserQuieted(tile)).setIsPackageSuspended(
-                            getPackageSuspended(tile)).setNotificationPolicyState(
-                            getNotificationPolicyState());
-            }
-        } catch (Exception e) {
-            Log.e(TAG, "Package no longer found for tile: " + tile.toString() + e);
-            return null;
+        switch (entryPoint) {
+            case NotificationManager
+                    .ACTION_INTERRUPTION_FILTER_CHANGED:
+                updatedTile.setNotificationPolicyState(getNotificationPolicyState());
+                break;
+            case Intent.ACTION_PACKAGES_SUSPENDED:
+            case Intent.ACTION_PACKAGES_UNSUSPENDED:
+                updatedTile.setIsPackageSuspended(getPackageSuspended(tile));
+                break;
+            case Intent.ACTION_MANAGED_PROFILE_AVAILABLE:
+            case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE:
+            case Intent.ACTION_USER_UNLOCKED:
+                updatedTile.setIsUserQuieted(getUserQuieted(tile));
+                break;
+            case Intent.ACTION_LOCALE_CHANGED:
+                break;
+            case ACTION_BOOT_COMPLETED:
+            default:
+                updatedTile.setIsUserQuieted(getUserQuieted(tile)).setIsPackageSuspended(
+                        getPackageSuspended(tile)).setNotificationPolicyState(
+                        getNotificationPolicyState());
         }
         return updatedTile.build();
     }
 
-    private boolean getPackageSuspended(PeopleSpaceTile tile) throws Exception {
+    private boolean getPackageSuspended(PeopleSpaceTile tile) throws
+            PackageManager.NameNotFoundException {
         boolean packageSuspended = !TextUtils.isEmpty(tile.getPackageName())
                 && mPackageManager.isPackageSuspended(tile.getPackageName());
         if (DEBUG) Log.d(TAG, "Package suspended: " + packageSuspended);
+        // isPackageSuspended() only throws an exception if the app has been uninstalled, and the
+        // app data has also been cleared. We want to empty the layout when the app is uninstalled
+        // regardless of app data clearing, which getApplicationInfoAsUser() handles.
+        mPackageManager.getApplicationInfoAsUser(
+                tile.getPackageName(), PackageManager.GET_META_DATA,
+                PeopleSpaceUtils.getUserId(tile));
         return packageSuspended;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index edfbed0..6660081 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -297,7 +297,7 @@
                 // start margin of next page).
                 qsPanelController.setPageMargin(mSideMargins);
             } else if (view == mHeader) {
-                // No content padding for the header.
+                quickStatusBarHeaderController.setContentMargins(mContentPadding, mContentPadding);
             } else {
                 view.setPaddingRelative(
                         mContentPadding,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 2d0d87d..bcce87a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -46,6 +46,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
 
 public class QSDetail extends LinearLayout {
 
@@ -83,6 +84,8 @@
     private boolean mSwitchState;
     private QSFooter mFooter;
 
+    private NotificationsQuickSettingsContainer mContainer;
+
     public QSDetail(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
     }
@@ -115,6 +118,10 @@
         mClipper = new QSDetailClipper(this);
     }
 
+    public void setContainer(NotificationsQuickSettingsContainer container) {
+        mContainer = container;
+    }
+
     /** */
     public void setQsPanel(QSPanelController panelController, QuickStatusBarHeader header,
             QSFooter footer) {
@@ -242,6 +249,9 @@
         }
         sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
         animateDetailVisibleDiff(x, y, visibleDiff, listener);
+        if (mContainer != null) {
+            mContainer.setDetailShowing(showingDetail);
+        }
     }
 
     protected void animateDetailVisibleDiff(int x, int y, boolean visibleDiff, AnimatorListener listener) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 7b8a6a0..c28c649 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -293,6 +293,7 @@
     public void setContainer(ViewGroup container) {
         if (container instanceof NotificationsQuickSettingsContainer) {
             mQSCustomizerController.setContainer((NotificationsQuickSettingsContainer) container);
+            mQSDetail.setContainer((NotificationsQuickSettingsContainer) container);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 0bb0a3f..c70eaff 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -425,7 +425,7 @@
             LinearLayout.LayoutParams layoutParams = (LayoutParams) hostView.getLayoutParams();
             layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
             layoutParams.width = horizontal ? 0 : ViewGroup.LayoutParams.MATCH_PARENT;
-            layoutParams.weight = horizontal ? 1.2f : 0;
+            layoutParams.weight = horizontal ? 1f : 0;
             // Add any bottom margin, such that the total spacing is correct. This is only
             // necessary if the view isn't horizontal, since otherwise the padding is
             // carried in the parent of this view (to ensure correct vertical alignment)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index e24acf2..659475d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -228,24 +228,5 @@
                 }
             }
         }
-
-        @Override
-        public void setExpansion(float expansion) {
-            if (expansion > 0f && expansion < 1f) {
-                return;
-            }
-            boolean selected = expansion == 0f;
-            // Expansion == 0f is when QQS is fully showing (as opposed to 1f, which is QS). At this
-            // point we want them to be selected so the tiles will marquee (but not at other points
-            // of expansion.
-            // We set it as not important while we change this, so setting each tile as selected
-            // will not cause them to announce themselves until the user has actually selected the
-            // item.
-            setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
-            for (int i = 0; i < getChildCount(); i++) {
-                getChildAt(i).setSelected(selected);
-            }
-            setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
-        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index cbdcad5..76076f6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -177,7 +177,6 @@
         mView.onAttach(mIconManager, mQSExpansionPathInterpolator);
 
         mDemoModeController.addCallback(mDemoModeReceiver);
-        mHeaderQsPanelController.setContentMargins(0, 0);
     }
 
     @Override
@@ -253,6 +252,10 @@
         return mMicCameraIndicatorsEnabled || mLocationIndicatorsEnabled;
     }
 
+    public void setContentMargins(int marginStart, int marginEnd) {
+        mHeaderQsPanelController.setContentMargins(marginStart, marginEnd);
+    }
+
     private static class ClockDemoModeReceiver implements DemoMode {
         private Clock mClockView;
 
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 8aed0a8..d31e67c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -190,6 +190,9 @@
         if (collapsed) {
             labelContainer.ignoreLastView = true
             secondaryLabel.alpha = 0f
+            // Do not marque in QQS
+            label.ellipsize = TextUtils.TruncateAt.END
+            secondaryLabel.ellipsize = TextUtils.TruncateAt.END
         }
         setLabelColor(getLabelColorForState(QSTile.State.DEFAULT_STATE))
         setSecondaryLabelColor(getSecondaryLabelColorForState(QSTile.State.DEFAULT_STATE))
@@ -492,11 +495,7 @@
         }
 
         return if (state.state == Tile.STATE_UNAVAILABLE || state is BooleanState) {
-            val resName = "$TILE_STATE_RES_PREFIX${state.spec}"
-            var arrayResId = resources.getIdentifier(resName, "array", context.packageName)
-            if (arrayResId == 0) {
-                arrayResId = R.array.tile_states_default
-            }
+            var arrayResId = SubtitleArrayMapping.getSubtitleId(state.spec)
             val array = resources.getStringArray(arrayResId)
             array[state.state]
         } else {
@@ -557,6 +556,41 @@
     private fun getChevronColorForState(state: Int): Int = getSecondaryLabelColorForState(state)
 }
 
+@VisibleForTesting
+internal object SubtitleArrayMapping {
+    private val subtitleIdsMap = mapOf<String?, Int>(
+        "internet" to R.array.tile_states_internet,
+        "wifi" to R.array.tile_states_wifi,
+        "cell" to R.array.tile_states_cell,
+        "battery" to R.array.tile_states_battery,
+        "dnd" to R.array.tile_states_dnd,
+        "flashlight" to R.array.tile_states_flashlight,
+        "rotation" to R.array.tile_states_rotation,
+        "bt" to R.array.tile_states_bt,
+        "airplane" to R.array.tile_states_airplane,
+        "location" to R.array.tile_states_location,
+        "hotspot" to R.array.tile_states_hotspot,
+        "inversion" to R.array.tile_states_inversion,
+        "saver" to R.array.tile_states_saver,
+        "dark" to R.array.tile_states_dark,
+        "work" to R.array.tile_states_work,
+        "cast" to R.array.tile_states_cast,
+        "night" to R.array.tile_states_night,
+        "screenrecord" to R.array.tile_states_screenrecord,
+        "reverse" to R.array.tile_states_reverse,
+        "reduce_brightness" to R.array.tile_states_reduce_brightness,
+        "cameratoggle" to R.array.tile_states_cameratoggle,
+        "mictoggle" to R.array.tile_states_mictoggle,
+        "controls" to R.array.tile_states_controls,
+        "wallet" to R.array.tile_states_wallet,
+        "alarm" to R.array.tile_states_alarm
+    )
+
+    fun getSubtitleId(spec: String?): Int {
+        return subtitleIdsMap.getOrDefault(spec, R.array.tile_states_default)
+    }
+}
+
 private fun colorValuesHolder(name: String, vararg values: Int): PropertyValuesHolder {
     return PropertyValuesHolder.ofInt(name, *values).apply {
         setEvaluator(ArgbEvaluator.getInstance())
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
index 730702e..51cc32a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
@@ -20,6 +20,7 @@
 import android.graphics.HardwareRenderer;
 import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
+import android.graphics.Region;
 import android.graphics.RenderNode;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
@@ -46,7 +47,6 @@
 
     private static final String TAG = "ImageTileSet";
 
-    private CallbackRegistry<OnBoundsChangedListener, ImageTileSet, Rect> mOnBoundsListeners;
     private CallbackRegistry<OnContentChangedListener, ImageTileSet, Rect> mContentListeners;
 
     @Inject
@@ -54,14 +54,6 @@
         mHandler = handler;
     }
 
-    interface OnBoundsChangedListener {
-        /**
-         * Reports an update to the bounding box that contains all active tiles. These are virtual
-         * (capture) coordinates which can be either negative or positive.
-         */
-        void onBoundsChanged(int left, int top, int right, int bottom);
-    }
-
     interface OnContentChangedListener {
         /**
          * Mark as dirty and rebuild display list.
@@ -70,25 +62,9 @@
     }
 
     private final List<ImageTile> mTiles = new ArrayList<>();
-    private final Rect mBounds = new Rect();
+    private final Region mRegion = new Region();
     private final Handler mHandler;
 
-    void addOnBoundsChangedListener(OnBoundsChangedListener listener) {
-        if (mOnBoundsListeners == null) {
-            mOnBoundsListeners = new CallbackRegistry<>(
-                    new NotifierCallback<OnBoundsChangedListener, ImageTileSet, Rect>() {
-                        @Override
-                        public void onNotifyCallback(OnBoundsChangedListener callback,
-                                ImageTileSet sender,
-                                int arg, Rect newBounds) {
-                            callback.onBoundsChanged(newBounds.left, newBounds.top, newBounds.right,
-                                    newBounds.bottom);
-                        }
-                    });
-        }
-        mOnBoundsListeners.add(listener);
-    }
-
     void addOnContentChangedListener(OnContentChangedListener listener) {
         if (mContentListeners == null) {
             mContentListeners = new CallbackRegistry<>(
@@ -110,14 +86,8 @@
             mHandler.post(() -> addTile(tile));
             return;
         }
-        final Rect newBounds = new Rect(mBounds);
-        final Rect newRect = tile.getLocation();
         mTiles.add(tile);
-        newBounds.union(newRect);
-        if (!newBounds.equals(mBounds)) {
-            mBounds.set(newBounds);
-            notifyBoundsChanged(mBounds);
-        }
+        mRegion.op(tile.getLocation(), mRegion, Region.Op.UNION);
         notifyContentChanged();
     }
 
@@ -127,12 +97,6 @@
         }
     }
 
-    private void notifyBoundsChanged(Rect bounds) {
-        if (mOnBoundsListeners != null) {
-            mOnBoundsListeners.notifyCallbacks(this, 0, bounds);
-        }
-    }
-
     /**
      * Returns a drawable to paint the combined contents of the tiles. Drawable dimensions are
      * zero-based and map directly to {@link #getLeft()}, {@link #getTop()}, {@link #getRight()},
@@ -153,6 +117,15 @@
         return mTiles.size();
     }
 
+    /**
+     * @return the bounding rect around any gaps in the tiles.
+     */
+    Rect getGaps() {
+        Region difference = new Region();
+        difference.op(mRegion.getBounds(), mRegion, Region.Op.DIFFERENCE);
+        return difference.getBounds();
+    }
+
     ImageTile get(int i) {
         return mTiles.get(i);
     }
@@ -182,41 +155,40 @@
     }
 
     int getLeft() {
-        return mBounds.left;
+        return mRegion.getBounds().left;
     }
 
     int getTop() {
-        return mBounds.top;
+        return mRegion.getBounds().top;
     }
 
     int getRight() {
-        return mBounds.right;
+        return mRegion.getBounds().right;
     }
 
     int getBottom() {
-        return mBounds.bottom;
+        return mRegion.getBounds().bottom;
     }
 
     int getWidth() {
-        return mBounds.width();
+        return mRegion.getBounds().width();
     }
 
     int getHeight() {
-        return mBounds.height();
+        return mRegion.getBounds().height();
     }
 
     void clear() {
         if (mTiles.isEmpty()) {
             return;
         }
-        mBounds.setEmpty();
+        mRegion.setEmpty();
         Iterator<ImageTile> i = mTiles.iterator();
         while (i.hasNext()) {
             ImageTile next = i.next();
             next.close();
             i.remove();
         }
-        notifyBoundsChanged(mBounds);
         notifyContentChanged();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
index 25ec1d7..f571e41 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -32,8 +32,6 @@
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.text.TextUtils;
-import android.transition.Transition;
-import android.transition.TransitionListenerAdapter;
 import android.util.Log;
 import android.view.ScrollCaptureResponse;
 import android.view.View;
@@ -74,7 +72,7 @@
     private final Executor mUiExecutor;
     private final Executor mBackgroundExecutor;
     private final ImageExporter mImageExporter;
-    private final LongScreenshotHolder mLongScreenshotHolder;
+    private final LongScreenshotData mLongScreenshotHolder;
 
     private ImageView mPreview;
     private ImageView mTransitionView;
@@ -103,7 +101,7 @@
     @Inject
     public LongScreenshotActivity(UiEventLogger uiEventLogger, ImageExporter imageExporter,
             @Main Executor mainExecutor, @Background Executor bgExecutor,
-            LongScreenshotHolder longScreenshotHolder) {
+            LongScreenshotData longScreenshotHolder) {
         mUiEventLogger = uiEventLogger;
         mUiExecutor = mainExecutor;
         mBackgroundExecutor = bgExecutor;
@@ -116,7 +114,6 @@
     public void onCreate(Bundle savedInstanceState) {
         Log.d(TAG, "onCreate(savedInstanceState = " + savedInstanceState + ")");
         super.onCreate(savedInstanceState);
-        postponeEnterTransition();
         setContentView(R.layout.long_screenshot);
 
         mPreview = requireViewById(R.id.preview);
@@ -210,25 +207,22 @@
                     public boolean onPreDraw() {
                         mEnterTransitionView.getViewTreeObserver().removeOnPreDrawListener(this);
                         updateImageDimensions();
-                        startPostponedEnterTransition();
-                        if (isActivityTransitionRunning()) {
-                            getWindow().getSharedElementEnterTransition().addListener(
-                                    new TransitionListenerAdapter() {
-                                        @Override
-                                        public void onTransitionEnd(Transition transition) {
-                                            super.onTransitionEnd(transition);
-                                            mPreview.animate().alpha(1f);
-                                            mCropView.setBoundaryPosition(
-                                                    CropView.CropBoundary.TOP, topFraction);
-                                            mCropView.setBoundaryPosition(
-                                                    CropView.CropBoundary.BOTTOM, bottomFraction);
-                                            mCropView.animateEntrance();
-                                            mCropView.setVisibility(View.VISIBLE);
-                                            setButtonsEnabled(true);
-                                            mEnterTransitionView.setVisibility(View.GONE);
-                                        }
+                        mEnterTransitionView.post(() -> {
+                            Rect dest = new Rect();
+                            mEnterTransitionView.getBoundsOnScreen(dest);
+                            mLongScreenshotHolder.takeTransitionDestinationCallback()
+                                    .setTransitionDestination(dest, () -> {
+                                        mPreview.animate().alpha(1f);
+                                        mCropView.setBoundaryPosition(
+                                                CropView.CropBoundary.TOP, topFraction);
+                                        mCropView.setBoundaryPosition(
+                                                CropView.CropBoundary.BOTTOM, bottomFraction);
+                                        mCropView.animateEntrance();
+                                        mCropView.setVisibility(View.VISIBLE);
+                                        setButtonsEnabled(true);
+                                        mEnterTransitionView.setVisibility(View.GONE);
                                     });
-                        }
+                        });
                         return true;
                     }
                 });
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotHolder.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotData.java
similarity index 63%
rename from packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotHolder.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotData.java
index 39c6f07..f549faf 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotHolder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotData.java
@@ -23,16 +23,19 @@
 import javax.inject.Inject;
 
 /**
- * LongScreenshotHolder holds on to 1 LongScreenshot reference to facilitate indirect in-process
- * passing.
+ * LongScreenshotData holds on to 1 LongScreenshot reference and 1 TransitionDestination
+ * reference, to facilitate indirect in-process passing.
  */
 @SysUISingleton
-public class LongScreenshotHolder {
+public class LongScreenshotData {
     private final AtomicReference<ScrollCaptureController.LongScreenshot> mLongScreenshot;
+    private final AtomicReference<ScreenshotController.TransitionDestination>
+            mTransitionDestinationCallback;
 
     @Inject
-    public LongScreenshotHolder() {
+    public LongScreenshotData() {
         mLongScreenshot = new AtomicReference<>();
+        mTransitionDestinationCallback = new AtomicReference<>();
     }
 
     /**
@@ -56,4 +59,18 @@
         return mLongScreenshot.getAndSet(null);
     }
 
+    /**
+     * Set the holder's TransitionDestination callback.
+     */
+    public void setTransitionDestinationCallback(
+            ScreenshotController.TransitionDestination destination) {
+        mTransitionDestinationCallback.set(destination);
+    }
+
+    /**
+     * Return the current TransitionDestination callback and clear.
+     */
+    public ScreenshotController.TransitionDestination takeTransitionDestinationCallback() {
+        return mTransitionDestinationCallback.getAndSet(null);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index aea8849..2e13836 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -64,6 +64,7 @@
 import android.view.IRemoteAnimationRunner;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
+import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationTarget;
 import android.view.ScrollCaptureResponse;
 import android.view.SurfaceControl;
@@ -72,6 +73,7 @@
 import android.view.Window;
 import android.view.WindowInsets;
 import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.widget.Toast;
@@ -203,6 +205,15 @@
         void onActionsReady(ScreenshotController.QuickShareData quickShareData);
     }
 
+    interface TransitionDestination {
+        /**
+         * Allows the long screenshot activity to call back with a destination location (the bounds
+         * on screen of the destination for the transitioning view) and a Runnable to be run once
+         * the transition animation is complete.
+         */
+        void setTransitionDestination(Rect transitionDestination, Runnable onTransitionEnd);
+    }
+
     // These strings are used for communicating the action invoked to
     // ScreenshotNotificationSmartActionsProvider.
     static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type";
@@ -241,7 +252,7 @@
     private final PhoneWindow mWindow;
     private final DisplayManager mDisplayManager;
     private final ScrollCaptureController mScrollCaptureController;
-    private final LongScreenshotHolder mLongScreenshotHolder;
+    private final LongScreenshotData mLongScreenshotHolder;
 
     private ScreenshotView mScreenshotView;
     private Bitmap mScreenBitmap;
@@ -286,7 +297,7 @@
             ImageExporter imageExporter,
             @Main Executor mainExecutor,
             ScrollCaptureController scrollCaptureController,
-            LongScreenshotHolder longScreenshotHolder) {
+            LongScreenshotData longScreenshotHolder) {
         mScreenshotSmartActions = screenshotSmartActions;
         mNotificationsController = screenshotNotificationsController;
         mScrollCaptureClient = scrollCaptureClient;
@@ -648,17 +659,29 @@
                     }
 
                     mLongScreenshotHolder.setLongScreenshot(longScreenshot);
+                    mLongScreenshotHolder.setTransitionDestinationCallback(
+                            (transitionDestination, onTransitionEnd) ->
+                                    mScreenshotView.startLongScreenshotTransition(
+                                            transitionDestination, onTransitionEnd,
+                                            longScreenshot));
 
                     final Intent intent = new Intent(mContext, LongScreenshotActivity.class);
                     intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
 
                     Pair<ActivityOptions, ExitTransitionCoordinator> transition =
-                            ActivityOptions.startSharedElementAnimation(
-                                    mWindow, new ScreenshotExitTransitionCallbacks(), null,
-                                    Pair.create(mScreenshotView.getScrollablePreview(),
-                                            ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME));
+                            ActivityOptions.startSharedElementAnimation(mWindow,
+                                    new ScreenshotExitTransitionCallbacksSupplier(false).get(),
+                                    null);
                     transition.second.startExit();
                     mContext.startActivity(intent, transition.first.toBundle());
+                    RemoteAnimationAdapter runner = new RemoteAnimationAdapter(
+                            SCREENSHOT_REMOTE_RUNNER, 0, 0);
+                    try {
+                        WindowManagerGlobal.getWindowManagerService()
+                                .overridePendingAppTransitionRemote(runner, DEFAULT_DISPLAY);
+                    } catch (Exception e) {
+                        Log.e(TAG, "Error overriding screenshot app transition", e);
+                    }
                 }, mMainExecutor);
             });
         } catch (CancellationException e) {
@@ -883,8 +906,8 @@
         return () -> {
             Pair<ActivityOptions, ExitTransitionCoordinator> transition =
                     ActivityOptions.startSharedElementAnimation(
-                            mWindow, new ScreenshotExitTransitionCallbacks(), null,
-                            Pair.create(mScreenshotView.getScreenshotPreview(),
+                            mWindow, new ScreenshotExitTransitionCallbacksSupplier(true).get(),
+                            null, Pair.create(mScreenshotView.getScreenshotPreview(),
                                     ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME));
             transition.second.startExit();
 
@@ -970,19 +993,33 @@
         return matchWithinTolerance;
     }
 
-    private class ScreenshotExitTransitionCallbacks implements ExitTransitionCallbacks {
-        @Override
-        public boolean isReturnTransitionAllowed() {
-            return false;
+    private class ScreenshotExitTransitionCallbacksSupplier implements
+            Supplier<ExitTransitionCallbacks> {
+        final boolean mDismissOnHideSharedElements;
+
+        ScreenshotExitTransitionCallbacksSupplier(boolean dismissOnHideSharedElements) {
+            mDismissOnHideSharedElements = dismissOnHideSharedElements;
         }
 
         @Override
-        public void hideSharedElements() {
-            finishDismiss();
-        }
+        public ExitTransitionCallbacks get() {
+            return new ExitTransitionCallbacks() {
+                @Override
+                public boolean isReturnTransitionAllowed() {
+                    return false;
+                }
 
-        @Override
-        public void onFinish() {
+                @Override
+                public void hideSharedElements() {
+                    if (mDismissOnHideSharedElements) {
+                        finishDismiss();
+                    }
+                }
+
+                @Override
+                public void onFinish() {
+                }
+            };
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 2a22179..3c830cc 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -453,14 +453,6 @@
                 mCornerSizeX / (mOrientationPortrait ? bounds.width() : bounds.height());
         final float currentScale = 1 / cornerScale;
 
-        mScreenshotPreview.setScaleX(currentScale);
-        mScreenshotPreview.setScaleY(currentScale);
-
-        if (mAccessibilityManager.isEnabled()) {
-            mDismissButton.setAlpha(0);
-            mDismissButton.setVisibility(View.VISIBLE);
-        }
-
         AnimatorSet dropInAnimation = new AnimatorSet();
         ValueAnimator flashInAnimator = ValueAnimator.ofFloat(0, 1);
         flashInAnimator.setDuration(SCREENSHOT_FLASH_IN_DURATION_MS);
@@ -479,6 +471,11 @@
         final PointF finalPos = new PointF(targetPosition.exactCenterX(),
                 targetPosition.exactCenterY());
 
+        // Shift to screen coordinates so that the animation runs on top of the entire screen,
+        // including e.g. bars covering the display cutout.
+        int[] locInScreen = mScreenshotPreview.getLocationOnScreen();
+        startPos.offset(targetPosition.left - locInScreen[0], targetPosition.top - locInScreen[1]);
+
         if (DEBUG_ANIM) {
             Log.d(TAG, "toCorner: startPos=" + startPos);
             Log.d(TAG, "toCorner: finalPos=" + finalPos);
@@ -486,6 +483,20 @@
 
         ValueAnimator toCorner = ValueAnimator.ofFloat(0, 1);
         toCorner.setDuration(SCREENSHOT_TO_CORNER_Y_DURATION_MS);
+
+        toCorner.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mScreenshotPreview.setScaleX(currentScale);
+                mScreenshotPreview.setScaleY(currentScale);
+                mScreenshotPreview.setVisibility(View.VISIBLE);
+                if (mAccessibilityManager.isEnabled()) {
+                    mDismissButton.setAlpha(0);
+                    mDismissButton.setVisibility(View.VISIBLE);
+                }
+            }
+        });
+
         float xPositionPct =
                 SCREENSHOT_TO_CORNER_X_DURATION_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
         float dismissPct =
@@ -529,13 +540,6 @@
             }
         });
 
-        toCorner.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                mScreenshotPreview.setVisibility(View.VISIBLE);
-            }
-        });
-
         mScreenshotFlash.setAlpha(0f);
         mScreenshotFlash.setVisibility(View.VISIBLE);
 
@@ -759,7 +763,53 @@
         return r;
     }
 
+    void startLongScreenshotTransition(Rect destination, Runnable onTransitionEnd,
+            ScrollCaptureController.LongScreenshot longScreenshot) {
+        mScrollablePreview.setImageBitmap(longScreenshot.toBitmap());
+        ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+        float startX = mScrollablePreview.getX();
+        float startY = mScrollablePreview.getY();
+        int[] locInScreen = mScrollablePreview.getLocationOnScreen();
+        destination.offset((int) startX - locInScreen[0], (int) startY - locInScreen[1]);
+        mScrollablePreview.setPivotX(0);
+        mScrollablePreview.setPivotY(0);
+        mScrollablePreview.setAlpha(1f);
+        float currentScale = mScrollablePreview.getWidth() / (float) longScreenshot.getWidth();
+        Matrix matrix = new Matrix();
+        matrix.setScale(currentScale, currentScale);
+        matrix.postTranslate(
+                longScreenshot.getLeft() * currentScale, longScreenshot.getTop() * currentScale);
+        mScrollablePreview.setImageMatrix(matrix);
+        float destinationScale = destination.width() / (float) mScrollablePreview.getWidth();
+        anim.addUpdateListener(animation -> {
+            float t = animation.getAnimatedFraction();
+            mScrollingScrim.setAlpha(1 - t);
+            float currScale = MathUtils.lerp(1, destinationScale, t);
+            mScrollablePreview.setScaleX(currScale);
+            mScrollablePreview.setScaleY(currScale);
+            mScrollablePreview.setX(MathUtils.lerp(startX, destination.left, t));
+            mScrollablePreview.setY(MathUtils.lerp(startY, destination.top, t));
+        });
+        anim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                onTransitionEnd.run();
+                mScrollablePreview.animate().alpha(0).setListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        super.onAnimationEnd(animation);
+                        mCallbacks.onDismiss();
+                    }
+                });
+            }
+        });
+        anim.start();
+    }
+
     void prepareScrollingTransition(ScrollCaptureResponse response, Bitmap screenBitmap) {
+        mScrollingScrim.setImageBitmap(screenBitmap);
+        mScrollingScrim.setVisibility(View.VISIBLE);
         Rect scrollableArea = scrollableAreaOnScreen(response);
         float scale = mCornerSizeX
                 / (mOrientationPortrait ? screenBitmap.getWidth() : screenBitmap.getHeight());
@@ -770,14 +820,12 @@
         params.height = (int) (scale * scrollableArea.height());
         Matrix matrix = new Matrix();
         matrix.setScale(scale, scale);
-        matrix.postTranslate(0, -scrollableArea.top * scale);
+        matrix.postTranslate(-scrollableArea.left * scale, -scrollableArea.top * scale);
 
         mScrollablePreview.setTranslationX(scale * scrollableArea.left);
         mScrollablePreview.setTranslationY(scale * scrollableArea.top);
         mScrollablePreview.setImageMatrix(matrix);
 
-        mScrollingScrim.setImageBitmap(screenBitmap);
-        mScrollingScrim.setVisibility(View.VISIBLE);
         mScrollablePreview.setImageBitmap(screenBitmap);
         mScrollablePreview.setVisibility(View.VISIBLE);
         createScreenshotFadeDismissAnimation(true).start();
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
index 94e3149..ce6e469 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
@@ -56,6 +56,7 @@
 public class ScrollCaptureClient {
     private static final int TILE_SIZE_PX_MAX = 4 * (1024 * 1024);
     private static final int TILES_PER_PAGE = 2; // increase once b/174571735 is addressed
+    private static final int MAX_TILES = 30;
 
     @VisibleForTesting
     static final int MATCH_ANY_TASK = ActivityTaskManager.INVALID_TASK_ID;
@@ -83,11 +84,12 @@
         int getMaxTiles();
 
         /**
-         * @return the maximum combined capture height for this session, in pixels.
+         * Target pixel height for acquisition this session. Session may yield more or less data
+         * than this, but acquiring this height is considered sufficient for completion.
+         *
+         * @return target height in pixels.
          */
-        default int getMaxHeight() {
-            return getMaxTiles() * getTileHeight();
-        }
+        int getTargetHeight();
 
         /**
          * @return the height of each image tile
@@ -234,11 +236,11 @@
         private final int mTileWidth;
         private Rect mRequestRect;
         private boolean mStarted;
+        private final int mTargetHeight;
 
         private ICancellationSignal mCancellationSignal;
         private final Rect mWindowBounds;
         private final Rect mBoundsInWindow;
-        private final int mMaxTiles;
 
         private Completer<Session> mStartCompleter;
         private Completer<CaptureResult> mTileRequestCompleter;
@@ -256,7 +258,7 @@
 
             mTileWidth = mBoundsInWindow.width();
             mTileHeight = pxPerTile / mBoundsInWindow.width();
-            mMaxTiles = (int) Math.ceil(maxPages * TILES_PER_PAGE);
+            mTargetHeight = (int) (mBoundsInWindow.height() * maxPages);
 
             if (DEBUG_SCROLL) {
                 Log.d(TAG, "boundsInWindow: " + mBoundsInWindow);
@@ -285,7 +287,7 @@
 
         private void start(Completer<Session> completer) {
             mReader = ImageReader.newInstance(mTileWidth, mTileHeight, PixelFormat.RGBA_8888,
-                    mMaxTiles, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
+                    MAX_TILES, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
             mStartCompleter = completer;
             try {
                 mCancellationSignal = mConnection.startCapture(mReader.getSurface(), this);
@@ -410,8 +412,13 @@
         }
 
         @Override
+        public int getTargetHeight() {
+            return mTargetHeight;
+        }
+
+        @Override
         public int getMaxTiles() {
-            return mMaxTiles;
+            return MAX_TILES;
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index bbcfdbd..4c1f6a1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -199,22 +199,20 @@
         Log.d(TAG, "onCaptureResult: " + result + " scrolling " + (mScrollingUp ? "UP" : "DOWN")
                 + " finish on boundary: " + mFinishOnBoundary);
         boolean emptyResult = result.captured.height() == 0;
-        boolean partialResult = !emptyResult
-                && result.captured.height() < result.requested.height();
-        boolean finish = false;
 
         if (emptyResult) {
             // Potentially reached a vertical boundary. Extend in the other direction.
             if (mFinishOnBoundary) {
-                Log.d(TAG, "Partial/empty: finished!");
-                finish = true;
+                Log.d(TAG, "Empty: finished!");
+                finishCapture();
+                return;
             } else {
                 // We hit a boundary, clear the tiles, capture everything in the opposite direction,
                 // then finish.
                 mImageTileSet.clear();
                 mFinishOnBoundary = true;
                 mScrollingUp = !mScrollingUp;
-                Log.d(TAG, "Partial/empty: cleared, switch direction to finish");
+                Log.d(TAG, "Empty: cleared, switch direction to finish");
             }
         } else {
             // Got a non-empty result, but may already have enough bitmap data now
@@ -223,12 +221,14 @@
                 Log.d(TAG, "Hit max tiles: finished");
                 // If we ever hit the max tiles, we've got enough bitmap data to finish
                 // (even if we weren't sure we'd finish on this pass).
-                finish = true;
+                finishCapture();
+                return;
             } else {
                 if (mScrollingUp && !mFinishOnBoundary) {
                     // During the initial scroll up, we only want to acquire the portion described
                     // by IDEAL_PORTION_ABOVE.
-                    if (expectedTiles >= mSession.getMaxTiles() * IDEAL_PORTION_ABOVE) {
+                    if (mImageTileSet.getHeight() + result.captured.height()
+                            >= mSession.getTargetHeight() * IDEAL_PORTION_ABOVE) {
                         Log.d(TAG, "Hit ideal portion above: clear and switch direction");
                         // We got enough above the start point, now see how far down it can go.
                         mImageTileSet.clear();
@@ -246,15 +246,15 @@
                 + " - " +  mImageTileSet.getRight() + "," + mImageTileSet.getBottom()
                 + " (" + mImageTileSet.getWidth() + "x" + mImageTileSet.getHeight() + ")");
 
-
-        // Stop when "too tall"
-        if (mImageTileSet.getHeight() > MAX_HEIGHT) {
-            Log.d(TAG, "Max height reached.");
-            finish = true;
+        Rect gapBounds = mImageTileSet.getGaps();
+        if (!gapBounds.isEmpty()) {
+            Log.d(TAG, "Found gaps in tileset: " + gapBounds + ", requesting " + gapBounds.top);
+            requestNextTile(gapBounds.top);
+            return;
         }
 
-        if (finish) {
-            Log.d(TAG, "Stop.");
+        if (mImageTileSet.getHeight() >= mSession.getTargetHeight()) {
+            Log.d(TAG, "Target height reached.");
             finishCapture();
             return;
         }
@@ -268,8 +268,8 @@
                     : result.requested.bottom;
         } else {
             nextTop = (mScrollingUp)
-                    ? result.captured.top - mSession.getTileHeight()
-                    : result.captured.bottom;
+                    ? mImageTileSet.getTop() - mSession.getTileHeight()
+                    : mImageTileSet.getBottom();
         }
         requestNextTile(nextTop);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
index 06c1c6f..e6d48676 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
@@ -218,7 +218,12 @@
     }
 
     private fun disableSensorPrivacy() {
-        sensorPrivacyController.setSensorBlocked(sensor, false)
+        if (sensor == ALL_SENSORS) {
+            sensorPrivacyController.setSensorBlocked(MICROPHONE, false)
+            sensorPrivacyController.setSensorBlocked(CAMERA, false)
+        } else {
+            sensorPrivacyController.setSensorBlocked(sensor, false)
+        }
         unsuppressImmediately = true
         setResult(RESULT_OK)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvUnblockSensorActivity.java b/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvUnblockSensorActivity.java
new file mode 100644
index 0000000..9d101ef
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvUnblockSensorActivity.java
@@ -0,0 +1,145 @@
+/*
+ * 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.sensorprivacy.television;
+
+import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
+import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
+
+import android.hardware.SensorPrivacyManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
+import com.android.systemui.tv.TvBottomSheetActivity;
+
+import javax.inject.Inject;
+
+/**
+ * Bottom sheet that is shown when the camera/mic sensors are blocked by the global toggle and
+ * allows the user to re-enable them.
+ */
+public class TvUnblockSensorActivity extends TvBottomSheetActivity {
+
+    private static final String TAG = TvUnblockSensorActivity.class.getSimpleName();
+
+    private static final int ALL_SENSORS = Integer.MAX_VALUE;
+    private int mSensor = -1;
+
+    private final IndividualSensorPrivacyController mSensorPrivacyController;
+    private IndividualSensorPrivacyController.Callback mSensorPrivacyCallback;
+
+    @Inject
+    public TvUnblockSensorActivity(
+            IndividualSensorPrivacyController individualSensorPrivacyController) {
+        mSensorPrivacyController = individualSensorPrivacyController;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        boolean allSensors = getIntent().getBooleanExtra(SensorPrivacyManager.EXTRA_ALL_SENSORS,
+                false);
+        if (allSensors) {
+            mSensor = ALL_SENSORS;
+        } else {
+            mSensor = getIntent().getIntExtra(SensorPrivacyManager.EXTRA_SENSOR, -1);
+        }
+
+        if (mSensor == -1) {
+            Log.v(TAG, "Invalid extras");
+            finish();
+            return;
+        }
+
+        mSensorPrivacyCallback = (sensor, blocked) -> {
+            if (mSensor == ALL_SENSORS) {
+                if (!mSensorPrivacyController.isSensorBlocked(CAMERA)
+                        && !mSensorPrivacyController.isSensorBlocked(MICROPHONE)) {
+                    finish();
+                }
+            } else if (this.mSensor == sensor && !blocked) {
+                finish();
+            }
+        };
+
+        initUI();
+    }
+
+    private void initUI() {
+        TextView title = findViewById(R.id.bottom_sheet_title);
+        TextView content = findViewById(R.id.bottom_sheet_body);
+        ImageView icon = findViewById(R.id.bottom_sheet_icon);
+        // mic icon if both icons are shown
+        ImageView secondIcon = findViewById(R.id.bottom_sheet_second_icon);
+        Button unblockButton = findViewById(R.id.bottom_sheet_positive_button);
+        Button cancelButton = findViewById(R.id.bottom_sheet_negative_button);
+
+        switch (mSensor) {
+            case MICROPHONE:
+                title.setText(R.string.sensor_privacy_start_use_mic_dialog_title);
+                content.setText(R.string.sensor_privacy_start_use_mic_dialog_content);
+                icon.setImageResource(com.android.internal.R.drawable.perm_group_microphone);
+                secondIcon.setVisibility(View.GONE);
+                break;
+            case CAMERA:
+                title.setText(R.string.sensor_privacy_start_use_camera_dialog_title);
+                content.setText(R.string.sensor_privacy_start_use_camera_dialog_content);
+                icon.setImageResource(com.android.internal.R.drawable.perm_group_camera);
+                secondIcon.setVisibility(View.GONE);
+                break;
+            case ALL_SENSORS:
+            default:
+                title.setText(R.string.sensor_privacy_start_use_mic_camera_dialog_title);
+                content.setText(R.string.sensor_privacy_start_use_mic_camera_dialog_content);
+                icon.setImageResource(com.android.internal.R.drawable.perm_group_camera);
+                secondIcon.setImageResource(com.android.internal.R.drawable.perm_group_microphone);
+                break;
+        }
+        unblockButton.setText(
+                com.android.internal.R.string.sensor_privacy_start_use_dialog_turn_on_button);
+        unblockButton.setOnClickListener(v -> {
+            if (mSensor == ALL_SENSORS) {
+                mSensorPrivacyController.setSensorBlocked(CAMERA, false);
+                mSensorPrivacyController.setSensorBlocked(MICROPHONE, false);
+            } else {
+                mSensorPrivacyController.setSensorBlocked(mSensor, false);
+            }
+        });
+
+        cancelButton.setText(android.R.string.cancel);
+        cancelButton.setOnClickListener(v -> finish());
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mSensorPrivacyController.addCallback(mSensorPrivacyCallback);
+    }
+
+    @Override
+    public void onPause() {
+        mSensorPrivacyController.removeCallback(mSensorPrivacyCallback);
+        super.onPause();
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
index ce796d9..89dda9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
@@ -22,6 +22,7 @@
 import android.content.res.Resources
 import android.util.IndentingPrintWriter
 import android.util.MathUtils
+import android.view.CrossWindowBlurListeners
 import android.view.SurfaceControl
 import android.view.ViewRootImpl
 import androidx.annotation.VisibleForTesting
@@ -37,6 +38,7 @@
 @SysUISingleton
 open class BlurUtils @Inject constructor(
     @Main private val resources: Resources,
+    private val crossWindowBlurListeners: CrossWindowBlurListeners,
     dumpManager: DumpManager
 ) : Dumpable {
     val minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius)
@@ -97,7 +99,8 @@
      * @return {@code true} when supported.
      */
     open fun supportsBlursOnWindows(): Boolean {
-        return CROSS_WINDOW_BLUR_SUPPORTED && ActivityManager.isHighEndGfx()
+        return CROSS_WINDOW_BLUR_SUPPORTED && ActivityManager.isHighEndGfx() &&
+                crossWindowBlurListeners.isCrossWindowBlurEnabled()
     }
 
     override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 4a4e990..6f4a73e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -18,6 +18,7 @@
 import com.android.systemui.Gefingerpoken
 import com.android.systemui.R
 import com.android.systemui.animation.Interpolators
+import com.android.systemui.biometrics.UdfpsKeyguardViewController
 import com.android.systemui.classifier.Classifier
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.dagger.SysUISingleton
@@ -109,6 +110,11 @@
     private var nextHideKeyguardNeedsNoAnimation = false
 
     /**
+     * The udfpsKeyguardViewController if it exists.
+     */
+    var udfpsKeyguardViewController: UdfpsKeyguardViewController? = null
+
+    /**
      * The touch helper responsible for the drag down animation.
      */
     val touchHelper = DragDownHelper(falsingManager, falsingCollector, this, context)
@@ -291,6 +297,7 @@
                     // Fade out all content only visible on the lockscreen
                     notificationPanelController.setKeyguardOnlyContentAlpha(1.0f - scrimProgress)
                     depthController.transitionToFullShadeProgress = scrimProgress
+                    udfpsKeyguardViewController?.setTransitionToFullShadeProgress(scrimProgress)
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index a69b8d6..25cbdc5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -245,7 +245,8 @@
         mMediaDataManager.addListener(new MediaDataManager.Listener() {
             @Override
             public void onMediaDataLoaded(@NonNull String key,
-                    @Nullable String oldKey, @NonNull MediaData data, boolean immediately) {
+                    @Nullable String oldKey, @NonNull MediaData data, boolean immediately,
+                    boolean isSsReactivated) {
             }
 
             @Override
@@ -318,7 +319,8 @@
         mMediaDataManager.addListener(new MediaDataManager.Listener() {
             @Override
             public void onMediaDataLoaded(@NonNull String key,
-                    @Nullable String oldKey, @NonNull MediaData data, boolean immediately) {
+                    @Nullable String oldKey, @NonNull MediaData data, boolean immediately,
+                    boolean isSsReactivated) {
             }
 
             @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 8900551..f03a9a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -96,6 +96,15 @@
     var globalActionsSpring = DepthAnimation()
     var showingHomeControls: Boolean = false
 
+    @VisibleForTesting
+    var brightnessMirrorSpring = DepthAnimation()
+    var brightnessMirrorVisible: Boolean = false
+        set(value) {
+            field = value
+            brightnessMirrorSpring.animateTo(if (value) blurUtils.blurRadiusOfRatio(1f)
+                else 0)
+        }
+
     var qsPanelExpansion = 0f
         set(value) {
             if (field == value) return
@@ -186,13 +195,16 @@
         var blur = max(shadeRadius.toInt(), globalActionsRadius)
 
         // Make blur be 0 if it is necessary to stop blur effect.
-        if (scrimsVisible) {
+        if (scrimsVisible || !blurUtils.supportsBlursOnWindows()) {
             blur = 0
         }
+        val zoomOut = blurUtils.ratioOfBlurRadius(blur)
+
+        // Brightness slider removes blur, but doesn't affect zooms
+        blur = (blur * (1f - brightnessMirrorSpring.ratio)).toInt()
 
         val opaque = scrimsVisible && !ignoreShadeBlurUntilHidden
         blurUtils.applyBlur(blurRoot?.viewRootImpl ?: root.viewRootImpl, blur, opaque)
-        val zoomOut = blurUtils.ratioOfBlurRadius(blur)
         try {
             if (root.isAttachedToWindow && root.windowToken != null) {
                 wallpaperManager.setWallpaperZoomOut(root.windowToken, zoomOut)
@@ -260,6 +272,7 @@
                 shadeSpring.finishIfRunning()
                 shadeAnimation.finishIfRunning()
                 globalActionsSpring.finishIfRunning()
+                brightnessMirrorSpring.finishIfRunning()
             }
         }
 
@@ -425,6 +438,7 @@
             it.println("shadeRadius: ${shadeSpring.radius}")
             it.println("shadeAnimation: ${shadeAnimation.radius}")
             it.println("globalActionsRadius: ${globalActionsSpring.radius}")
+            it.println("brightnessMirrorRadius: ${brightnessMirrorSpring.radius}")
             it.println("wakeAndUnlockBlur: $wakeAndUnlockBlurRadius")
             it.println("ignoreShadeBlurUntilHidden: $ignoreShadeBlurUntilHidden")
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt
index f1479a1..f19cf5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt
@@ -22,11 +22,27 @@
     )
 
     var startTranslationZ = 0f
+
+    /**
+     * The top position of the notification at the start of the animation. This is needed in order
+     * to keep the notification at its place when launching a notification that is clipped rounded.
+     */
+    var startNotificationTop = 0f
     var startClipTopAmount = 0
     var parentStartClipTopAmount = 0
     var progress = 0f
     var linearProgress = 0f
 
+    /**
+     * The rounded top clipping at the beginning.
+     */
+    var startRoundedTopClipping = 0
+
+    /**
+     * The rounded top clipping of the parent notification at the start.
+     */
+    var parentStartRoundedTopClipping = 0
+
     override val topChange: Int
         get() {
             // We need this compensation to ensure that the QS moves in sync.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
index c248670..1bbef25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
@@ -40,6 +40,11 @@
     private val headsUpManager: HeadsUpManagerPhone,
     private val notification: ExpandableNotificationRow
 ) : ActivityLaunchAnimator.Controller {
+
+    companion object {
+        const val ANIMATION_DURATION_TOP_ROUNDING = 100L
+    }
+
     private val notificationEntry = notification.entry
     private val notificationKey = notificationEntry.sbn.key
 
@@ -54,18 +59,37 @@
         val height = max(0, notification.actualHeight - notification.clipBottomAmount)
         val location = notification.locationOnScreen
 
+        val clipStartLocation = notificationListContainer.getTopClippingStartLocation()
+        val roundedTopClipping = Math.max(clipStartLocation - location[1], 0)
+        val windowTop = location[1] + roundedTopClipping
+        val topCornerRadius = if (roundedTopClipping > 0) {
+            // Because the rounded Rect clipping is complex, we start the top rounding at
+            // 0, which is pretty close to matching the real clipping.
+            // We'd have to clipOut the overlaid drawable too with the outer rounded rect in case
+            // if we'd like to have this perfect, but this is close enough.
+            0f
+        } else {
+            notification.currentBackgroundRadiusTop
+        }
         val params = ExpandAnimationParameters(
-                top = location[1],
+                top = windowTop,
                 bottom = location[1] + height,
                 left = location[0],
                 right = location[0] + notification.width,
-                topCornerRadius = notification.currentBackgroundRadiusTop,
+                topCornerRadius = topCornerRadius,
                 bottomCornerRadius = notification.currentBackgroundRadiusBottom
         )
 
         params.startTranslationZ = notification.translationZ
+        params.startNotificationTop = notification.translationY
+        params.startRoundedTopClipping = roundedTopClipping
         params.startClipTopAmount = notification.clipTopAmount
         if (notification.isChildInGroup) {
+            params.startNotificationTop += notification.notificationParent.translationY
+            val parentRoundedClip = Math.max(clipStartLocation
+                - notification.notificationParent.locationOnScreen[1], 0)
+            params.parentStartRoundedTopClipping = parentRoundedClip
+
             val parentClip = notification.notificationParent.clipTopAmount
             params.parentStartClipTopAmount = parentClip
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index c24c2be..4f3406c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -203,6 +203,14 @@
         mBackgroundNormal.setCustomBackground(R.drawable.notification_material_bg);
     }
 
+    protected boolean hideBackground() {
+        return false;
+    }
+
+    protected void updateBackground() {
+        mBackgroundNormal.setVisibility(hideBackground() ? INVISIBLE : VISIBLE);
+    }
+
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index ba28dc5..76f9fe7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -35,6 +35,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.graphics.Canvas;
 import android.graphics.Path;
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.AnimationDrawable;
@@ -85,6 +86,7 @@
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
 import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
+import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
@@ -133,7 +135,7 @@
 
     private boolean mUpdateBackgroundOnUpdate;
     private boolean mNotificationTranslationFinished = false;
-    private ArrayList<MenuItem> mSnoozedMenuItems;
+    private boolean mIsSnoozed;
 
     /**
      * Listener for when {@link ExpandableNotificationRow} is laid out.
@@ -252,6 +254,7 @@
     private OnExpandClickListener mOnExpandClickListener;
     private View.OnClickListener mOnAppClickListener;
     private View.OnClickListener mOnFeedbackClickListener;
+    private Path mExpandingClipPath;
 
     // Listener will be called when receiving a long click event.
     // Use #setLongPressPosition to optionally assign positional data with the long press.
@@ -836,6 +839,7 @@
     public void setIsChildInGroup(boolean isChildInGroup, ExpandableNotificationRow parent) {
         if (mExpandAnimationRunning && !isChildInGroup && mNotificationParent != null) {
             mNotificationParent.setChildIsExpanding(false);
+            mNotificationParent.setExpandingClipPath(null);
             mNotificationParent.setExtraWidthForClipping(0.0f);
             mNotificationParent.setMinimumHeightForClipping(0);
         }
@@ -1105,8 +1109,7 @@
                     false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
                     false /* resetMenu */);
             mNotificationGutsManager.openGuts(this, 0, 0, item);
-            mSnoozedMenuItems = mMenuRow.getMenuItems(mMenuRow.getMenuView().getContext());
-            mMenuRow.resetMenu();
+            mIsSnoozed = true;
         };
     }
 
@@ -1821,10 +1824,7 @@
 
     void onGutsClosed() {
         updateContentAccessibilityImportanceForGuts(true /* isEnabled */);
-        if (mSnoozedMenuItems != null && mSnoozedMenuItems.size() > 0) {
-            mMenuRow.setMenuItems(mSnoozedMenuItems);
-            mSnoozedMenuItems = null;
-        }
+        mIsSnoozed = false;
     }
 
     /**
@@ -2036,7 +2036,22 @@
         setTranslationZ(translationZ);
         float extraWidthForClipping = params.getWidth() - getWidth();
         setExtraWidthForClipping(extraWidthForClipping);
-        int top = params.getTop();
+        int top;
+        if (params.getStartRoundedTopClipping() > 0) {
+            // If we were clipping initially, let's interpolate from the start position to the
+            // top. Otherwise, we just take the top directly.
+            float expandProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
+                    params.getProgress(0,
+                            NotificationLaunchAnimatorController.ANIMATION_DURATION_TOP_ROUNDING));
+            float startTop = params.getStartNotificationTop();
+            top = (int) Math.min(MathUtils.lerp(startTop,
+                    params.getTop(), expandProgress),
+                    startTop);
+        } else {
+            top = params.getTop();
+        }
+        int actualHeight = params.getBottom() - top;
+        setActualHeight(actualHeight);
         int startClipTopAmount = params.getStartClipTopAmount();
         int clipTopAmount = (int) MathUtils.lerp(startClipTopAmount, 0, params.getProgress());
         if (mNotificationParent != null) {
@@ -2065,13 +2080,12 @@
             setClipTopAmount(clipTopAmount);
         }
         setTranslationY(top);
-        setActualHeight(params.getHeight());
 
         mTopRoundnessDuringExpandAnimation = params.getTopCornerRadius() / mOutlineRadius;
         mBottomRoundnessDuringExpandAnimation = params.getBottomCornerRadius() / mOutlineRadius;
         invalidateOutline();
 
-        mBackgroundNormal.setExpandAnimationParams(params);
+        mBackgroundNormal.setExpandAnimationSize(params.getWidth(), actualHeight);
     }
 
     public void setExpandAnimationRunning(boolean expandAnimationRunning) {
@@ -2890,6 +2904,12 @@
             mShowNoBackground = false;
         }
         updateOutline();
+        updateBackground();
+    }
+
+    @Override
+    protected boolean hideBackground() {
+        return mShowNoBackground || super.hideBackground();
     }
 
     public int getPositionOfChild(ExpandableNotificationRow childRow) {
@@ -2975,7 +2995,7 @@
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfoInternal(info);
         info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
-        if (canViewBeDismissed()) {
+        if (canViewBeDismissed() && !mIsSnoozed) {
             info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS);
         }
         boolean expandable = shouldShowPublic();
@@ -2991,7 +3011,7 @@
                 isExpanded = isExpanded();
             }
         }
-        if (expandable) {
+        if (expandable && !mIsSnoozed) {
             if (isExpanded) {
                 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE);
             } else {
@@ -3079,6 +3099,26 @@
         return super.childNeedsClipping(child);
     }
 
+    /**
+     * Set a clip path to be set while expanding the notification. This is needed to nicely
+     * clip ourselves during the launch if we were clipped rounded in the beginning
+     */
+    public void setExpandingClipPath(Path path) {
+        mExpandingClipPath = path;
+        invalidate();
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        canvas.save();
+        if (mExpandingClipPath != null && (mExpandAnimationRunning || mChildIsExpanding)) {
+            // If we're launching a notification, let's clip if a clip rounded to the clipPath
+            canvas.clipPath(mExpandingClipPath);
+        }
+        super.dispatchDraw(canvas);
+        canvas.restore();
+    }
+
     @Override
     protected void applyRoundness() {
         super.applyRoundness();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 754de58..0f615aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -240,10 +240,10 @@
         invalidate();
     }
 
-    /** Set the current expand animation parameters. */
-    public void setExpandAnimationParams(ExpandAnimationParameters params) {
-        mActualHeight = params.getHeight();
-        mActualWidth = params.getWidth();
+    /** Set the current expand animation size. */
+    public void setExpandAnimationSize(int actualWidth, int actualHeight) {
+        mActualHeight = actualHeight;
+        mActualWidth = actualWidth;
         invalidate();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 26606cd..197920f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -72,8 +72,6 @@
     private boolean mUnlockHintRunning;
     private boolean mQsCustomizerShowing;
     private int mIntrinsicPadding;
-    private int mExpandAnimationTopChange;
-    private ExpandableNotificationRow mExpandingNotification;
     private float mHideAmount;
     private boolean mAppearing;
     private float mPulseHeight = MAX_PULSE_HEIGHT;
@@ -518,22 +516,6 @@
         return isDozing() && !isPulsing(row.getEntry());
     }
 
-    public void setExpandAnimationTopChange(int expandAnimationTopChange) {
-        mExpandAnimationTopChange = expandAnimationTopChange;
-    }
-
-    public void setExpandingNotification(ExpandableNotificationRow row) {
-        mExpandingNotification = row;
-    }
-
-    public ExpandableNotificationRow getExpandingNotification() {
-        return mExpandingNotification;
-    }
-
-    public int getExpandAnimationTopChange() {
-        return mExpandAnimationTopChange;
-    }
-
     /**
      * @return {@code true } when shade is completely hidden: in AOD, ambient display or when
      * bypassing.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index f2e39e7..c2716b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -725,7 +725,7 @@
             tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
             float alpha = mChildrenExpanded && viewState.alpha != 0 ? mDividerAlpha : 0;
             if (mUserLocked && !showingAsLowPriority() && viewState.alpha != 0) {
-                alpha = NotificationUtils.interpolate(0, 0.5f,
+                alpha = NotificationUtils.interpolate(0, mDividerAlpha,
                         Math.min(viewState.alpha, expandFraction));
             }
             tmpState.hidden = !dividersVisible;
@@ -802,9 +802,9 @@
             View divider = mDividers.get(i);
             tmpState.initFrom(divider);
             tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
-            float alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
+            float alpha = mChildrenExpanded && viewState.alpha != 0 ? mDividerAlpha : 0;
             if (mUserLocked && !showingAsLowPriority() && viewState.alpha != 0) {
-                alpha = NotificationUtils.interpolate(0, 0.5f,
+                alpha = NotificationUtils.interpolate(0, mDividerAlpha,
                         Math.min(viewState.alpha, expandFraction));
             }
             tmpState.hidden = !dividersVisible;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
index 2a2e733f7..7a5c188 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
@@ -200,4 +200,11 @@
     default void setWillExpand(boolean willExpand) {}
 
     void setNotificationActivityStarter(NotificationActivityStarter notificationActivityStarter);
+
+    /**
+     * @return the start location where we start clipping notifications.
+     */
+    default int getTopClippingStartLocation() {
+        return 0;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index f0201cb..4e6d376 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -77,6 +77,7 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.ExpandHelper;
 import com.android.systemui.R;
+import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.animation.Interpolators;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
 import com.android.systemui.statusbar.CommandQueue;
@@ -90,6 +91,7 @@
 import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
 import com.android.systemui.statusbar.notification.FakeShadowView;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.ShadeViewRefactor;
 import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent;
@@ -111,6 +113,7 @@
 import com.android.systemui.statusbar.policy.HeadsUpUtil;
 import com.android.systemui.statusbar.policy.ScrollAdapter;
 import com.android.systemui.util.Assert;
+import com.android.systemui.util.leak.RotationUtils;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -138,6 +141,9 @@
     // adb shell setprop persist.debug.nssl true && adb reboot
     private static final boolean DEBUG = SystemProperties.getBoolean("persist.debug.nssl",
             false /* default */);
+    // TODO(b/187291379) disable again before release
+    private static final boolean DEBUG_REMOVE_ANIMATION = SystemProperties.getBoolean(
+            "persist.debug.nssl.dismiss", true /* default */);
 
     private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
     private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
@@ -421,6 +427,9 @@
         animateScroll();
     };
     private int mCornerRadius;
+    private int mMinimumPaddings;
+    private int mQsTilePadding;
+    private boolean mSkinnyNotifsInLandscape;
     private int mSidePaddings;
     private final Rect mBackgroundAnimationRect = new Rect();
     private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
@@ -467,6 +476,12 @@
     private final Path mRoundedClipPath = new Path();
 
     /**
+     * The clip Path used to clip the launching notification. This may be different
+     * from the normal path, as the views launch animation could start clipped.
+     */
+    private final Path mLaunchedNotificationClipPath = new Path();
+
+    /**
      * Should we use rounded rect clipping right now
      */
     private boolean mShouldUseRoundedRectClipping = false;
@@ -489,6 +504,26 @@
     private boolean mLaunchingNotification;
 
     /**
+     * Does the launching notification need to be clipped
+     */
+    private boolean mLaunchingNotificationNeedsToBeClipped;
+
+    /**
+     * The current launch animation params when launching a notification
+     */
+    private ExpandAnimationParameters mLaunchAnimationParams;
+
+    /**
+     * Corner radii of the launched notification if it's clipped
+     */
+    private float[] mLaunchedNotificationRadii = new float[8];
+
+    /**
+     * The notification that is being launched currently.
+     */
+    private ExpandableNotificationRow mExpandingNotificationRow;
+
+    /**
      * Do notifications dismiss with normal transitioning
      */
     private boolean mDismissUsingRowTranslationX = true;
@@ -896,7 +931,10 @@
                 R.dimen.min_top_overscroll_to_qs);
         mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height);
         mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
-        mSidePaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
+        mMinimumPaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
+        mQsTilePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_margin_horizontal);
+        mSkinnyNotifsInLandscape = res.getBoolean(R.bool.config_skinnyNotifsInLandscape);
+        mSidePaddings = mMinimumPaddings;  // Updated in onMeasure by updateSidePadding()
         mMinInteractionHeight = res.getDimensionPixelSize(
                 R.dimen.notification_min_interaction_height);
         mCornerRadius = res.getDimensionPixelSize(R.dimen.notification_corner_radius);
@@ -906,6 +944,21 @@
                 com.android.internal.R.dimen.quick_qs_offset_height);
     }
 
+    void updateSidePadding(int viewWidth) {
+        if (viewWidth == 0 || !mSkinnyNotifsInLandscape) {
+            mSidePaddings = mMinimumPaddings;
+            return;
+        }
+        // Portrait is easy, just use the dimen for paddings
+        if (RotationUtils.getRotation(mContext) == RotationUtils.ROTATION_NONE) {
+            mSidePaddings = mMinimumPaddings;
+            return;
+        }
+        final int innerWidth = viewWidth - mMinimumPaddings * 2;
+        final int qsTileWidth = (innerWidth - mQsTilePadding * 3) / 4;
+        mSidePaddings = mMinimumPaddings + qsTileWidth + mQsTilePadding;
+    }
+
     void updateCornerRadius() {
         int newRadius = getResources().getDimensionPixelSize(R.dimen.notification_corner_radius);
         if (mCornerRadius != newRadius) {
@@ -966,6 +1019,7 @@
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 
         int width = MeasureSpec.getSize(widthMeasureSpec);
+        updateSidePadding(width);
         int childWidthSpec = MeasureSpec.makeMeasureSpec(width - mSidePaddings * 2,
                 MeasureSpec.getMode(widthMeasureSpec));
         // Don't constrain the height of the children so we know how big they'd like to be
@@ -2117,7 +2171,7 @@
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private void updateContentHeight() {
-        final float scrimTopPadding = mAmbientState.isOnKeyguard() ? 0 : mSidePaddings;
+        final float scrimTopPadding = mAmbientState.isOnKeyguard() ? 0 : mMinimumPaddings;
         int height = (int) scrimTopPadding;
         float previousPaddingRequest = mPaddingBetweenElements;
         int numShownItems = 0;
@@ -2624,7 +2678,17 @@
      */
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     boolean generateRemoveAnimation(ExpandableView child) {
+        String key = "";
+        if (DEBUG_REMOVE_ANIMATION) {
+            if (child instanceof ExpandableNotificationRow) {
+                key = ((ExpandableNotificationRow) child).getEntry().getKey();
+            }
+            Log.d(TAG, "generateRemoveAnimation " + key);
+        }
         if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
+            if (DEBUG_REMOVE_ANIMATION) {
+                Log.d(TAG, "removedBecauseOfHeadsUp " + key);
+            }
             mAddedHeadsUpChildren.remove(child);
             return false;
         }
@@ -2633,8 +2697,17 @@
             mClearTransientViewsWhenFinished.add(child);
             return true;
         }
+        if (DEBUG_REMOVE_ANIMATION) {
+            Log.d(TAG, "generateRemove " + key
+                    + "\nmIsExpanded " + mIsExpanded
+                    + "\nmAnimationsEnabled " + mAnimationsEnabled
+                    + "\n!invisible group " + !isChildInInvisibleGroup(child));
+        }
         if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
             if (!mChildrenToAddAnimated.contains(child)) {
+                if (DEBUG_REMOVE_ANIMATION) {
+                    Log.d(TAG, "needsAnimation = true " + key);
+                }
                 // Generate Animations
                 mChildrenToRemoveAnimated.add(child);
                 mNeedsAnimation = true;
@@ -2656,7 +2729,7 @@
     /**
      * Remove a removed child view from the heads up animations if it was just added there
      *
-     * @return whether any child was removed from the list to animate
+     * @return whether any child was removed from the list to animate and the view was just added
      */
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     private boolean removeRemovedChildFromHeadsUpChangeAnimations(View child) {
@@ -2675,7 +2748,7 @@
             ((ExpandableNotificationRow) child).setHeadsUpAnimatingAway(false);
         }
         mTmpList.clear();
-        return hasAddEvent;
+        return hasAddEvent && mAddedHeadsUpChildren.contains(child);
     }
 
     // TODO (b/162832756): remove since this won't happen in new pipeline (we prune groups in
@@ -2726,6 +2799,9 @@
      * @return the amount of scrolling needed to start clipping notifications.
      */
     private int getScrollAmountToScrollBoundary() {
+        if (mShouldUseSplitNotificationShade) {
+            return mSidePaddings;
+        }
         return mTopPadding - mQsScrollBoundaryPosition;
     }
 
@@ -2869,7 +2945,16 @@
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     void setExpandingNotification(ExpandableNotificationRow row) {
-        mAmbientState.setExpandingNotification(row);
+        if (mExpandingNotificationRow != null && row == null) {
+            // Let's unset the clip path being set during launch
+            mExpandingNotificationRow.setExpandingClipPath(null);
+            ExpandableNotificationRow parent = mExpandingNotificationRow.getNotificationParent();
+            if (parent != null) {
+                parent.setExpandingClipPath(null);
+            }
+        }
+        mExpandingNotificationRow = row;
+        updateLaunchedNotificationClipPath();
         requestChildrenUpdate();
     }
 
@@ -2879,10 +2964,10 @@
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     public void applyExpandAnimationParams(ExpandAnimationParameters params) {
-        mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange());
-
-        // Disable clipping for launches
+        // Modify the clipping for launching notifications
+        mLaunchAnimationParams = params;
         setLaunchingNotification(params != null);
+        updateLaunchedNotificationClipPath();
         requestChildrenUpdate();
     }
 
@@ -5330,7 +5415,15 @@
             return;
         }
         mLaunchingNotification = launching;
-        updateUseRoundedRectClipping();
+        mLaunchingNotificationNeedsToBeClipped = mLaunchAnimationParams != null
+                && (mLaunchAnimationParams.getStartRoundedTopClipping() > 0
+                        || mLaunchAnimationParams.getParentStartRoundedTopClipping() > 0);
+        if (!mLaunchingNotificationNeedsToBeClipped || !mLaunchingNotification) {
+            mLaunchedNotificationClipPath.reset();
+        }
+        // When launching notifications, we're clipping the children individually instead of in
+        // dispatchDraw
+        invalidate();
     }
 
     /**
@@ -5340,22 +5433,97 @@
         // We don't want to clip notifications when QS is expanded, because incoming heads up on
         // the bottom would be clipped otherwise
         boolean qsAllowsClipping = mQsExpansionFraction < 0.5f || mShouldUseSplitNotificationShade;
-        boolean clip = !mLaunchingNotification && mIsExpanded && qsAllowsClipping;
+        boolean clip = mIsExpanded && qsAllowsClipping;
         if (clip != mShouldUseRoundedRectClipping) {
             mShouldUseRoundedRectClipping = clip;
             invalidate();
         }
     }
 
+    /**
+     * Update the clip path for launched notifications in case they were originally clipped
+     */
+    private void updateLaunchedNotificationClipPath() {
+        if (!mLaunchingNotificationNeedsToBeClipped || !mLaunchingNotification
+                || mExpandingNotificationRow == null) {
+            return;
+        }
+        int left = Math.min(mLaunchAnimationParams.getLeft(), mRoundedRectClippingLeft);
+        int right = Math.max(mLaunchAnimationParams.getRight(), mRoundedRectClippingRight);
+        int bottom = Math.max(mLaunchAnimationParams.getBottom(), mRoundedRectClippingBottom);
+        float expandProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
+                mLaunchAnimationParams.getProgress(0,
+                        NotificationLaunchAnimatorController.ANIMATION_DURATION_TOP_ROUNDING));
+        int top = (int) Math.min(MathUtils.lerp(mRoundedRectClippingTop,
+                mLaunchAnimationParams.getTop(), expandProgress),
+                mRoundedRectClippingTop);
+        float topRadius = mLaunchAnimationParams.getTopCornerRadius();
+        float bottomRadius = mLaunchAnimationParams.getBottomCornerRadius();
+        mLaunchedNotificationRadii[0] = topRadius;
+        mLaunchedNotificationRadii[1] = topRadius;
+        mLaunchedNotificationRadii[2] = topRadius;
+        mLaunchedNotificationRadii[3] = topRadius;
+        mLaunchedNotificationRadii[4] = bottomRadius;
+        mLaunchedNotificationRadii[5] = bottomRadius;
+        mLaunchedNotificationRadii[6] = bottomRadius;
+        mLaunchedNotificationRadii[7] = bottomRadius;
+        mLaunchedNotificationClipPath.reset();
+        mLaunchedNotificationClipPath.addRoundRect(left, top, right, bottom,
+                mLaunchedNotificationRadii, Path.Direction.CW);
+        // Offset into notification clip coordinates instead of parent ones.
+        // This is needed since the notification changes in translationZ, where clipping via
+        // canvas dispatching won't work.
+        ExpandableNotificationRow expandingRow = mExpandingNotificationRow;
+        if (expandingRow.getNotificationParent() != null) {
+            expandingRow = expandingRow.getNotificationParent();
+        }
+        mLaunchedNotificationClipPath.offset(
+                -expandingRow.getLeft() - expandingRow.getTranslationX(),
+                -expandingRow.getTop() - expandingRow.getTranslationY());
+        expandingRow.setExpandingClipPath(mLaunchedNotificationClipPath);
+        if (mShouldUseRoundedRectClipping) {
+            invalidate();
+        }
+    }
+
     @Override
     protected void dispatchDraw(Canvas canvas) {
-        if (mShouldUseRoundedRectClipping) {
+        if (mShouldUseRoundedRectClipping && !mLaunchingNotification) {
+            // When launching notifications, we're clipping the children individually instead of in
+            // dispatchDraw
             // Let's clip rounded.
             canvas.clipPath(mRoundedClipPath);
         }
         super.dispatchDraw(canvas);
     }
 
+    @Override
+    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+        if (mShouldUseRoundedRectClipping && mLaunchingNotification) {
+            // Let's clip children individually during notification launch
+            canvas.save();
+            ExpandableView expandableView = (ExpandableView) child;
+            Path clipPath;
+            if (expandableView.isExpandAnimationRunning()
+                    || ((ExpandableView) child).hasExpandingChild()) {
+                // When launching the notification, it is not clipped by this layout, but by the
+                // view itself. This is because the view is Translating in Z, where this clipPath
+                // wouldn't apply.
+                clipPath = null;
+            } else {
+                clipPath = mRoundedClipPath;
+            }
+            if (clipPath != null) {
+                canvas.clipPath(clipPath);
+            }
+            boolean result = super.drawChild(canvas, child, drawingTime);
+            canvas.restore();
+            return result;
+        } else {
+            return super.drawChild(canvas, child, drawingTime);
+        }
+    }
+
     /**
      * Calculate the total translation needed when dismissing.
      */
@@ -5370,6 +5538,13 @@
     }
 
     /**
+     * @return the start location where we start clipping notifications.
+     */
+    public int getTopClippingStartLocation() {
+        return mIsExpanded ? mQsScrollBoundaryPosition : 0;
+    }
+
+    /**
      * A listener that is notified when the empty space below the notifications is clicked on
      */
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 495eda7..e71f7db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -1533,6 +1533,11 @@
         }
 
         @Override
+        public int getTopClippingStartLocation() {
+            return mView.getTopClippingStartLocation();
+        }
+
+        @Override
         public View getContainerChildAt(int i) {
             return mView.getContainerChildAt(i);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
index 54ef623..b148eeb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
@@ -27,6 +27,7 @@
     private val listeners: MutableList<ConfigurationController.ConfigurationListener> = ArrayList()
     private val lastConfig = Configuration()
     private var density: Int = 0
+    private var smallestScreenWidth: Int = 0
     private var fontScale: Float = 0.toFloat()
     private val inCarMode: Boolean
     private var uiMode: Int = 0
@@ -38,6 +39,7 @@
         this.context = context
         fontScale = currentConfig.fontScale
         density = currentConfig.densityDpi
+        smallestScreenWidth = currentConfig.smallestScreenWidthDp
         inCarMode = currentConfig.uiMode and Configuration.UI_MODE_TYPE_MASK ==
                 Configuration.UI_MODE_TYPE_CAR
         uiMode = currentConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK
@@ -72,6 +74,14 @@
             this.fontScale = fontScale
         }
 
+        val smallestScreenWidth = newConfig.smallestScreenWidthDp
+        if (smallestScreenWidth != this.smallestScreenWidth) {
+            this.smallestScreenWidth = smallestScreenWidth
+            listeners.filterForEach({ this.listeners.contains(it) }) {
+                it.onSmallestScreenWidthChanged()
+            }
+        }
+
         val localeList = newConfig.locales
         if (localeList != this.localeList) {
             this.localeList = localeList
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 3e4177d3..fa5011e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -40,9 +40,9 @@
     private static float CLOCK_HEIGHT_WEIGHT = 0.7f;
 
     /**
-     * Margin between the bottom of the clock and the notification shade.
+     * Margin between the bottom of the status view and the notification shade.
      */
-    private int mClockNotificationsMargin;
+    private int mStatusViewBottomMargin;
 
     /**
      * Height of the parent view - display size in px.
@@ -153,8 +153,8 @@
      * Refreshes the dimension values.
      */
     public void loadDimens(Resources res) {
-        mClockNotificationsMargin = res.getDimensionPixelSize(
-                R.dimen.keyguard_clock_notifications_margin);
+        mStatusViewBottomMargin = res.getDimensionPixelSize(
+                R.dimen.keyguard_status_view_bottom_margin);
 
         mContainerTopPadding =
                 res.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) / 2;
@@ -181,7 +181,7 @@
         mNotificationStackHeight = notificationStackHeight;
         mPanelExpansion = panelExpansion;
         mHeight = parentHeight;
-        mKeyguardStatusHeight = keyguardStatusHeight;
+        mKeyguardStatusHeight = keyguardStatusHeight + mStatusViewBottomMargin;
         mUserSwitchHeight = userSwitchHeight;
         mUserSwitchPreferredY = userSwitchPreferredY;
         mHasCustomClock = hasCustomClock;
@@ -221,40 +221,15 @@
 
     public float getMinStackScrollerPadding() {
         return mBypassEnabled ? mUnlockedStackScrollerPadding
-                : mMinTopMargin + mKeyguardStatusHeight + mClockNotificationsMargin;
-    }
-
-    private int getMaxClockY() {
-        return mHeight / 2 - mKeyguardStatusHeight - mClockNotificationsMargin;
+                : mMinTopMargin + mKeyguardStatusHeight;
     }
 
     private int getExpandedPreferredClockY() {
         return mMinTopMargin + mUserSwitchHeight;
     }
 
-    /**
-     * Vertically align the clock and the shade in the available space considering only
-     * a percentage of the clock height defined by {@code CLOCK_HEIGHT_WEIGHT}.
-     * @return Clock Y in pixels.
-     */
-    public int getExpandedClockPosition() {
-        final int availableHeight = mMaxShadeBottom - mMinTopMargin;
-        final int containerCenter = mMinTopMargin + availableHeight / 2;
-
-        float y = containerCenter
-                - (mKeyguardStatusHeight + mUserSwitchHeight) * CLOCK_HEIGHT_WEIGHT
-                - mClockNotificationsMargin - mNotificationStackHeight / 2;
-        if (y < mMinTopMargin) {
-            y = mMinTopMargin;
-        }
-
-        // Don't allow the clock base to be under half of the screen
-        final float maxClockY = getMaxClockY();
-        if (y > maxClockY) {
-            y = maxClockY;
-        }
-
-        return (int) y;
+    public int getLockscreenStatusViewHeight() {
+        return mKeyguardStatusHeight;
     }
 
     private int getClockY(float panelExpansion, float darkAmount) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 6b864c9..41af80e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -602,6 +602,10 @@
         updateAodIconColors();
     }
 
+    public int getHeight() {
+        return mAodIcons == null ? 0 : mAodIcons.getHeight();
+    }
+
     public void appearAodIcons() {
         if (mAodIcons == null) {
             return;
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 aaddfca..5d31786 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -36,9 +36,11 @@
 import android.app.ActivityManager;
 import android.app.Fragment;
 import android.app.StatusBarManager;
+import android.content.ContentResolver;
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.database.ContentObserver;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.ColorFilter;
@@ -50,10 +52,12 @@
 import android.graphics.drawable.Drawable;
 import android.hardware.biometrics.BiometricSourceType;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.os.UserManager;
 import android.os.VibrationEffect;
+import android.provider.Settings;
 import android.util.Log;
 import android.util.MathUtils;
 import android.view.LayoutInflater;
@@ -210,6 +214,8 @@
             new MyOnHeadsUpChangedListener();
     private final HeightListener mHeightListener = new HeightListener();
     private final ConfigurationListener mConfigurationListener = new ConfigurationListener();
+    private final SettingsChangeObserver mSettingsChangeObserver;
+
     @VisibleForTesting final StatusBarStateListener mStatusBarStateListener =
             new StatusBarStateListener();
     private final BiometricUnlockController mBiometricUnlockController;
@@ -594,6 +600,8 @@
     private int mScreenCornerRadius;
     private boolean mQSAnimatingHiddenFromCollapsed;
 
+    private final ContentResolver mContentResolver;
+
     private final Executor mUiExecutor;
     private final SecureSettings mSecureSettings;
 
@@ -635,6 +643,7 @@
     @Inject
     public NotificationPanelViewController(NotificationPanelView view,
             @Main Resources resources,
+            @Main Handler handler,
             LayoutInflater layoutInflater,
             NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler,
             DynamicPrivacyController dynamicPrivacyController,
@@ -678,6 +687,7 @@
             TapAgainViewController tapAgainViewController,
             NavigationModeController navigationModeController,
             FragmentService fragmentService,
+            ContentResolver contentResolver,
             QuickAccessWalletController quickAccessWalletController,
             @Main Executor uiExecutor,
             SecureSettings secureSettings,
@@ -704,15 +714,12 @@
         mKeyguardStatusBarViewComponentFactory = keyguardStatusBarViewComponentFactory;
         mDepthController = notificationShadeDepthController;
         mFeatureFlags = featureFlags;
+        mContentResolver = contentResolver;
         mKeyguardQsUserSwitchComponentFactory = keyguardQsUserSwitchComponentFactory;
         mKeyguardUserSwitcherComponentFactory = keyguardUserSwitcherComponentFactory;
         mQSDetailDisplayer = qsDetailDisplayer;
         mFragmentService = fragmentService;
-        mKeyguardUserSwitcherEnabled = mResources.getBoolean(
-                com.android.internal.R.bool.config_keyguardUserSwitcher);
-        mKeyguardQsUserSwitchEnabled =
-                mKeyguardUserSwitcherEnabled && mResources.getBoolean(
-                        R.bool.config_keyguard_user_switch_opens_qs_details);
+        mSettingsChangeObserver = new SettingsChangeObserver(handler);
         mShouldUseSplitNotificationShade =
                 Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources);
         mView.setWillNotDraw(!DEBUG);
@@ -795,6 +802,7 @@
         }
 
         mMaxKeyguardNotifications = resources.getInteger(R.integer.keyguard_max_notification_count);
+        updateUserSwitcherFlags();
         onFinishInflate();
     }
 
@@ -1034,6 +1042,10 @@
                 view = mLayoutInflater.inflate(layoutId, mView, false);
                 mView.addView(view, index);
             } else {
+                // Add the stub back so we can re-inflate it again if necessary
+                ViewStub stub = new ViewStub(mView.getContext(), layoutId);
+                stub.setId(stubId);
+                mView.addView(stub, index);
                 view = null;
             }
         } else if (enabled) {
@@ -1061,6 +1073,7 @@
         updateResources();
 
         // Re-inflate the keyguard user switcher group.
+        updateUserSwitcherFlags();
         boolean isUserSwitcherEnabled = mUserManager.isUserSwitcherEnabled();
         boolean showQsUserSwitch = mKeyguardQsUserSwitchEnabled && isUserSwitcherEnabled;
         boolean showKeyguardUserSwitcher =
@@ -1260,7 +1273,7 @@
                 mNotificationStackScrollLayoutController.getIntrinsicContentHeight(),
                 expandedFraction,
                 totalHeight,
-                mKeyguardStatusViewController.getHeight(),
+                mKeyguardStatusViewController.getLockscreenHeight(),
                 userIconHeight,
                 userSwitcherPreferredY, hasCustomClock(),
                 hasVisibleNotifications, darkamount, mOverStretchAmount,
@@ -1443,7 +1456,6 @@
 
     private void setQsExpansionEnabled() {
         mQsExpansionEnabled = mQsExpansionEnabledPolicy && mQsExpansionEnabledAmbient;
-        Log.d(TAG, "Set qsExpansionEnabled: " + mQsExpansionEnabled);
         if (mQs == null) return;
         mQs.setHeaderClickable(mQsExpansionEnabled);
     }
@@ -2669,14 +2681,6 @@
 
     @Override
     protected int getMaxPanelHeight() {
-        if (mKeyguardBypassController.getBypassEnabled() && mBarState == KEYGUARD) {
-            return getMaxPanelHeightBypass();
-        } else {
-            return getMaxPanelHeightNonBypass();
-        }
-    }
-
-    private int getMaxPanelHeightNonBypass() {
         int min = mStatusBarMinHeight;
         if (!(mBarState == KEYGUARD)
                 && mNotificationStackScrollLayoutController.getNotGoneChildCount() == 0) {
@@ -2701,16 +2705,6 @@
         return maxHeight;
     }
 
-    private int getMaxPanelHeightBypass() {
-        int position =
-                mClockPositionAlgorithm.getExpandedClockPosition()
-                        + mKeyguardStatusViewController.getHeight();
-        if (mNotificationStackScrollLayoutController.getVisibleNotificationCount() != 0) {
-            position += mShelfHeight / 2.0f + mDarkIconSize / 2.0f;
-        }
-        return position;
-    }
-
     public boolean isInSettings() {
         return mQsExpanded;
     }
@@ -2782,11 +2776,8 @@
         maxHeight += mNotificationStackScrollLayoutController.getTopPaddingOverflow();
 
         if (mBarState == KEYGUARD) {
-            int
-                    minKeyguardPanelBottom =
-                    mClockPositionAlgorithm.getExpandedClockPosition()
-                            + mKeyguardStatusViewController.getHeight()
-                            + mNotificationStackScrollLayoutController.getIntrinsicContentHeight();
+            int minKeyguardPanelBottom = mClockPositionAlgorithm.getLockscreenStatusViewHeight()
+                    + mNotificationStackScrollLayoutController.getIntrinsicContentHeight();
             return Math.max(maxHeight, minKeyguardPanelBottom);
         } else {
             return maxHeight;
@@ -3336,7 +3327,7 @@
         }
         if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()) {
             // The expandedHeight is always the full panel Height when bypassing
-            expandedHeight = getMaxPanelHeightNonBypass();
+            expandedHeight = getMaxPanelHeight();
         }
         mNotificationStackScrollLayoutController.setExpandedHeight(expandedHeight);
         updateKeyguardBottomAreaAlpha();
@@ -3895,6 +3886,26 @@
         return false;
     }
 
+    private void updateUserSwitcherFlags() {
+        mKeyguardUserSwitcherEnabled = mResources.getBoolean(
+                com.android.internal.R.bool.config_keyguardUserSwitcher);
+        mKeyguardQsUserSwitchEnabled =
+                mKeyguardUserSwitcherEnabled && mResources.getBoolean(
+                        R.bool.config_keyguard_user_switch_opens_qs_details);
+    }
+
+    private void registerSettingsChangeListener() {
+        mContentResolver.registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.USER_SWITCHER_ENABLED),
+                /* notifyForDescendants */ false,
+                mSettingsChangeObserver
+        );
+    }
+
+    private void unregisterSettingsChangeListener() {
+        mContentResolver.unregisterContentObserver(mSettingsChangeObserver);
+    }
+
     private class OnHeightChangedListener implements ExpandableView.OnHeightChangedListener {
         @Override
         public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
@@ -4216,6 +4227,15 @@
         }
 
         @Override
+        public void onSmallestScreenWidthChanged() {
+            if (DEBUG) Log.d(TAG, "onSmallestScreenWidthChanged");
+
+            // Can affect multi-user switcher visibility as it depends on screen size by default:
+            // it is enabled only for devices with large screens (see config_keyguardUserSwitcher)
+            reInflateViews();
+        }
+
+        @Override
         public void onOverlayChanged() {
             if (DEBUG) Log.d(TAG, "onOverlayChanged");
             reInflateViews();
@@ -4228,6 +4248,21 @@
         }
     }
 
+    private class SettingsChangeObserver extends ContentObserver {
+
+        SettingsChangeObserver(Handler handler) {
+            super(handler);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            if (DEBUG) Log.d(TAG, "onSettingsChanged");
+
+            // Can affect multi-user switcher visibility
+            reInflateViews();
+        }
+    }
+
     private class StatusBarStateListener implements StateListener {
         @Override
         public void onStateChanged(int statusBarState) {
@@ -4343,10 +4378,12 @@
             mConfigurationListener.onThemeChanged();
             mFalsingManager.addTapListener(mFalsingTapListener);
             mKeyguardIndicationController.init();
+            registerSettingsChangeListener();
         }
 
         @Override
         public void onViewDetachedFromWindow(View v) {
+            unregisterSettingsChangeListener();
             mFragmentService.getFragmentHostManager(mView)
                             .removeTagListener(QS.TAG, mFragmentListener);
             mStatusBarStateController.removeCallback(mStatusBarStateListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index 1cb0be0..ed8fb31 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -47,6 +47,8 @@
     private View mKeyguardStatusBar;
     private boolean mQsExpanded;
     private boolean mCustomizerAnimating;
+    private boolean mCustomizing;
+    private boolean mDetailShowing;
 
     private int mBottomPadding;
     private int mStackScrollerMargin;
@@ -140,7 +142,18 @@
     }
 
     public void setCustomizerShowing(boolean isShowing) {
-        if (isShowing) {
+        mCustomizing = isShowing;
+        updateBottomMargin();
+        mStackScroller.setQsCustomizerShowing(isShowing);
+    }
+
+    public void setDetailShowing(boolean isShowing) {
+        mDetailShowing = isShowing;
+        updateBottomMargin();
+    }
+
+    private void updateBottomMargin() {
+        if (mCustomizing || mDetailShowing) {
             // Clear out bottom paddings/margins so the qs customization can be full height.
             setPadding(0, 0, 0, 0);
             setBottomMargin(mStackScroller, 0);
@@ -148,7 +161,6 @@
             setPadding(0, 0, 0, mBottomPadding);
             setBottomMargin(mStackScroller, mStackScrollerMargin);
         }
-        mStackScroller.setQsCustomizerShowing(isShowing);
     }
 
     private void setBottomMargin(View v, int bottomMargin) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 7a1e5cf..cfcea96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -551,11 +551,11 @@
      */
     public void setNotificationsBounds(float left, float top, float right, float bottom) {
         if (mClipsQsScrim) {
-            // notification scrim's rounded corners are anti-aliased, but clipping of the QS scrim
-            // can't be and it's causing jagged corners. That's why notification scrim needs
-            // to overlap QS scrim by one pixel - both vertically (top - 1) and
-            // horizontally (left - 1 and right + 1), see: b/186644628
-            mNotificationsScrim.setDrawableBounds(left - 1, top - 1, right + 1, bottom);
+            // notification scrim's rounded corners are anti-aliased, but clipping of the QS/behind
+            // scrim can't be and it's causing jagged corners. That's why notification scrim needs
+            // to overlap QS scrim by one pixel horizontally (left - 1 and right + 1)
+            // see: b/186644628
+            mNotificationsScrim.setDrawableBounds(left - 1, top, right + 1, bottom);
             mScrimBehind.setBottomEdgePosition((int) top);
         } else {
             mNotificationsScrim.setDrawableBounds(left, top, right, bottom);
@@ -1242,6 +1242,9 @@
         pw.println(mDefaultScrimAlpha);
         pw.print("  mExpansionFraction=");
         pw.println(mPanelExpansion);
+
+        pw.print("  mState.getMaxLightRevealScrimAlpha=");
+        pw.println(mState.getMaxLightRevealScrimAlpha());
     }
 
     public void setWallpaperSupportsAmbientMode(boolean wallpaperSupportsAmbientMode) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index e52e1fa..0681193 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -197,7 +197,7 @@
         }
 
         @Override
-        public float getBehindAlpha() {
+        public float getMaxLightRevealScrimAlpha() {
             return mWallpaperSupportsAmbientMode && !mHasBackdrop ? 0f : 1f;
         }
 
@@ -220,18 +220,11 @@
             mBlankScreen = mDisplayRequiresBlanking;
             mAnimationDuration = mWakeLockScreenSensorActive
                     ? ScrimController.ANIMATION_DURATION_LONG : ScrimController.ANIMATION_DURATION;
-
-            // Wake sensor will show the wallpaper, let's fade from black. Otherwise it will
-            // feel like the screen is flashing if the wallpaper is light.
-            if (mWakeLockScreenSensorActive && previousState == AOD) {
-                updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK);
-            }
         }
-
         @Override
-        public float getBehindAlpha() {
+        public float getMaxLightRevealScrimAlpha() {
             return mWakeLockScreenSensorActive ? ScrimController.WAKE_SENSOR_SCRIM_ALPHA
-                    : AOD.getBehindAlpha();
+                : AOD.getMaxLightRevealScrimAlpha();
         }
     },
 
@@ -351,6 +344,10 @@
         return mBehindAlpha;
     }
 
+    public float getMaxLightRevealScrimAlpha() {
+        return 1f;
+    }
+
     public float getNotifAlpha() {
         return mNotifAlpha;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 5d2fe52..3f07785 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1473,9 +1473,7 @@
      * @param why the reason for the wake up
      */
     public void wakeUpIfDozing(long time, View where, String why) {
-        if (mDozing && !(mKeyguardViewMediator.isAnimatingScreenOff()
-                || mUnlockedScreenOffAnimationController
-                    .isScreenOffLightRevealAnimationPlaying())) {
+        if (mDozing && !mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()) {
             mPowerManager.wakeUp(
                     time, PowerManager.WAKE_REASON_GESTURE, "com.android.systemui:" + why);
             mWakeUpComingFromTouch = true;
@@ -4444,6 +4442,8 @@
         } else {
             mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
         }
+        updateLightRevealScrimVisibility();
+
         Trace.endSection();
     }
 
@@ -4894,6 +4894,7 @@
             return;
         }
 
+        mLightRevealScrim.setAlpha(mScrimController.getState().getMaxLightRevealScrimAlpha());
         if (mFeatureFlags.useNewLockscreenAnimations()
                 && (mDozeParameters.getAlwaysOn() || mDozeParameters.isQuickPickupEnabled())) {
             mLightRevealScrim.setVisibility(View.VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index 52bf2d5..9a04d39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -172,7 +172,8 @@
         // We currently draw both the light reveal scrim, and the AOD UI, in the shade. If it's
         // already expanded and showing notifications/QS, the animation looks really messy. For now,
         // disable it if the notification panel is expanded.
-        if (statusBar.notificationPanelViewController.isFullyExpanded) {
+        if (!this::statusBar.isInitialized ||
+                statusBar.notificationPanelViewController.isFullyExpanded) {
             return false
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
index 399c850..a0edc7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
@@ -74,11 +74,13 @@
         mBrightnessMirror.setVisibility(View.VISIBLE);
         mVisibilityCallback.accept(true);
         mNotificationPanel.setPanelAlpha(0, true /* animate */);
+        mDepthController.setBrightnessMirrorVisible(true);
     }
 
     public void hideMirror() {
         mVisibilityCallback.accept(false);
         mNotificationPanel.setPanelAlpha(255, true /* animate */);
+        mDepthController.setBrightnessMirrorVisible(false);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
index 0a6cf7b..c2bd87c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
@@ -33,6 +33,7 @@
     interface ConfigurationListener {
         default void onConfigChanged(Configuration newConfig) {}
         default void onDensityOrFontScaleChanged() {}
+        default void onSmallestScreenWidthChanged() {}
         default void onOverlayChanged() {}
         default void onUiModeChanged() {}
         default void onThemeChanged() {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index ce08075..2ac5c1e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -387,15 +387,8 @@
             int qsTypeIcon = 0;
             IconState qsIcon = null;
             CharSequence description = null;
-            // Mobile icon will only be shown in the statusbar in 2 scenarios
-            // 1. Mobile is the default network, and it is validated
-            // 2. Mobile is the default network, it is not validated and there is no other
-            // non-Carrier WiFi networks available.
-            boolean maybeShowIcons = (mCurrentState.inetCondition == 1)
-                    || (mCurrentState.inetCondition == 0
-                            && !mNetworkController.isNonCarrierWifiNetworkAvailable());
             // Only send data sim callbacks to QS.
-            if (mCurrentState.dataSim && mCurrentState.isDefault && maybeShowIcons) {
+            if (mCurrentState.dataSim && mCurrentState.isDefault) {
                 qsTypeIcon =
                         (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.qsDataType : 0;
                 qsIcon = new IconState(mCurrentState.enabled
@@ -408,7 +401,7 @@
             boolean activityOut = mCurrentState.dataConnected
                     && !mCurrentState.carrierNetworkChangeMode
                     && mCurrentState.activityOut;
-            showDataIcon &= mCurrentState.dataSim && mCurrentState.isDefault && maybeShowIcons;
+            showDataIcon &= mCurrentState.dataSim && mCurrentState.isDefault;
             boolean showTriangle = showDataIcon && !mCurrentState.airplaneMode;
             int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0;
             showDataIcon |= mCurrentState.roaming;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 753def0..2406db3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -109,17 +109,10 @@
             contentDescription += ("," + mContext.getString(R.string.data_connection_no_internet));
         }
         if (mProviderModel) {
-            // WiFi icon will only be shown in the statusbar in 2 scenarios
-            // 1. WiFi is the default network, and it is validated
-            // 2. WiFi is the default network, it is not validated and there is no other
-            // non-Carrier WiFi networks available.
-            boolean maybeShowIcons = (mCurrentState.inetCondition == 1)
-                    || (mCurrentState.inetCondition == 0
-                            && !mNetworkController.isNonCarrierWifiNetworkAvailable());
             IconState statusIcon = new IconState(
-                    wifiVisible && maybeShowIcons, getCurrentIconId(), contentDescription);
+                    wifiVisible, getCurrentIconId(), contentDescription);
             IconState qsIcon = null;
-            if ((mCurrentState.isDefault && maybeShowIcons) || (!mNetworkController.isRadioOn()
+            if (mCurrentState.isDefault || (!mNetworkController.isRadioOn()
                     && !mNetworkController.isEthernetDefault())) {
                 qsIcon = new IconState(mCurrentState.connected,
                         mWifiTracker.isCaptivePortal ? R.drawable.ic_qs_wifi_disconnected
@@ -158,15 +151,8 @@
         if (mCurrentState.inetCondition == 0) {
             dataContentDescription = mContext.getString(R.string.data_connection_no_internet);
         }
-        // Mobile icon will only be shown in the statusbar in 2 scenarios
-        // 1. Mobile is the default network, and it is validated
-        // 2. Mobile is the default network, it is not validated and there is no other
-        // non-Carrier WiFi networks available.
-        boolean maybeShowIcons = (mCurrentState.inetCondition == 1)
-                || (mCurrentState.inetCondition == 0
-                        && !mNetworkController.isNonCarrierWifiNetworkAvailable());
         boolean sbVisible = mCurrentState.enabled && mCurrentState.connected
-                && maybeShowIcons && mCurrentState.isDefault;
+                && mCurrentState.isDefault;
         IconState statusIcon =
                 new IconState(sbVisible, getCurrentIconIdForCarrierWifi(), contentDescription);
         int typeIcon = sbVisible ? icons.dataType : 0;
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index ca1f55e..11ddbd0 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -241,7 +241,7 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             boolean newWorkProfile = Intent.ACTION_MANAGED_PROFILE_ADDED.equals(intent.getAction());
-            boolean userStarted = Intent.ACTION_USER_STARTED.equals(intent.getAction());
+            boolean userStarted = Intent.ACTION_USER_SWITCHED.equals(intent.getAction());
             boolean isManagedProfile = mUserManager.isManagedProfile(
                     intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
             if (userStarted || newWorkProfile) {
@@ -288,7 +288,7 @@
     public void start() {
         if (DEBUG) Log.d(TAG, "Start");
         final IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_USER_STARTED);
+        filter.addAction(Intent.ACTION_USER_SWITCHED);
         filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
         filter.addAction(Intent.ACTION_WALLPAPER_CHANGED);
         mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, mMainExecutor,
diff --git a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
index c5e35a4..8b394bf 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
@@ -16,13 +16,18 @@
 
 package com.android.systemui.toast;
 
+import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
+import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+
 import android.animation.Animator;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.Application;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -53,7 +58,7 @@
     final ToastPlugin.Toast mPluginToast;
 
     private final String mPackageName;
-    private final int mUserId;
+    @UserIdInt private final int mUserId;
     private final LayoutInflater mLayoutInflater;
 
     final int mDefaultX = 0;
@@ -74,7 +79,7 @@
     }
 
     SystemUIToast(LayoutInflater layoutInflater, Context context, CharSequence text,
-            ToastPlugin.Toast pluginToast, String packageName, int userId,
+            ToastPlugin.Toast pluginToast, String packageName, @UserIdInt int userId,
             int orientation) {
         mLayoutInflater = layoutInflater;
         mContext = context;
@@ -248,6 +253,15 @@
             return null;
         }
 
+        final Context userContext;
+        try {
+            userContext = context.createPackageContextAsUser("android",
+                0, new UserHandle(userId));
+        } catch (NameNotFoundException e) {
+            Log.e(TAG, "Could not create user package context");
+            return null;
+        }
+
         final ApplicationsState appState =
                 ApplicationsState.getInstance((Application) context.getApplicationContext());
         if (!appState.isUserAdded(userId)) {
@@ -255,9 +269,11 @@
                     + "packageName=" + packageName);
             return null;
         }
+
+        final PackageManager packageManager = userContext.getPackageManager();
         final AppEntry appEntry = appState.getEntry(packageName, userId);
         if (appEntry == null || appEntry.info == null
-                || !ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(appEntry)) {
+                || !showApplicationIcon(appEntry.info, packageManager)) {
             return null;
         }
 
@@ -265,7 +281,20 @@
         UserHandle user = UserHandle.getUserHandleForUid(appInfo.uid);
         IconFactory iconFactory = IconFactory.obtain(context);
         Bitmap iconBmp = iconFactory.createBadgedIconBitmap(
-                appInfo.loadUnbadgedIcon(context.getPackageManager()), user, true).icon;
+                appInfo.loadUnbadgedIcon(packageManager), user, true).icon;
         return new BitmapDrawable(context.getResources(), iconBmp);
     }
+
+    private static boolean showApplicationIcon(ApplicationInfo appInfo,
+            PackageManager packageManager) {
+        if (hasFlag(appInfo.flags, FLAG_UPDATED_SYSTEM_APP)) {
+            return packageManager.getLaunchIntentForPackage(appInfo.packageName)
+                != null;
+        }
+        return !hasFlag(appInfo.flags, FLAG_SYSTEM);
+    }
+
+    private static boolean hasFlag(int flags, int flag) {
+        return (flags & flag) != 0;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvBottomSheetActivity.java b/packages/SystemUI/src/com/android/systemui/tv/TvBottomSheetActivity.java
new file mode 100644
index 0000000..2b7a332
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvBottomSheetActivity.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.tv;
+
+import android.app.Activity;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.WindowManager;
+
+import com.android.systemui.R;
+
+import java.util.function.Consumer;
+
+/**
+ * Generic bottom sheet with up to two icons in the beginning and two buttons.
+ */
+public abstract class TvBottomSheetActivity extends Activity {
+
+    private static final String TAG = TvBottomSheetActivity.class.getSimpleName();
+    private Drawable mBackgroundWithBlur;
+    private Drawable mBackgroundWithoutBlur;
+
+    private final Consumer<Boolean> mBlurConsumer = this::onBlurChanged;
+
+    private void onBlurChanged(boolean enabled) {
+        Log.v(TAG, "blur enabled: " + enabled);
+        getWindow().setBackgroundDrawable(enabled ? mBackgroundWithBlur : mBackgroundWithoutBlur);
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.tv_bottom_sheet);
+
+        overridePendingTransition(R.anim.tv_bottom_sheet_enter, 0);
+
+        mBackgroundWithBlur = getResources()
+                .getDrawable(R.drawable.bottom_sheet_background_with_blur);
+        mBackgroundWithoutBlur = getResources().getDrawable(R.drawable.bottom_sheet_background);
+
+        DisplayMetrics metrics = getResources().getDisplayMetrics();
+        int screenWidth = metrics.widthPixels;
+        int screenHeight = metrics.heightPixels;
+        int marginPx = getResources().getDimensionPixelSize(R.dimen.bottom_sheet_margin);
+
+        WindowManager.LayoutParams windowParams = getWindow().getAttributes();
+        windowParams.width = screenWidth - marginPx * 2;
+        windowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
+        windowParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
+        windowParams.horizontalMargin = 0f;
+        windowParams.verticalMargin = (float) marginPx / screenHeight;
+        windowParams.format = PixelFormat.TRANSPARENT;
+        windowParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
+        windowParams.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
+        windowParams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
+        getWindow().setAttributes(windowParams);
+        getWindow().setElevation(getWindow().getElevation() + 5);
+        getWindow().setBackgroundBlurRadius(getResources().getDimensionPixelSize(
+                R.dimen.bottom_sheet_background_blur_radius));
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        getWindowManager().addCrossWindowBlurEnabledListener(mBlurConsumer);
+    }
+
+    @Override
+    public void onDetachedFromWindow() {
+        getWindowManager().removeCrossWindowBlurEnabledListener(mBlurConsumer);
+        super.onDetachedFromWindow();
+    }
+
+    @Override
+    public void finish() {
+        super.finish();
+        overridePendingTransition(0, R.anim.tv_bottom_sheet_exit);
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 3af82f9..bc956dc 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -45,6 +45,7 @@
 import com.android.systemui.dagger.WMComponent;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.shared.tracing.ProtoTraceable;
@@ -114,6 +115,7 @@
     private final NavigationModeController mNavigationModeController;
     private final ScreenLifecycle mScreenLifecycle;
     private final SysUiState mSysUiState;
+    private final WakefulnessLifecycle mWakefulnessLifecycle;
     private final ProtoTracer mProtoTracer;
     private final Executor mSysUiMainExecutor;
 
@@ -121,6 +123,7 @@
     private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback;
     private KeyguardUpdateMonitorCallback mPipKeyguardCallback;
     private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback;
+    private WakefulnessLifecycle.Observer mWakefulnessObserver;
 
     @Inject
     public WMShell(Context context,
@@ -136,6 +139,7 @@
             ScreenLifecycle screenLifecycle,
             SysUiState sysUiState,
             ProtoTracer protoTracer,
+            WakefulnessLifecycle wakefulnessLifecycle,
             @Main Executor sysUiMainExecutor) {
         super(context);
         mCommandQueue = commandQueue;
@@ -148,6 +152,7 @@
         mSplitScreenOptional = splitScreenOptional;
         mOneHandedOptional = oneHandedOptional;
         mHideDisplayCutoutOptional = hideDisplayCutoutOptional;
+        mWakefulnessLifecycle = wakefulnessLifecycle;
         mProtoTracer = protoTracer;
         mShellCommandHandler = shellCommandHandler;
         mSysUiMainExecutor = sysUiMainExecutor;
@@ -266,21 +271,8 @@
 
         mOneHandedKeyguardCallback = new KeyguardUpdateMonitorCallback() {
             @Override
-            public void onKeyguardBouncerChanged(boolean bouncer) {
-                if (bouncer) {
-                    oneHanded.stopOneHanded();
-                }
-            }
-
-            @Override
             public void onKeyguardVisibilityChanged(boolean showing) {
-                if (showing) {
-                    // When keyguard shown, temperory lock OHM disabled to avoid mis-trigger.
-                    oneHanded.setLockedDisabled(true /* locked */, false /* enabled */);
-                } else {
-                    // Reset locked.
-                    oneHanded.setLockedDisabled(false /* locked */, false /* enabled */);
-                }
+                oneHanded.onKeyguardVisibilityChanged(showing);
                 oneHanded.stopOneHanded();
             }
 
@@ -291,6 +283,24 @@
         };
         mKeyguardUpdateMonitor.registerCallback(mOneHandedKeyguardCallback);
 
+        mWakefulnessObserver =
+                new WakefulnessLifecycle.Observer() {
+                    @Override
+                    public void onFinishedWakingUp() {
+                        // Reset locked for the case keyguard not shown.
+                        oneHanded.setLockedDisabled(false /* locked */, false /* enabled */);
+                    }
+
+                    @Override
+                    public void onStartedGoingToSleep() {
+                        oneHanded.stopOneHanded();
+                        // When user press power button going to sleep, temperory lock OHM disabled
+                        // to avoid mis-trigger.
+                        oneHanded.setLockedDisabled(true /* locked */, false /* enabled */);
+                    }
+                };
+        mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
+
         mScreenLifecycle.addObserver(new ScreenLifecycle.Observer() {
             @Override
             public void onScreenTurningOff() {
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 41f9877..0c750a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -53,6 +53,7 @@
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.util.concurrency.FakeExecutor;
@@ -117,6 +118,8 @@
     @Mock
     private AccessibilityManager mAccessibilityManager;
     @Mock
+    private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+    @Mock
     private ScreenLifecycle mScreenLifecycle;
     @Mock
     private Vibrator mVibrator;
@@ -176,6 +179,7 @@
                 mFalsingManager,
                 mPowerManager,
                 mAccessibilityManager,
+                mLockscreenShadeTransitionController,
                 mScreenLifecycle,
                 mVibrator,
                 Optional.of(mHbmProvider));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index 5923de6..f62587c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -35,6 +35,7 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -65,6 +66,8 @@
     @Mock
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     @Mock
+    private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+    @Mock
     private DumpManager mDumpManager;
     @Mock
     private DelayableExecutor mExecutor;
@@ -106,6 +109,7 @@
                 mExecutor,
                 mDumpManager,
                 mKeyguardViewMediator,
+                mLockscreenShadeTransitionController,
                 mUdfpsController);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index a11b9cf..10997fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -209,33 +209,14 @@
         // WHEN quick pick up is triggered
         mTriggers.onSensor(DozeLog.REASON_SENSOR_QUICK_PICKUP, 100, 100, null);
 
-        // THEN device goes into aod (shows clock with black background)
-        verify(mMachine).requestState(DOZE_AOD);
+        // THEN request pulse
+        verify(mMachine).requestPulse(anyInt());
 
         // THEN a log is taken that quick pick up was triggered
         verify(mUiEventLogger).log(DozingUpdateUiEvent.DOZING_UPDATE_QUICK_PICKUP);
     }
 
     @Test
-    public void testQuickPickupTimeOutAfterExecutables() {
-        // GIVEN quick pickup is triggered when device is in DOZE
-        when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
-        mTriggers.onSensor(DozeLog.REASON_SENSOR_QUICK_PICKUP, 100, 100, null);
-        verify(mMachine).requestState(DOZE_AOD);
-        verify(mMachine, never()).requestState(DozeMachine.State.DOZE);
-
-        // WHEN next executable is run
-        mExecutor.advanceClockToLast();
-        mExecutor.runAllReady();
-
-        // THEN device goes back into DOZE
-        verify(mMachine).requestState(DozeMachine.State.DOZE);
-
-        // THEN a log is taken that wake up timeout expired
-        verify(mUiEventLogger).log(DozingUpdateUiEvent.DOZING_UPDATE_WAKE_TIMEOUT);
-    }
-
-    @Test
     public void testOnSensor_Fingerprint() {
         // GIVEN dozing state
         when(mMachine.getState()).thenReturn(DOZE_AOD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
index e20b426..66b6470 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
@@ -82,9 +82,11 @@
     @Test
     public void eventNotEmittedWithoutDevice() {
         // WHEN data source emits an event without device data
-        mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */);
+        mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */,
+                false /* isSsReactivated */);
         // THEN an event isn't emitted
-        verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any(), anyBoolean());
+        verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any(), anyBoolean(),
+                anyBoolean());
     }
 
     @Test
@@ -92,7 +94,8 @@
         // WHEN device source emits an event without media data
         mManager.onMediaDeviceChanged(KEY, null, mDeviceData);
         // THEN an event isn't emitted
-        verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any(), anyBoolean());
+        verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any(), anyBoolean(),
+                anyBoolean());
     }
 
     @Test
@@ -100,80 +103,95 @@
         // GIVEN that a device event has already been received
         mManager.onMediaDeviceChanged(KEY, null, mDeviceData);
         // WHEN media event is received
-        mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */);
+        mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */,
+                false /* isSsReactivated */);
         // THEN the listener receives a combined event
         ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
-        verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture(), anyBoolean());
+        verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture(), anyBoolean(),
+                anyBoolean());
         assertThat(captor.getValue().getDevice()).isNotNull();
     }
 
     @Test
     public void emitEventAfterMediaFirst() {
         // GIVEN that media event has already been received
-        mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */);
+        mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */,
+                false /* isSsReactivated */);
         // WHEN device event is received
         mManager.onMediaDeviceChanged(KEY, null, mDeviceData);
         // THEN the listener receives a combined event
         ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
-        verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture(), anyBoolean());
+        verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture(), anyBoolean(),
+                anyBoolean());
         assertThat(captor.getValue().getDevice()).isNotNull();
     }
 
     @Test
     public void migrateKeyMediaFirst() {
         // GIVEN that media and device info has already been received
-        mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */);
+        mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */,
+                false /* isSsReactivated */);
         mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
         reset(mListener);
         // WHEN a key migration event is received
-        mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */);
+        mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */,
+                false /* isSsReactivated */);
         // THEN the listener receives a combined event
         ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
-        verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture(), anyBoolean());
+        verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture(), anyBoolean(),
+                anyBoolean());
         assertThat(captor.getValue().getDevice()).isNotNull();
     }
 
     @Test
     public void migrateKeyDeviceFirst() {
         // GIVEN that media and device info has already been received
-        mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */);
+        mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */,
+                false /* isSsReactivated */);
         mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
         reset(mListener);
         // WHEN a key migration event is received
         mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData);
         // THEN the listener receives a combined event
         ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
-        verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture(), anyBoolean());
+        verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture(), anyBoolean(),
+                anyBoolean());
         assertThat(captor.getValue().getDevice()).isNotNull();
     }
 
     @Test
     public void migrateKeyMediaAfter() {
         // GIVEN that media and device info has already been received
-        mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */);
+        mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */,
+                false /* isSsReactivated */);
         mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
         mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData);
         reset(mListener);
         // WHEN a second key migration event is received for media
-        mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */);
+        mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */,
+                false /* isSsReactivated */);
         // THEN the key has already been migrated
         ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
-        verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture(), anyBoolean());
+        verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture(), anyBoolean(),
+                anyBoolean());
         assertThat(captor.getValue().getDevice()).isNotNull();
     }
 
     @Test
     public void migrateKeyDeviceAfter() {
         // GIVEN that media and device info has already been received
-        mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */);
+        mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */,
+                false /* isSsReactivated */);
         mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
-        mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */);
+        mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */,
+                false /* isSsReactivated */);
         reset(mListener);
         // WHEN a second key migration event is received for the device
         mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData);
         // THEN the key has already be migrated
         ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
-        verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture(), anyBoolean());
+        verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture(), anyBoolean(),
+                anyBoolean());
         assertThat(captor.getValue().getDevice()).isNotNull();
     }
 
@@ -187,7 +205,8 @@
 
     @Test
     public void mediaDataRemovedAfterMediaEvent() {
-        mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */);
+        mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */,
+                false /* isSsReactivated */);
         mManager.onMediaDataRemoved(KEY);
         verify(mListener).onMediaDataRemoved(eq(KEY));
     }
@@ -202,13 +221,15 @@
     @Test
     public void mediaDataKeyUpdated() {
         // GIVEN that device and media events have already been received
-        mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */);
+        mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */,
+                false /* isSsReactivated */);
         mManager.onMediaDeviceChanged(KEY, null, mDeviceData);
         // WHEN the key is changed
-        mManager.onMediaDataLoaded("NEW_KEY", KEY, mMediaData, true /* immediately */);
+        mManager.onMediaDataLoaded("NEW_KEY", KEY, mMediaData, true /* immediately */,
+                false /* isSsReactivated */);
         // THEN the listener gets a load event with the correct keys
         ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
         verify(mListener).onMediaDataLoaded(
-                eq("NEW_KEY"), any(), captor.capture(), anyBoolean());
+                eq("NEW_KEY"), any(), captor.capture(), anyBoolean(), anyBoolean());
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
index 17f2a07..d879186 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
@@ -120,7 +120,8 @@
         mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
 
         // THEN we should tell the listener
-        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataMain), eq(true))
+        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataMain), eq(true),
+                eq(false))
     }
 
     @Test
@@ -129,7 +130,7 @@
         mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest)
 
         // THEN we should NOT tell the listener
-        verify(listener, never()).onMediaDataLoaded(any(), any(), any(), anyBoolean())
+        verify(listener, never()).onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyBoolean())
     }
 
     @Test
@@ -175,10 +176,12 @@
         setUser(USER_GUEST)
 
         // THEN we should add back the guest user media
-        verify(listener).onMediaDataLoaded(eq(KEY_ALT), eq(null), eq(dataGuest), eq(true))
+        verify(listener).onMediaDataLoaded(eq(KEY_ALT), eq(null), eq(dataGuest), eq(true),
+                eq(false))
 
         // but not the main user's
-        verify(listener, never()).onMediaDataLoaded(eq(KEY), any(), eq(dataMain), anyBoolean())
+        verify(listener, never()).onMediaDataLoaded(eq(KEY), any(), eq(dataMain), anyBoolean(),
+                anyBoolean())
     }
 
     @Test
@@ -245,7 +248,7 @@
 
         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
 
-        verify(listener, never()).onMediaDataLoaded(any(), any(), any(), anyBoolean())
+        verify(listener, never()).onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyBoolean())
         verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
         assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
     }
@@ -282,12 +285,15 @@
         // WHEN we have media that was recently played, but not currently active
         val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
         mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
-        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true))
+        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true),
+                eq(false))
 
         // AND we get a smartspace signal
         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
 
-        // THEN we should tell listeners to treat the media as active instead
+        // THEN we should tell listeners to treat the media as not active instead
+        verify(listener, never()).onMediaDataLoaded(eq(KEY), eq(KEY), any(), anyBoolean(),
+                anyBoolean())
         verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
         assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
     }
@@ -299,14 +305,16 @@
         // WHEN we have media that was recently played, but not currently active
         val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
         mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
-        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true))
+        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true),
+                eq(false))
 
         // AND we get a smartspace signal
         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
 
         // THEN we should tell listeners to treat the media as active instead
         val dataCurrentAndActive = dataCurrent.copy(active = true)
-        verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true))
+        verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true),
+                eq(true))
         assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
         // Smartspace update shouldn't be propagated for the empty rec list.
         verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
@@ -317,14 +325,16 @@
         // WHEN we have media that was recently played, but not currently active
         val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
         mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
-        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true))
+        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true),
+                eq(false))
 
         // AND we get a smartspace signal
         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
 
         // THEN we should tell listeners to treat the media as active instead
         val dataCurrentAndActive = dataCurrent.copy(active = true)
-        verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true))
+        verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true),
+                eq(true))
         assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
         // Smartspace update should also be propagated but not prioritized.
         verify(listener)
@@ -344,11 +354,17 @@
     fun testOnSmartspaceMediaDataRemoved_usedMediaAndSmartspace_clearsBoth() {
         val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
         mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true),
+                eq(false))
+
         mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
 
+        val dataCurrentAndActive = dataCurrent.copy(active = true)
+        verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true),
+                eq(true))
+
         mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
 
-        verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrent), eq(true))
         verify(listener).onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
         assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index 3128db4..5b4e124 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -185,7 +185,8 @@
     fun testOnMetaDataLoaded_callsListener() {
         mediaDataManager.onNotificationAdded(KEY, mediaNotification)
         mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = mock(MediaData::class.java))
-        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), anyObject(), eq(true))
+        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), anyObject(), eq(true),
+                eq(false))
     }
 
     @Test
@@ -196,7 +197,8 @@
         mediaDataManager.onNotificationAdded(KEY, mediaNotification)
         assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
         assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
-        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true))
+        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
+                eq(false))
         assertThat(mediaDataCaptor.value!!.active).isTrue()
     }
 
@@ -215,7 +217,8 @@
         mediaDataManager.onNotificationAdded(KEY, mediaNotification)
         assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
         assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
-        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true))
+        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
+                eq(false))
         val data = mediaDataCaptor.value
         assertThat(data.resumption).isFalse()
         mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
@@ -223,7 +226,8 @@
         mediaDataManager.onNotificationRemoved(KEY)
         // THEN the media data indicates that it is for resumption
         verify(listener)
-            .onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true))
+            .onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true),
+                    eq(false))
         assertThat(mediaDataCaptor.value.resumption).isTrue()
     }
 
@@ -236,7 +240,8 @@
         assertThat(backgroundExecutor.runAllReady()).isEqualTo(2)
         assertThat(foregroundExecutor.runAllReady()).isEqualTo(2)
         verify(listener)
-            .onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true))
+            .onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
+                    eq(false))
         val data = mediaDataCaptor.value
         assertThat(data.resumption).isFalse()
         val resumableData = data.copy(resumeAction = Runnable {})
@@ -247,7 +252,8 @@
         mediaDataManager.onNotificationRemoved(KEY)
         // THEN the data is for resumption and the key is migrated to the package name
         verify(listener)
-            .onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true))
+            .onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true),
+                    eq(false))
         assertThat(mediaDataCaptor.value.resumption).isTrue()
         verify(listener, never()).onMediaDataRemoved(eq(KEY))
         // WHEN the second is removed
@@ -255,7 +261,8 @@
         // THEN the data is for resumption and the second key is removed
         verify(listener)
             .onMediaDataLoaded(
-                eq(PACKAGE_NAME), eq(PACKAGE_NAME), capture(mediaDataCaptor), eq(true))
+                eq(PACKAGE_NAME), eq(PACKAGE_NAME), capture(mediaDataCaptor), eq(true),
+                    eq(false))
         assertThat(mediaDataCaptor.value.resumption).isTrue()
         verify(listener).onMediaDataRemoved(eq(KEY_2))
     }
@@ -269,7 +276,8 @@
         mediaDataManager.onNotificationAdded(KEY, mediaNotification)
         assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
         assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
-        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true))
+        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
+                eq(false))
         val data = mediaDataCaptor.value
         val dataRemoteWithResume = data.copy(resumeAction = Runnable {}, isLocalSession = false)
         mediaDataManager.onMediaDataLoaded(KEY, null, dataRemoteWithResume)
@@ -295,7 +303,8 @@
         assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
         // THEN the media data indicates that it is for resumption
         verify(listener)
-            .onMediaDataLoaded(eq(PACKAGE_NAME), eq(null), capture(mediaDataCaptor), eq(true))
+            .onMediaDataLoaded(eq(PACKAGE_NAME), eq(null), capture(mediaDataCaptor), eq(true),
+                    eq(false))
         val data = mediaDataCaptor.value
         assertThat(data.resumption).isTrue()
         assertThat(data.song).isEqualTo(SESSION_TITLE)
@@ -335,7 +344,8 @@
         assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
         assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
         verify(listener)
-            .onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true))
+            .onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
+                    eq(false))
     }
 
     @Test
@@ -414,7 +424,8 @@
         mediaDataManager.onNotificationAdded(KEY, mediaNotification)
         assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
         assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
-        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true))
+        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
+                eq(false))
         assertThat(mediaDataCaptor.value!!.lastActive).isAtLeast(currentTime)
     }
 
@@ -431,7 +442,8 @@
         mediaDataManager.setTimedOut(KEY, true, true)
 
         // THEN the last active time is not changed
-        verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), capture(mediaDataCaptor), eq(true))
+        verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), capture(mediaDataCaptor), eq(true),
+                eq(false))
         assertThat(mediaDataCaptor.value.lastActive).isLessThan(currentTime)
     }
 
@@ -442,7 +454,8 @@
         mediaDataManager.onNotificationAdded(KEY, mediaNotification)
         assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
         assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
-        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true))
+        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
+                eq(false))
         val data = mediaDataCaptor.value
         assertThat(data.resumption).isFalse()
         mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
@@ -454,7 +467,8 @@
 
         // THEN the last active time is not changed
         verify(listener)
-            .onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true))
+            .onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true),
+                    eq(false))
         assertThat(mediaDataCaptor.value.resumption).isTrue()
         assertThat(mediaDataCaptor.value.lastActive).isLessThan(currentTime)
     }
@@ -480,7 +494,8 @@
         assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
 
         // THEN only the first MAX_COMPACT_ACTIONS are actually set
-        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true))
+        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
+                eq(false))
         assertThat(mediaDataCaptor.value.actionsToShowInCompact.size).isEqualTo(
                 MediaDataManager.MAX_COMPACT_ACTIONS)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt
index c6d7e92..b9caab2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt
@@ -185,7 +185,8 @@
         filter.onMediaDataLoaded(KEY, null, mediaData1)
         bgExecutor.runAllReady()
         fgExecutor.runAllReady()
-        verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true))
+        verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true),
+                eq(false))
     }
 
     @Test
@@ -207,7 +208,8 @@
         bgExecutor.runAllReady()
         fgExecutor.runAllReady()
         // THEN the event is not filtered
-        verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true))
+        verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true),
+                eq(false))
     }
 
     @Test
@@ -236,7 +238,8 @@
         bgExecutor.runAllReady()
         fgExecutor.runAllReady()
         // THEN the event is not filtered
-        verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true))
+        verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true),
+                eq(false))
     }
 
     @Test
@@ -251,14 +254,15 @@
         bgExecutor.runAllReady()
         fgExecutor.runAllReady()
         // THEN the event is not filtered
-        verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true))
+        verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true),
+                eq(false))
         // WHEN a loaded event is received that matches the local session
         filter.onMediaDataLoaded(KEY, null, mediaData2)
         bgExecutor.runAllReady()
         fgExecutor.runAllReady()
         // THEN the event is filtered
         verify(mediaListener, never()).onMediaDataLoaded(
-            eq(KEY), eq(null), eq(mediaData2), anyBoolean())
+            eq(KEY), eq(null), eq(mediaData2), anyBoolean(), anyBoolean())
     }
 
     @Test
@@ -274,7 +278,8 @@
         fgExecutor.runAllReady()
         // THEN the event is not filtered because there isn't a notification for the remote
         // session.
-        verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true))
+        verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true),
+                eq(false))
     }
 
     @Test
@@ -291,14 +296,15 @@
         bgExecutor.runAllReady()
         fgExecutor.runAllReady()
         // THEN the event is not filtered
-        verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1), eq(true))
+        verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1), eq(true),
+                eq(false))
         // WHEN a loaded event is received that matches the local session
         filter.onMediaDataLoaded(key2, null, mediaData2)
         bgExecutor.runAllReady()
         fgExecutor.runAllReady()
         // THEN the event is filtered
         verify(mediaListener, never())
-            .onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), anyBoolean())
+            .onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), anyBoolean(), anyBoolean())
         // AND there should be a removed event for key2
         verify(mediaListener).onMediaDataRemoved(eq(key2))
     }
@@ -317,13 +323,15 @@
         bgExecutor.runAllReady()
         fgExecutor.runAllReady()
         // THEN the event is not filtered
-        verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1), eq(true))
+        verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1), eq(true),
+                eq(false))
         // WHEN a loaded event is received that matches the remote session
         filter.onMediaDataLoaded(key2, null, mediaData2)
         bgExecutor.runAllReady()
         fgExecutor.runAllReady()
         // THEN the event is not filtered
-        verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), eq(true))
+        verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), eq(true),
+                eq(false))
     }
 
     @Test
@@ -339,13 +347,15 @@
         bgExecutor.runAllReady()
         fgExecutor.runAllReady()
         // THEN the event is not filtered
-        verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true))
+        verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true),
+                eq(false))
         // WHEN a loaded event is received that matches the local session
         filter.onMediaDataLoaded(KEY, null, mediaData2)
         bgExecutor.runAllReady()
         fgExecutor.runAllReady()
         // THEN the event is not filtered
-        verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData2), eq(true))
+        verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData2), eq(true),
+                eq(false))
     }
 
     @Test
@@ -363,7 +373,8 @@
         bgExecutor.runAllReady()
         fgExecutor.runAllReady()
         // THEN the event is not filtered
-        verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true))
+        verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true),
+                eq(false))
     }
 
     @Test
@@ -385,7 +396,8 @@
         bgExecutor.runAllReady()
         fgExecutor.runAllReady()
         // THEN the key migration event is fired
-        verify(mediaListener).onMediaDataLoaded(eq(key2), eq(key1), eq(mediaData2), eq(true))
+        verify(mediaListener).onMediaDataLoaded(eq(key2), eq(key1), eq(mediaData2), eq(true),
+                eq(false))
     }
 
     @Test
@@ -415,12 +427,13 @@
         fgExecutor.runAllReady()
         // THEN the key migration event is filtered
         verify(mediaListener, never())
-            .onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), anyBoolean())
+            .onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), anyBoolean(), anyBoolean())
         // WHEN a loaded event is received that matches the remote session
         filter.onMediaDataLoaded(key2, null, mediaData1)
         bgExecutor.runAllReady()
         fgExecutor.runAllReady()
         // THEN the key migration event is fired
-        verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData1), eq(true))
+        verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData1), eq(true),
+                eq(false))
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 6e21642..2c68661 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -135,8 +135,6 @@
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
 
         assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
@@ -154,8 +152,6 @@
         mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
 
         assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
@@ -176,9 +172,25 @@
         assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
+    }
+
+    @Test
+    public void onBindViewHolder_bindConnectedDevice_withSelectableDevice_showAddIcon() {
+        when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(mMediaDevices);
+        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
         assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
+    }
+
+    @Test
+    public void onBindViewHolder_bindConnectedDevice_withoutSelectableDevice_hideAddIcon() {
+        when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(new ArrayList<>());
+        mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+        assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
     }
 
     @Test
@@ -245,8 +257,6 @@
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
     }
@@ -263,8 +273,6 @@
         assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
         assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE);
         assertThat(mViewHolder.mTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index 589ae2e..9bd07b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -155,6 +155,7 @@
 
     @Test
     public void refresh_notInDragging_verifyUpdateAdapter() {
+        when(mMediaOutputBaseAdapter.getCurrentActivePosition()).thenReturn(-1);
         when(mMediaOutputBaseAdapter.isDragging()).thenReturn(false);
         mMediaOutputBaseDialogImpl.refresh();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index da63b8a..4980f74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -52,6 +52,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.phone.ShadeController;
@@ -108,7 +109,8 @@
                         Dependency.get(Dependency.MAIN_HANDLER),
                         mock(UiEventLogger.class),
                         mock(NavigationBarOverlayController.class),
-                        mock(ConfigurationController.class)));
+                        mock(ConfigurationController.class),
+                        mock(UserTracker.class)));
         initializeNavigationBars();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 4ec45b4..b1afeec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -76,6 +76,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.phone.ShadeController;
@@ -276,7 +277,8 @@
                 mock(SystemActions.class),
                 mHandler,
                 mock(NavigationBarOverlayController.class),
-                mUiEventLogger));
+                mUiEventLogger,
+                mock(UserTracker.class)));
     }
 
     private void processAllMessages() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
index 007a3b9..33c7a57 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
@@ -17,6 +17,7 @@
 package com.android.systemui.people;
 
 import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME;
+import static com.android.systemui.people.PeopleSpaceUtils.getContactLookupKeysWithBirthdaysToday;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -35,6 +36,7 @@
 import android.app.people.IPeopleManager;
 import android.app.people.PeopleSpaceTile;
 import android.appwidget.AppWidgetManager;
+import android.content.ContentProviderOperation;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -64,12 +66,17 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 
+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;
 
+import java.text.SimpleDateFormat;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -112,6 +119,7 @@
                     .setNotificationDataUri(URI)
                     .setMessagesCount(1)
                     .build();
+    private static final String TEST_DISPLAY_NAME = "Display Name";
 
     private final ShortcutInfo mShortcutInfo = new ShortcutInfo.Builder(mContext,
             SHORTCUT_ID_1).setLongLabel(
@@ -227,6 +235,12 @@
                 .thenReturn(List.of(mNotificationEntry1, mNotificationEntry2, mNotificationEntry3));
     }
 
+    @After
+    public void tearDown() {
+        cleanupTestContactFromContactProvider();
+    }
+
+
     @Test
     public void testAugmentTileFromNotification() {
         PeopleSpaceTile tile =
@@ -467,4 +481,82 @@
                 eq(WIDGET_ID_WITH_SHORTCUT),
                 any());
     }
+
+    @Test
+    public void testBirthdayQueriesWithYear() throws Exception {
+        String birthdayToday = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
+        addBirthdayToContactsDatabase(birthdayToday);
+
+        List<String> lookupKeys = getContactLookupKeysWithBirthdaysToday(mContext);
+
+        assertThat(lookupKeys).hasSize(1);
+    }
+
+    @Test
+    public void testBirthdayQueriesWithoutYear() throws Exception {
+        String birthdayToday = new SimpleDateFormat("--MM-dd").format(new Date());
+        addBirthdayToContactsDatabase(birthdayToday);
+
+        List<String> lookupKeys = getContactLookupKeysWithBirthdaysToday(mContext);
+
+        assertThat(lookupKeys).hasSize(1);
+    }
+
+    @Test
+    public void testBirthdayQueriesWithDifferentDates() throws Exception {
+        Date yesterday = new Date(System.currentTimeMillis() - Duration.ofDays(1).toMillis());
+        String birthdayYesterday = new SimpleDateFormat("--MM-dd").format(yesterday);
+        addBirthdayToContactsDatabase(birthdayYesterday);
+
+        List<String> lookupKeys = getContactLookupKeysWithBirthdaysToday(mContext);
+
+        assertThat(lookupKeys).isEmpty();
+    }
+
+    private void addBirthdayToContactsDatabase(String birthdayDate) throws Exception {
+        ContentResolver resolver = mContext.getContentResolver();
+        ArrayList<ContentProviderOperation> ops = new ArrayList<>(3);
+        ops.add(ContentProviderOperation
+                .newInsert(ContactsContract.RawContacts.CONTENT_URI)
+                .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, "com.google")
+                .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, "fakeAccountName")
+                .build());
+        ops.add(ContentProviderOperation
+                .newInsert(ContactsContract.Data.CONTENT_URI)
+                .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
+                .withValue(ContactsContract.Data.MIMETYPE,
+                        ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
+                .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME,
+                        TEST_DISPLAY_NAME)
+                .build());
+        ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+                .withValueBackReference(
+                        ContactsContract.Data.RAW_CONTACT_ID, 0)
+                .withValue(
+                        ContactsContract.Data.MIMETYPE,
+                        ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE)
+                .withValue(
+                        ContactsContract.CommonDataKinds.Event.TYPE,
+                        ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY)
+                .withValue(
+                        ContactsContract.CommonDataKinds.Event.START_DATE, birthdayDate)
+                .build());
+        resolver.applyBatch(ContactsContract.AUTHORITY, ops);
+    }
+
+    private void cleanupTestContactFromContactProvider() {
+        Cursor cursor = mContext.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,
+                null,
+                ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + "=?",
+                new String[]{TEST_DISPLAY_NAME},
+                null);
+        while (cursor.moveToNext()) {
+            String contactId = cursor.getString(cursor.getColumnIndex(
+                    ContactsContract.Contacts.NAME_RAW_CONTACT_ID));
+            mContext.getContentResolver().delete(ContactsContract.Data.CONTENT_URI,
+                    ContactsContract.Data.RAW_CONTACT_ID + "=?",
+                    new String[]{contactId});
+        }
+        cursor.close();
+    }
 }
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 7af3743..c48f26b 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
@@ -43,6 +43,7 @@
 import static android.app.people.PeopleSpaceTile.SHOW_STARRED_CONTACTS;
 import static android.content.Intent.ACTION_BOOT_COMPLETED;
 import static android.content.Intent.ACTION_PACKAGES_SUSPENDED;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
 import static android.content.PermissionChecker.PERMISSION_GRANTED;
 import static android.content.PermissionChecker.PERMISSION_HARD_DENIED;
 import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_ANYONE;
@@ -88,7 +89,6 @@
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.service.notification.ConversationChannelWrapper;
@@ -1104,7 +1104,7 @@
     }
 
     @Test
-    public void testGetPeopleTileFromPersistentStorageNoConversation() throws RemoteException {
+    public void testGetPeopleTileFromPersistentStorageNoConversation() throws Exception {
         when(mIPeopleManager.getConversation(TEST_PACKAGE_A, 0, SHORTCUT_ID)).thenReturn(null);
         PeopleTileKey key = new PeopleTileKey(SHORTCUT_ID, 0, TEST_PACKAGE_A);
         PeopleSpaceTile tile = mManager.getTileFromPersistentStorage(key, WIDGET_ID_WITH_SHORTCUT);
@@ -1223,8 +1223,38 @@
     }
 
     @Test
-    public void testUpdateWidgetsOnStateChange() {
-        mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED);
+    public void testUpdateWidgetsFromBroadcastInBackgroundBootCompleteWithPackageUninstalled()
+            throws Exception {
+        when(mPackageManager.getApplicationInfoAsUser(any(), anyInt(), anyInt())).thenThrow(
+                PackageManager.NameNotFoundException.class);
+
+        // We should remove widgets if the package is uninstalled at next reboot if we missed the
+        // package removed broadcast.
+        mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED);
+
+        PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
+        assertThat(tile).isNull();
+        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+                any());
+    }
+
+    @Test
+    public void testUpdateWidgetsFromBroadcastInBackgroundPackageRemovedWithPackageUninstalled()
+            throws Exception {
+        when(mPackageManager.getApplicationInfoAsUser(any(), anyInt(), anyInt())).thenThrow(
+                PackageManager.NameNotFoundException.class);
+
+        mManager.updateWidgetsFromBroadcastInBackground(ACTION_PACKAGE_REMOVED);
+
+        PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
+        assertThat(tile).isNull();
+        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+                any());
+    }
+
+    @Test
+    public void testUpdateWidgetsFromBroadcastInBackground() {
+        mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED);
 
         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
         assertThat(tile.isPackageSuspended()).isFalse();
@@ -1236,10 +1266,10 @@
     }
 
     @Test
-    public void testUpdateWidgetsOnStateChangeWithUserQuieted() {
+    public void testUpdateWidgetsFromBroadcastInBackgroundWithUserQuieted() {
         when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
 
-        mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED);
+        mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED);
 
         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
         assertThat(tile.isPackageSuspended()).isFalse();
@@ -1248,10 +1278,10 @@
     }
 
     @Test
-    public void testUpdateWidgetsOnStateChangeWithPackageSuspended() throws Exception {
+    public void testUpdateWidgetsFromBroadcastInBackgroundWithPackageSuspended() throws Exception {
         when(mPackageManager.isPackageSuspended(any())).thenReturn(true);
 
-        mManager.updateWidgetsOnStateChange(ACTION_PACKAGES_SUSPENDED);
+        mManager.updateWidgetsFromBroadcastInBackground(ACTION_PACKAGES_SUSPENDED);
 
         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
         assertThat(tile.isPackageSuspended()).isTrue();
@@ -1260,9 +1290,9 @@
     }
 
     @Test
-    public void testUpdateWidgetsOnStateChangeNotInDnd() {
+    public void testUpdateWidgetsFromBroadcastInBackgroundNotInDnd() {
         int expected = 0;
-        mManager.updateWidgetsOnStateChange(NotificationManager
+        mManager.updateWidgetsFromBroadcastInBackground(NotificationManager
                 .ACTION_INTERRUPTION_FILTER_CHANGED);
 
         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
@@ -1270,14 +1300,14 @@
     }
 
     @Test
-    public void testUpdateWidgetsOnStateChangeAllConversations() {
+    public void testUpdateWidgetsFromBroadcastInBackgroundAllConversations() {
         int expected = 0;
         when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn(
                 INTERRUPTION_FILTER_PRIORITY);
         when(mNotificationPolicy.allowConversations()).thenReturn(true);
         setFinalField("priorityConversationSenders", CONVERSATION_SENDERS_ANYONE);
 
-        mManager.updateWidgetsOnStateChange(NotificationManager
+        mManager.updateWidgetsFromBroadcastInBackground(NotificationManager
                 .ACTION_INTERRUPTION_FILTER_CHANGED);
 
         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
@@ -1285,7 +1315,7 @@
     }
 
     @Test
-    public void testUpdateWidgetsOnStateChangeAllowOnlyImportantConversations() {
+    public void testUpdateWidgetsFromBroadcastInBackgroundAllowOnlyImportantConversations() {
         int expected = 0;
         // Only allow important conversations.
         when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn(
@@ -1293,7 +1323,7 @@
         when(mNotificationPolicy.allowConversations()).thenReturn(true);
         setFinalField("priorityConversationSenders", CONVERSATION_SENDERS_IMPORTANT);
 
-        mManager.updateWidgetsOnStateChange(NotificationManager
+        mManager.updateWidgetsFromBroadcastInBackground(NotificationManager
                 .ACTION_INTERRUPTION_FILTER_CHANGED);
 
         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
@@ -1302,13 +1332,13 @@
     }
 
     @Test
-    public void testUpdateWidgetsOnStateChangeAllowNoConversations() {
+    public void testUpdateWidgetsFromBroadcastInBackgroundAllowNoConversations() {
         int expected = 0;
         when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn(
                 INTERRUPTION_FILTER_PRIORITY);
         when(mNotificationPolicy.allowConversations()).thenReturn(false);
 
-        mManager.updateWidgetsOnStateChange(NotificationManager
+        mManager.updateWidgetsFromBroadcastInBackground(NotificationManager
                 .ACTION_INTERRUPTION_FILTER_CHANGED);
 
         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
@@ -1316,7 +1346,7 @@
     }
 
     @Test
-    public void testUpdateWidgetsOnStateChangeAllowNoConversationsAllowContactMessages() {
+    public void testUpdateWidgetsFromBroadcastInBackgroundAllowNoConversationsAllowContactMessages() {
         int expected = 0;
         when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn(
                 INTERRUPTION_FILTER_PRIORITY);
@@ -1324,14 +1354,14 @@
         when(mNotificationPolicy.allowMessagesFrom()).thenReturn(ZenModeConfig.SOURCE_CONTACT);
         when(mNotificationPolicy.allowMessages()).thenReturn(true);
 
-        mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED);
+        mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED);
 
         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
         assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | SHOW_CONTACTS);
     }
 
     @Test
-    public void testUpdateWidgetsOnStateChangeAllowNoConversationsAllowStarredContactMessages() {
+    public void testUpdateWidgetsFromBroadcastInBackgroundAllowNoConversationsAllowStarredContactMessages() {
         int expected = 0;
         when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn(
                 INTERRUPTION_FILTER_PRIORITY);
@@ -1339,26 +1369,26 @@
         when(mNotificationPolicy.allowMessagesFrom()).thenReturn(ZenModeConfig.SOURCE_STAR);
         when(mNotificationPolicy.allowMessages()).thenReturn(true);
 
-        mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED);
+        mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED);
 
         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
         assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | SHOW_STARRED_CONTACTS);
 
         setFinalField("suppressedVisualEffects", SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
                 | SUPPRESSED_EFFECT_AMBIENT);
-        mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED);
+        mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED);
 
         tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
         assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | SHOW_CONVERSATIONS);
     }
 
     @Test
-    public void testUpdateWidgetsOnStateChangeAllowAlarmsOnly() {
+    public void testUpdateWidgetsFromBroadcastInBackgroundAllowAlarmsOnly() {
         int expected = 0;
         when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn(
                 INTERRUPTION_FILTER_ALARMS);
 
-        mManager.updateWidgetsOnStateChange(NotificationManager
+        mManager.updateWidgetsFromBroadcastInBackground(NotificationManager
                 .ACTION_INTERRUPTION_FILTER_CHANGED);
 
         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
@@ -1366,7 +1396,7 @@
     }
 
     @Test
-    public void testUpdateWidgetsOnStateChangeAllowVisualEffectsAndAllowAlarmsOnly() {
+    public void testUpdateWidgetsFromBroadcastInBackgroundAllowVisualEffectsAndAllowAlarmsOnly() {
         int expected = 0;
         // If we show visuals, but just only make sounds for alarms, still show content in tiles.
         when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn(
@@ -1374,7 +1404,7 @@
         setFinalField("suppressedVisualEffects", SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
                 | SUPPRESSED_EFFECT_AMBIENT);
 
-        mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED);
+        mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED);
 
         PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
         assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | SHOW_CONVERSATIONS);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/TilesStatesTextTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/TilesStatesTextTest.kt
index 19ffa49..b8d018f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/TilesStatesTextTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/TilesStatesTextTest.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.qs.tileimpl
 
 import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
+import androidx.test.filters.MediumTest
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.google.common.truth.Truth.assertThat
@@ -27,7 +27,7 @@
 import org.junit.runner.RunWith
 
 @RunWith(AndroidTestingRunner::class)
-@SmallTest
+@MediumTest
 class TilesStatesTextTest : SysuiTestCase() {
 
     @Test
@@ -51,4 +51,29 @@
 
         assertThat(array.size).isEqualTo(3)
     }
+
+    @Test
+    fun testStockTilesSubtitlesMap() {
+        val tiles = mContext.getString(R.string.quick_settings_tiles_stock).split(",")
+        tiles.forEach { spec ->
+            val resName = "${QSTileViewImpl.TILE_STATE_RES_PREFIX}$spec"
+            val resId = mContext.resources.getIdentifier(resName, "array", mContext.packageName)
+
+            assertNotEquals("Missing resource for $resName", 0, resId)
+
+            assertThat(SubtitleArrayMapping.getSubtitleId(spec)).isEqualTo(resId)
+        }
+    }
+
+    @Test
+    fun testStockTilesSubtitlesReturnsDefault_unknown() {
+        assertThat(SubtitleArrayMapping.getSubtitleId("unknown"))
+            .isEqualTo(R.array.tile_states_default)
+    }
+
+    @Test
+    fun testStockTilesSubtitlesReturnsDefault_null() {
+        assertThat(SubtitleArrayMapping.getSubtitleId(null))
+            .isEqualTo(R.array.tile_states_default)
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSession.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSession.java
index 9c68f0d..478658e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSession.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSession.java
@@ -57,16 +57,19 @@
 
     private int mScrollDelta;
     private int mPageHeight;
+    private int mTargetHeight;
 
     FakeSession(int pageHeight, float maxPages, int tileHeight, int visiblePageTop,
-            int visiblePageBottom, int availableTop, int availableBottom) {
+            int visiblePageBottom, int availableTop, int availableBottom,
+            int maxTiles) {
         mPageHeight = pageHeight;
         mTileHeight = tileHeight;
         mAvailable = new Rect(0, availableTop, getPageWidth(), availableBottom);
         mAvailableTop = new Rect(mAvailable);
         mAvailableTop.inset(0, 0, 0, pageHeight);
         mVisiblePage = new Rect(0, visiblePageTop, getPageWidth(), visiblePageBottom);
-        mMaxTiles = (int) Math.ceil((pageHeight * maxPages) / mTileHeight);
+        mTargetHeight = (int) (pageHeight * maxPages);
+        mMaxTiles = maxTiles;
     }
 
     private static Image mockImage() {
@@ -158,6 +161,11 @@
     }
 
     @Override
+    public int getTargetHeight() {
+        return mTargetHeight;
+    }
+
+    @Override
     public int getTileHeight() {
         return mTileHeight;
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSessionTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSessionTest.java
index 2520af9..4c8a4b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSessionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSessionTest.java
@@ -42,21 +42,6 @@
 @RunWith(AndroidTestingRunner.class)
 public class FakeSessionTest extends SysuiTestCase {
     @Test
-    public void testMaxTiles() {
-        FakeSession session = new FakeSession(
-                /* pageHeight */ 100,
-                /* maxPages */ 2.25f,
-                /* tileHeight */ 10,
-                /* visiblePageTop */ 0,
-                /* visiblePageBottom */ 100,
-                /* availableTop */ -250,
-                /* availableBottom */ 250);
-
-        // (pageHeight * maxPages) / tileHeight
-        assertEquals("getMaxTiles()", 23, session.getMaxTiles());
-    }
-
-    @Test
     public void testNonEmptyResult_hasImage() {
         FakeSession session = new FakeSession(
                 /* pageHeight */ 100,
@@ -65,7 +50,8 @@
                 /* visiblePageTop */ 0,
                 /* visiblePageBottom */ 100,
                 /* availableTop */ 0,
-                /* availableBottom */ 100);
+                /* availableBottom */ 100,
+                /* max Tiles */ 30);
         ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(0));
         assertNotNull("result.image", result.image);
         assertNotNull("result.image.getHardwareBuffer()", result.image.getHardwareBuffer());
@@ -80,7 +66,8 @@
                 /* visiblePageTop */ 0,
                 /* visiblePageBottom */ 100,
                 /* availableTop */ 0,
-                /* availableBottom */ 100);
+                /* availableBottom */ 100,
+                /* max Tiles */ 30);
         ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(-100));
         assertNull("result.image", result.image);
     }
@@ -94,7 +81,8 @@
                 /* visiblePageTop */ 0,
                 /* visiblePageBottom */ 100,
                 /* availableTop */ -250,
-                /* availableBottom */ 250);
+                /* availableBottom */ 250,
+                /* max Tiles */ 30);
 
         ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(0));
         assertEquals("requested top", 0, result.requested.top);
@@ -113,7 +101,8 @@
                 /* visiblePageTop */ 0,
                 /* visiblePageBottom */ 100,
                 /* availableTop */ -250,
-                /* availableBottom */ 250);
+                /* availableBottom */ 250,
+                /* max Tiles */ 30);
 
         ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(90));
         assertEquals("requested top", 90, result.requested.top);
@@ -132,7 +121,8 @@
                 /* visiblePageTop */ 0,
                 /* visiblePageBottom */ 100,
                 /* availableTop */ -250,
-                /* availableBottom */ 250);
+                /* availableBottom */ 250,
+                /* max Tiles */ 30);
 
         ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(-100));
         assertEquals("requested top", -100, result.requested.top);
@@ -151,7 +141,8 @@
                 /* visiblePageTop */ 0,
                 /* visiblePageBottom */ 100,
                 /* availableTop */ -250,
-                /* availableBottom */ 250);
+                /* availableBottom */ 250,
+                /* max Tiles */ 30);
 
         ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(150));
         assertEquals("requested top", 150, result.requested.top);
@@ -170,7 +161,8 @@
                 /* visiblePageTop */ 0,
                 /* visiblePageBottom */ 100,
                 /* availableTop */ -100,
-                /* availableBottom */ 100);
+                /* availableBottom */ 100,
+                /* max Tiles */ 30);
 
         ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(-125));
         assertEquals("requested top", -125, result.requested.top);
@@ -189,7 +181,8 @@
                 /* visiblePageTop */ 0,
                 /* visiblePageBottom */ 100,
                 /* availableTop */ -100,
-                /* availableBottom */ 100);
+                /* availableBottom */ 100,
+                /* max Tiles */ 30);
 
         ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(75));
         assertEquals("requested top", 75, result.requested.top);
@@ -211,7 +204,8 @@
                 /* visiblePageTop */ 25,  // <<--
                 /* visiblePageBottom */ 100,
                 /* availableTop */ -150,
-                /* availableBottom */ 150);
+                /* availableBottom */ 150,
+                /* max Tiles */ 30);
 
         ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(-150));
         assertEquals("requested top", -150, result.requested.top);
@@ -233,7 +227,8 @@
                 /* visiblePageTop */ 0,
                 /* visiblePageBottom */ 75,
                 /* availableTop */ -150,
-                /* availableBottom */ 150);
+                /* availableBottom */ 150,
+                /* max Tiles */ 30);
 
         ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(50));
         assertEquals("requested top", 50, result.requested.top);
@@ -252,7 +247,8 @@
                 /* visiblePageTop */ 0,
                 /* visiblePageBottom */ 100,
                 /* availableTop */ -100,
-                /* availableBottom */ 200);
+                /* availableBottom */ 200,
+                /* max Tiles */ 30);
         ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(-150));
         assertTrue("captured rect is empty", result.captured.isEmpty());
     }
@@ -266,7 +262,8 @@
                 /* visiblePageTop */ 0,
                 /* visiblePageBottom */ 100,
                 /* availableTop */ -100,
-                /* availableBottom */ 200);
+                /* availableBottom */ 200,
+                /* max Tiles */ 30);
         ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(200));
         assertTrue("captured rect is empty", result.captured.isEmpty());
     }
@@ -280,7 +277,8 @@
                 /* visiblePageTop */ 60,  // <<---
                 /* visiblePageBottom */ 0,
                 /* availableTop */ -150,
-                /* availableBottom */ 150);
+                /* availableBottom */ 150,
+                /* max Tiles */ 30);
 
         ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(0));
         assertEquals("requested top", 0, result.requested.top);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java
index 5bab1bc..10c878a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java
@@ -92,14 +92,16 @@
         // Each tile is cropped to the visible page size, which is inset 5px from the TOP
         // requested    result
         //   0,   50       5,   50
-        // -45,    5     -40,    5   <-- clear previous /  top
-        //   5,   55       5,   55   (not cropped, target is positioned fully within visible range)
-        //  55,  105      55,  105
-        // 105,  155     105,  155
-        // 155,  205     155,  205   <-- bottom
+        // -45,    5     -40,    5
+        // -90,  -40     -85,  -40   <-- clear previous /  top
+        // -40,   10     -40,   10   (not cropped, target is positioned fully within visible range)
+        //  10,   60      10,   60
+        //  60,  110      60,  110
+        // 110,  160     110,  160
+        // 160,  210     160,  210   <-- bottom
 
-        assertEquals("top", -40, screenshot.getTop());
-        assertEquals("bottom", 205, screenshot.getBottom());
+        assertEquals("top", -85, screenshot.getTop());
+        assertEquals("bottom", 210, screenshot.getBottom());
     }
 
     @Test
@@ -119,13 +121,14 @@
         // requested      result
         //    0,   50        0,   50   // not cropped, positioned within visible range
         //  -50,    0      -50,    0   <-- clear previous/reverse
-        //    0,   50     -  0,   45   // target now positioned at page bottom, bottom cropped
+        //    0,   50        0,   45   // target now positioned at page bottom, bottom cropped
         //   45,   95,      45,   90
         //   90,  140,     140,  135
-        //  135,  185      185,  180   <-- bottom
+        //  135,  185      185,  180
+        //  180,  230      180,  225   <-- bottom
 
         assertEquals("top", -50, screenshot.getTop());
-        assertEquals("bottom", 180, screenshot.getBottom());
+        assertEquals("bottom", 225, screenshot.getBottom());
     }
 
     @Test
@@ -265,7 +268,8 @@
                 mLocalVisibleBottom = mPageHeight;
             }
             Session session = new FakeSession(mPageHeight, mMaxPages, mTileHeight,
-                    mLocalVisibleTop, mLocalVisibleBottom, mAvailableTop, mAvailableBottom);
+                    mLocalVisibleTop, mLocalVisibleBottom, mAvailableTop, mAvailableBottom,
+                    /* maxTiles */ 30);
             ScrollCaptureClient client = mock(ScrollCaptureClient.class);
             when(client.start(/* response */ any(), /* maxPages */ anyFloat()))
                     .thenReturn(immediateFuture(session));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt
index 7c7d2dc..2b44d8b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar
 
 import android.content.res.Resources
+import android.view.CrossWindowBlurListeners
 import android.view.SurfaceControl
 import android.view.ViewRootImpl
 import androidx.test.filters.SmallTest
@@ -37,11 +38,13 @@
     @Mock lateinit var resources: Resources
     @Mock lateinit var dumpManager: DumpManager
     @Mock lateinit var transaction: SurfaceControl.Transaction
+    @Mock lateinit var corssWindowBlurListeners: CrossWindowBlurListeners
     lateinit var blurUtils: BlurUtils
 
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
+        `when`(corssWindowBlurListeners.isCrossWindowBlurEnabled).thenReturn(true)
         blurUtils = TestableBlurUtils()
     }
 
@@ -71,7 +74,7 @@
         verify(transaction).apply()
     }
 
-    inner class TestableBlurUtils() : BlurUtils(resources, dumpManager) {
+    inner class TestableBlurUtils() : BlurUtils(resources, corssWindowBlurListeners, dumpManager) {
         override fun supportsBlursOnWindows(): Boolean {
             return true
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index 61c3835..60b3889 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -47,6 +47,7 @@
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.doThrow
 import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.junit.MockitoJUnit
@@ -71,6 +72,7 @@
     @Mock private lateinit var shadeSpring: NotificationShadeDepthController.DepthAnimation
     @Mock private lateinit var shadeAnimation: NotificationShadeDepthController.DepthAnimation
     @Mock private lateinit var globalActionsSpring: NotificationShadeDepthController.DepthAnimation
+    @Mock private lateinit var brightnessSpring: NotificationShadeDepthController.DepthAnimation
     @Mock private lateinit var listener: NotificationShadeDepthController.DepthListener
     @Mock private lateinit var dozeParameters: DozeParameters
     @Captor private lateinit var scrimVisibilityCaptor: ArgumentCaptor<Consumer<Int>>
@@ -90,7 +92,11 @@
         `when`(blurUtils.blurRadiusOfRatio(anyFloat())).then { answer ->
             (answer.arguments[0] as Float * maxBlur).toInt()
         }
-        `when`(blurUtils.minBlurRadius).thenReturn(0)
+        `when`(blurUtils.ratioOfBlurRadius(anyInt())).then { answer ->
+            answer.arguments[0] as Int / maxBlur.toFloat()
+        }
+        `when`(blurUtils.supportsBlursOnWindows()).thenReturn(true)
+        `when`(blurUtils.maxBlurRadius).thenReturn(maxBlur)
         `when`(blurUtils.maxBlurRadius).thenReturn(maxBlur)
 
         notificationShadeDepthController = NotificationShadeDepthController(
@@ -99,6 +105,7 @@
                 notificationShadeWindowController, dozeParameters, dumpManager)
         notificationShadeDepthController.shadeSpring = shadeSpring
         notificationShadeDepthController.shadeAnimation = shadeAnimation
+        notificationShadeDepthController.brightnessMirrorSpring = brightnessSpring
         notificationShadeDepthController.globalActionsSpring = globalActionsSpring
         notificationShadeDepthController.root = root
 
@@ -191,6 +198,14 @@
     }
 
     @Test
+    fun setFullShadeTransition_appliesBlur_onlyIfSupported() {
+        reset(blurUtils)
+        notificationShadeDepthController.transitionToFullShadeProgress = 1f
+        notificationShadeDepthController.updateBlurCallback.doFrame(0)
+        verify(blurUtils).applyBlur(any(), eq(0), eq(false))
+    }
+
+    @Test
     fun updateGlobalDialogVisibility_animatesBlur() {
         notificationShadeDepthController.updateGlobalDialogVisibility(0.5f, root)
         verify(globalActionsSpring).animateTo(eq(maxBlur / 2), eq(root))
@@ -267,6 +282,32 @@
     }
 
     @Test
+    fun brightnessMirrorVisible_whenVisible() {
+        notificationShadeDepthController.brightnessMirrorVisible = true
+        verify(brightnessSpring).animateTo(eq(maxBlur), any())
+    }
+
+    @Test
+    fun brightnessMirrorVisible_whenHidden() {
+        notificationShadeDepthController.brightnessMirrorVisible = false
+        verify(brightnessSpring).animateTo(eq(0), any())
+    }
+
+    @Test
+    fun brightnessMirror_hidesShadeBlur() {
+        // Brightness mirror is fully visible
+        `when`(brightnessSpring.ratio).thenReturn(1f)
+        // And shade is blurred
+        `when`(shadeSpring.radius).thenReturn(maxBlur)
+        `when`(shadeAnimation.radius).thenReturn(maxBlur)
+
+        notificationShadeDepthController.updateBlurCallback.doFrame(0)
+        verify(notificationShadeWindowController).setBackgroundBlurRadius(eq(0))
+        verify(wallpaperManager).setWallpaperZoomOut(any(), eq(1f))
+        verify(blurUtils).applyBlur(eq(viewRootImpl), eq(0), eq(false))
+    }
+
+    @Test
     fun ignoreShadeBlurUntilHidden_whennNull_ignoresIfShadeHasNoBlur() {
         `when`(shadeSpring.radius).thenReturn(0)
         `when`(shadeAnimation.radius).thenReturn(0)
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 2d51683..2693b94 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
@@ -26,9 +26,12 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -37,9 +40,13 @@
 
 import android.annotation.IdRes;
 import android.app.ActivityManager;
+import android.content.ContentResolver;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.database.ContentObserver;
 import android.hardware.biometrics.BiometricSourceType;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.PowerManager;
 import android.os.UserManager;
 import android.testing.AndroidTestingRunner;
@@ -50,6 +57,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewPropertyAnimator;
+import android.view.ViewStub;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 
@@ -60,6 +68,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.testing.UiEventLoggerFake;
+import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.LatencyTracker;
 import com.android.keyguard.KeyguardClockSwitch;
 import com.android.keyguard.KeyguardClockSwitchController;
@@ -140,6 +149,7 @@
     private KeyguardBottomAreaView mKeyguardBottomArea;
     @Mock
     private KeyguardBottomAreaView mQsFrame;
+    private KeyguardStatusView mKeyguardStatusView;
     @Mock
     private ViewGroup mBigClockContainer;
     @Mock
@@ -153,6 +163,10 @@
     @Mock
     private KeyguardStatusBarView mKeyguardStatusBar;
     @Mock
+    private View mUserSwitcherView;
+    @Mock
+    private ViewStub mUserSwitcherStubView;
+    @Mock
     private HeadsUpTouchHelper.Callback mHeadsUpCallback;
     @Mock
     private PanelBar mPanelBar;
@@ -204,7 +218,6 @@
     @Mock
     private KeyguardClockSwitch mKeyguardClockSwitch;
     private PanelViewController.TouchHandler mTouchHandler;
-    @Mock
     private ConfigurationController mConfigurationController;
     @Mock
     private MediaHierarchyManager mMediaHiearchyManager;
@@ -265,6 +278,8 @@
     @Mock
     private SecureSettings mSecureSettings;
     @Mock
+    private ContentResolver mContentResolver;
+    @Mock
     private TapAgainViewController mTapAgainViewController;
     @Mock
     private KeyguardIndicationController mKeyguardIndicationController;
@@ -287,6 +302,9 @@
         MockitoAnnotations.initMocks(this);
         mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger);
 
+        mKeyguardStatusView = new KeyguardStatusView(mContext);
+        mKeyguardStatusView.setId(R.id.keyguard_status_view);
+
         when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(false);
         when(mHeadsUpCallback.getContext()).thenReturn(mContext);
         when(mView.getResources()).thenReturn(mResources);
@@ -301,6 +319,9 @@
         when(mResources.getDimensionPixelSize(R.dimen.notification_panel_width)).thenReturn(400);
         when(mView.getContext()).thenReturn(getContext());
         when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar);
+        when(mView.findViewById(R.id.keyguard_user_switcher_view)).thenReturn(mUserSwitcherView);
+        when(mView.findViewById(R.id.keyguard_user_switcher_stub)).thenReturn(
+                mUserSwitcherStubView);
         when(mView.findViewById(R.id.keyguard_clock_container)).thenReturn(mKeyguardClockSwitch);
         when(mView.findViewById(R.id.notification_stack_scroller))
                 .thenReturn(mNotificationStackScrollLayout);
@@ -320,7 +341,7 @@
         mNotificationContainerParent = new NotificationsQuickSettingsContainer(getContext(), null);
         mNotificationContainerParent.addView(newViewWithId(R.id.qs_frame));
         mNotificationContainerParent.addView(newViewWithId(R.id.notification_stack_scroller));
-        mNotificationContainerParent.addView(newViewWithId(R.id.keyguard_status_view));
+        mNotificationContainerParent.addView(mKeyguardStatusView);
         when(mView.findViewById(R.id.notification_container_parent))
                 .thenReturn(mNotificationContainerParent);
         when(mFragmentService.getFragmentHostManager(mView)).thenReturn(mFragmentHostManager);
@@ -348,6 +369,7 @@
                 mFalsingManager,
                 mLockscreenShadeTransitionController,
                 new FalsingCollectorFake());
+        mConfigurationController = new ConfigurationControllerImpl(mContext);
         when(mKeyguardStatusViewComponentFactory.build(any()))
                 .thenReturn(mKeyguardStatusViewComponent);
         when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController())
@@ -358,10 +380,16 @@
                 .thenReturn(mKeyguardStatusBarViewComponent);
         when(mKeyguardStatusBarViewComponent.getKeyguardStatusBarViewController())
                 .thenReturn(mKeyguardStatusBarViewController);
+        when(mLayoutInflater.inflate(eq(R.layout.keyguard_status_view), any(), anyBoolean()))
+                .thenReturn(mKeyguardStatusView);
+        when(mLayoutInflater.inflate(eq(R.layout.keyguard_bottom_area), any(), anyBoolean()))
+                .thenReturn(mKeyguardBottomArea);
 
         reset(mView);
+
         mNotificationPanelViewController = new NotificationPanelViewController(mView,
                 mResources,
+                new Handler(Looper.getMainLooper()),
                 mLayoutInflater,
                 coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController,
                 mFalsingManager, new FalsingCollectorFake(),
@@ -395,6 +423,7 @@
                 mTapAgainViewController,
                 mNavigationModeController,
                 mFragmentService,
+                mContentResolver,
                 mQuickAccessWalletController,
                 new FakeExecutor(new FakeSystemClock()),
                 mSecureSettings,
@@ -549,6 +578,38 @@
     }
 
     @Test
+    public void testDisableUserSwitcherAfterEnabling_returnsViewStubToTheViewHierarchy() {
+        givenViewAttached();
+        when(mResources.getBoolean(
+                com.android.internal.R.bool.config_keyguardUserSwitcher)).thenReturn(true);
+        updateMultiUserSetting(true);
+        clearInvocations(mView);
+
+        updateMultiUserSetting(false);
+
+        ArgumentCaptor<View> captor = ArgumentCaptor.forClass(View.class);
+        verify(mView, atLeastOnce()).addView(captor.capture(), anyInt());
+        final View userSwitcherStub = CollectionUtils.find(captor.getAllValues(),
+                view -> view.getId() == R.id.keyguard_user_switcher_stub);
+        assertThat(userSwitcherStub).isNotNull();
+        assertThat(userSwitcherStub).isInstanceOf(ViewStub.class);
+    }
+
+    @Test
+    public void testChangeSmallestScreenWidthAndUserSwitchEnabled_inflatesUserSwitchView() {
+        givenViewAttached();
+        when(mView.findViewById(R.id.keyguard_user_switcher_view)).thenReturn(null);
+        updateSmallestScreenWidth(300);
+        when(mResources.getBoolean(
+                com.android.internal.R.bool.config_keyguardUserSwitcher)).thenReturn(true);
+        when(mUserManager.isUserSwitcherEnabled()).thenReturn(true);
+
+        updateSmallestScreenWidth(800);
+
+        verify(mUserSwitcherStubView).inflate();
+    }
+
+    @Test
     public void testSplitShadeLayout_isAlignedToGuideline() {
         enableSplitShade();
 
@@ -682,6 +743,12 @@
         return mFalsingManager.getTapListeners().get(0);
     }
 
+    private void givenViewAttached() {
+        for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) {
+            listener.onViewAttachedToWindow(mView);
+        }
+    }
+
     private View newViewWithId(int id) {
         View view = new View(mContext);
         view.setId(id);
@@ -704,6 +771,21 @@
         mNotificationPanelViewController.updateResources();
     }
 
+    private void updateMultiUserSetting(boolean enabled) {
+        when(mUserManager.isUserSwitcherEnabled()).thenReturn(enabled);
+        final ArgumentCaptor<ContentObserver> observerCaptor =
+                ArgumentCaptor.forClass(ContentObserver.class);
+        verify(mContentResolver)
+                .registerContentObserver(any(), anyBoolean(), observerCaptor.capture());
+        observerCaptor.getValue().onChange(/* selfChange */ false);
+    }
+
+    private void updateSmallestScreenWidth(int smallestScreenWidthDp) {
+        Configuration configuration = new Configuration();
+        configuration.smallestScreenWidthDp = smallestScreenWidthDp;
+        mConfigurationController.onConfigurationChanged(configuration);
+    }
+
     private void onTouchEvent(MotionEvent ev) {
         mTouchHandler.onTouch(mView, ev);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index e55361e..678b193 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -312,6 +312,8 @@
                 mScrimBehind, true,
                 mScrimForBubble, false
         ));
+
+        assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
     }
 
     @Test
@@ -321,8 +323,9 @@
 
         assertScrimAlpha(Map.of(
                 mScrimInFront, TRANSPARENT,
-                mScrimBehind, OPAQUE,
+                mScrimBehind, TRANSPARENT,
                 mNotificationsScrim, TRANSPARENT));
+        assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
 
         assertScrimTinted(Map.of(
                 mScrimInFront, true,
@@ -340,6 +343,7 @@
         assertScrimAlpha(Map.of(
                 mScrimInFront, TRANSPARENT,
                 mScrimBehind, TRANSPARENT));
+        assertEquals(0f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
 
         // Pulsing notification should conserve AOD wallpaper.
         mScrimController.transitionTo(ScrimState.PULSING);
@@ -348,6 +352,7 @@
         assertScrimAlpha(Map.of(
                 mScrimInFront, TRANSPARENT,
                 mScrimBehind, TRANSPARENT));
+        assertEquals(0f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
     }
 
     @Test
@@ -359,7 +364,8 @@
 
         assertScrimAlpha(Map.of(
                 mScrimInFront, TRANSPARENT,
-                mScrimBehind, OPAQUE));
+                mScrimBehind, TRANSPARENT));
+        assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
 
         assertScrimTinted(Map.of(
                 mScrimInFront, true,
@@ -378,7 +384,8 @@
 
         assertScrimAlpha(Map.of(
                 mScrimInFront, TRANSPARENT,
-                mScrimBehind, OPAQUE));
+                mScrimBehind, TRANSPARENT));
+        assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
 
         assertScrimTinted(Map.of(
                 mScrimInFront, true,
@@ -403,13 +410,15 @@
         finishAnimationsImmediately();
         assertScrimAlpha(Map.of(
                 mScrimInFront, SEMI_TRANSPARENT,
-                mScrimBehind, OPAQUE));
+                mScrimBehind, TRANSPARENT));
+        assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
 
         // ... and that if we set it while we're in AOD, it does take immediate effect.
         mScrimController.setAodFrontScrimAlpha(1f);
         assertScrimAlpha(Map.of(
                 mScrimInFront, OPAQUE,
-                mScrimBehind, OPAQUE));
+                mScrimBehind, TRANSPARENT));
+        assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
 
         // ... and make sure we recall the previous front scrim alpha even if we transition away
         // for a bit.
@@ -418,7 +427,8 @@
         finishAnimationsImmediately();
         assertScrimAlpha(Map.of(
                 mScrimInFront, OPAQUE,
-                mScrimBehind, OPAQUE));
+                mScrimBehind, TRANSPARENT));
+        assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
 
         // ... and alpha updates should be completely ignored if always_on is off.
         // Passing it forward would mess up the wake-up transition.
@@ -448,23 +458,28 @@
         finishAnimationsImmediately();
         assertScrimAlpha(Map.of(
                 mScrimInFront, OPAQUE,
-                mScrimBehind, OPAQUE));
+                mScrimBehind, TRANSPARENT));
+        assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
 
         // ... but will take effect after docked
         when(mDockManager.isDocked()).thenReturn(true);
         mScrimController.transitionTo(ScrimState.KEYGUARD);
         mScrimController.setAodFrontScrimAlpha(0.5f);
         mScrimController.transitionTo(ScrimState.AOD);
+        finishAnimationsImmediately();
 
         assertScrimAlpha(Map.of(
                 mScrimInFront, SEMI_TRANSPARENT,
-                mScrimBehind, OPAQUE));
+                mScrimBehind, TRANSPARENT));
+        assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
 
         // ... and that if we set it while we're in AOD, it does take immediate effect after docked.
         mScrimController.setAodFrontScrimAlpha(1f);
+        finishAnimationsImmediately();
         assertScrimAlpha(Map.of(
                 mScrimInFront, OPAQUE,
-                mScrimBehind, OPAQUE));
+                mScrimBehind, TRANSPARENT));
+        assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
 
         // Reset value since enums are static.
         mScrimController.setAodFrontScrimAlpha(0f);
@@ -480,7 +495,8 @@
         finishAnimationsImmediately();
         assertScrimAlpha(Map.of(
                 mScrimInFront, TRANSPARENT,
-                mScrimBehind, OPAQUE));
+                mScrimBehind, TRANSPARENT));
+        assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
 
         mScrimController.transitionTo(ScrimState.PULSING);
         finishAnimationsImmediately();
@@ -489,7 +505,8 @@
         // Pulse callback should have been invoked
         assertScrimAlpha(Map.of(
                 mScrimInFront, TRANSPARENT,
-                mScrimBehind, OPAQUE));
+                mScrimBehind, TRANSPARENT));
+        assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
 
         assertScrimTinted(Map.of(
                 mScrimInFront, true,
@@ -503,13 +520,16 @@
         // Front scrim should be semi-transparent
         assertScrimAlpha(Map.of(
                 mScrimInFront, SEMI_TRANSPARENT,
-                mScrimBehind, OPAQUE));
+                mScrimBehind, TRANSPARENT));
+        assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
 
         mScrimController.setWakeLockScreenSensorActive(true);
         finishAnimationsImmediately();
         assertScrimAlpha(Map.of(
                 mScrimInFront, SEMI_TRANSPARENT,
-                mScrimBehind, SEMI_TRANSPARENT));
+                mScrimBehind, TRANSPARENT));
+        assertEquals(ScrimController.WAKE_SENSOR_SCRIM_ALPHA,
+                mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
 
         // Reset value since enums are static.
         mScrimController.setAodFrontScrimAlpha(0f);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index 521b958..bd9d1a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -238,9 +238,7 @@
         mNetworkController.setNoNetworksAvailable(false);
         setWifiStateForVcn(true, testSsid);
         setWifiLevelForVcn(0);
-        // Connected, but still not validated - does not show
-        //verifyLastWifiIcon(false, WifiIcons.WIFI_SIGNAL_STRENGTH[0][0]);
-        verifyLastMobileDataIndicatorsForVcn(false, 0, 0, false);
+        verifyLastMobileDataIndicatorsForVcn(true, 0, TelephonyIcons.ICON_CWF, false);
 
         mNetworkController.setNoNetworksAvailable(true);
         for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 208790b..1a24c11 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -415,13 +415,6 @@
     }
 
     @Test
-    public void onUserSwitch_setsTheme() {
-        mBroadcastReceiver.getValue().onReceive(null,
-                new Intent(Intent.ACTION_USER_STARTED));
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
-    }
-
-    @Test
     public void onProfileAdded_setsTheme() {
         mBroadcastReceiver.getValue().onReceive(null,
                 new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index ff15d01..5691660 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -27,6 +27,7 @@
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.statusbar.CommandQueue;
@@ -70,6 +71,7 @@
     @Mock LegacySplitScreen mLegacySplitScreen;
     @Mock OneHanded mOneHanded;
     @Mock HideDisplayCutout mHideDisplayCutout;
+    @Mock WakefulnessLifecycle mWakefulnessLifecycle;
     @Mock ProtoTracer mProtoTracer;
     @Mock ShellCommandHandler mShellCommandHandler;
     @Mock ShellExecutor mSysUiMainExecutor;
@@ -82,7 +84,8 @@
                 Optional.of(mOneHanded), Optional.of(mHideDisplayCutout),
                 Optional.of(mShellCommandHandler), mCommandQueue, mConfigurationController,
                 mKeyguardUpdateMonitor, mNavigationModeController,
-                mScreenLifecycle, mSysUiState, mProtoTracer, mSysUiMainExecutor);
+                mScreenLifecycle, mSysUiState, mProtoTracer, mWakefulnessLifecycle,
+                mSysUiMainExecutor);
     }
 
     @Test
diff --git a/packages/services/CameraExtensionsProxy/AndroidManifest.xml b/packages/services/CameraExtensionsProxy/AndroidManifest.xml
index e5f460e..d356894 100644
--- a/packages/services/CameraExtensionsProxy/AndroidManifest.xml
+++ b/packages/services/CameraExtensionsProxy/AndroidManifest.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.camera">
+    package="com.android.cameraextensions">
 
     <application
         android:label="@string/app_name"
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/camera/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
similarity index 99%
rename from packages/services/CameraExtensionsProxy/src/com/android/camera/CameraExtensionsProxyService.java
rename to packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index 7b9ca2d..69874f8 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/camera/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.camera;
+package com.android.cameraextensions;
 
 import android.app.Service;
 import android.content.Context;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 241a0db..f631988 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -872,18 +872,32 @@
                     "userId=" + userId);
         }
 
+        final int resolvedUserId;
+        final List<AccessibilityServiceInfo> serviceInfos;
         synchronized (mLock) {
             // We treat calls from a profile as if made by its parent as profiles
             // share the accessibility state of the parent. The call below
             // performs the current profile parent resolution.
-            final int resolvedUserId = mSecurityPolicy
+            resolvedUserId = mSecurityPolicy
                     .resolveCallingUserIdEnforcingPermissionsLocked(userId);
-
-            if (Binder.getCallingPid() == OWN_PROCESS_ID) {
-                return new ArrayList<>(getUserStateLocked(resolvedUserId).mInstalledServices);
-            }
-            return getUserStateLocked(resolvedUserId).mInstalledServices;
+            serviceInfos = new ArrayList<>(
+                    getUserStateLocked(resolvedUserId).mInstalledServices);
         }
+
+        if (Binder.getCallingPid() == OWN_PROCESS_ID) {
+            return serviceInfos;
+        }
+        final PackageManagerInternal pm = LocalServices.getService(
+                PackageManagerInternal.class);
+        final int callingUid = Binder.getCallingUid();
+        for (int i = serviceInfos.size() - 1; i >= 0; i--) {
+            final AccessibilityServiceInfo serviceInfo = serviceInfos.get(i);
+            if (pm.filterAppAccess(serviceInfo.getComponentName().getPackageName(), callingUid,
+                    resolvedUserId)) {
+                serviceInfos.remove(i);
+            }
+        }
+        return serviceInfos;
     }
 
     @Override
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index acbf487..5aec6aa 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -409,6 +409,8 @@
 
                 // If the set of providers has been modified, notify each active AppWidgetHost
                 scheduleNotifyGroupHostsForProvidersChangedLocked(userId);
+                // Possibly notify any new components of widget id changes
+                mBackupRestoreController.widgetComponentsChanged(userId);
             }
         }
     }
@@ -2471,8 +2473,8 @@
     }
 
     @Override
-    public void restoreStarting(int userId) {
-        mBackupRestoreController.restoreStarting(userId);
+    public void systemRestoreStarting(int userId) {
+        mBackupRestoreController.systemRestoreStarting(userId);
     }
 
     @Override
@@ -2481,8 +2483,8 @@
     }
 
     @Override
-    public void restoreFinished(int userId) {
-        mBackupRestoreController.restoreFinished(userId);
+    public void systemRestoreFinished(int userId) {
+        mBackupRestoreController.systemRestoreFinished(userId);
     }
 
     @SuppressWarnings("deprecation")
@@ -4272,6 +4274,9 @@
         private final HashMap<Host, ArrayList<RestoreUpdateRecord>> mUpdatesByHost =
                 new HashMap<>();
 
+        @GuardedBy("mLock")
+        private boolean mHasSystemRestoreFinished;
+
         public List<String> getWidgetParticipants(int userId) {
             if (DEBUG) {
                 Slog.i(TAG, "Getting widget participants for user: " + userId);
@@ -4375,12 +4380,13 @@
             return stream.toByteArray();
         }
 
-        public void restoreStarting(int userId) {
+        public void systemRestoreStarting(int userId) {
             if (DEBUG) {
-                Slog.i(TAG, "Restore starting for user: " + userId);
+                Slog.i(TAG, "System restore starting for user: " + userId);
             }
 
             synchronized (mLock) {
+                mHasSystemRestoreFinished = false;
                 // We're starting a new "system" restore operation, so any widget restore
                 // state that we see from here on is intended to replace the current
                 // widget configuration of any/all of the affected apps.
@@ -4542,26 +4548,90 @@
             }
         }
 
-        // Called once following the conclusion of a restore operation.  This is when we
+        // Called once following the conclusion of a system restore operation.  This is when we
         // send out updates to apps involved in widget-state restore telling them about
-        // the new widget ID space.
-        public void restoreFinished(int userId) {
+        // the new widget ID space.  Apps that are not yet installed will be notifed when they are.
+        public void systemRestoreFinished(int userId) {
             if (DEBUG) {
-                Slog.i(TAG, "restoreFinished for " + userId);
+                Slog.i(TAG, "systemRestoreFinished for " + userId);
+            }
+            synchronized (mLock) {
+                mHasSystemRestoreFinished = true;
+                maybeSendWidgetRestoreBroadcastsLocked(userId);
+            }
+        }
+
+        // Called when widget components (hosts or providers) are added or changed.  If system
+        // restore has completed, we use this opportunity to tell the apps to update to the new
+        // widget ID space.  If system restore is still in progress, we delay the updates until
+        // the end, to allow all participants to restore their state before updating widget IDs.
+        public void widgetComponentsChanged(int userId) {
+            synchronized (mLock) {
+                if (mHasSystemRestoreFinished) {
+                    maybeSendWidgetRestoreBroadcastsLocked(userId);
+                }
+            }
+        }
+
+        // Called following the conclusion of a restore operation and when widget components
+        // are added or changed.  This is when we send out updates to apps involved in widget-state
+        // restore telling them about the new widget ID space.
+        @GuardedBy("mLock")
+        private void maybeSendWidgetRestoreBroadcastsLocked(int userId) {
+            if (DEBUG) {
+                Slog.i(TAG, "maybeSendWidgetRestoreBroadcasts for " + userId);
             }
 
             final UserHandle userHandle = new UserHandle(userId);
-            synchronized (mLock) {
-                // Build the providers' broadcasts and send them off
-                Set<Map.Entry<Provider, ArrayList<RestoreUpdateRecord>>> providerEntries
-                        = mUpdatesByProvider.entrySet();
-                for (Map.Entry<Provider, ArrayList<RestoreUpdateRecord>> e : providerEntries) {
-                    // For each provider there's a list of affected IDs
-                    Provider provider = e.getKey();
+            // Build the providers' broadcasts and send them off
+            Set<Map.Entry<Provider, ArrayList<RestoreUpdateRecord>>> providerEntries
+                    = mUpdatesByProvider.entrySet();
+            for (Map.Entry<Provider, ArrayList<RestoreUpdateRecord>> e : providerEntries) {
+                // For each provider there's a list of affected IDs
+                Provider provider = e.getKey();
+                if (provider.zombie) {
+                    // Provider not installed, we can't send them broadcasts yet.
+                    // We'll be called again when the provider is installed.
+                    continue;
+                }
+                ArrayList<RestoreUpdateRecord> updates = e.getValue();
+                final int pending = countPendingUpdates(updates);
+                if (DEBUG) {
+                    Slog.i(TAG, "Provider " + provider + " pending: " + pending);
+                }
+                if (pending > 0) {
+                    int[] oldIds = new int[pending];
+                    int[] newIds = new int[pending];
+                    final int N = updates.size();
+                    int nextPending = 0;
+                    for (int i = 0; i < N; i++) {
+                        RestoreUpdateRecord r = updates.get(i);
+                        if (!r.notified) {
+                            r.notified = true;
+                            oldIds[nextPending] = r.oldId;
+                            newIds[nextPending] = r.newId;
+                            nextPending++;
+                            if (DEBUG) {
+                                Slog.i(TAG, "   " + r.oldId + " => " + r.newId);
+                            }
+                        }
+                    }
+                    sendWidgetRestoreBroadcastLocked(
+                            AppWidgetManager.ACTION_APPWIDGET_RESTORED,
+                            provider, null, oldIds, newIds, userHandle);
+                }
+            }
+
+            // same thing per host
+            Set<Map.Entry<Host, ArrayList<RestoreUpdateRecord>>> hostEntries
+                    = mUpdatesByHost.entrySet();
+            for (Map.Entry<Host, ArrayList<RestoreUpdateRecord>> e : hostEntries) {
+                Host host = e.getKey();
+                if (host.id.uid != UNKNOWN_UID) {
                     ArrayList<RestoreUpdateRecord> updates = e.getValue();
                     final int pending = countPendingUpdates(updates);
                     if (DEBUG) {
-                        Slog.i(TAG, "Provider " + provider + " pending: " + pending);
+                        Slog.i(TAG, "Host " + host + " pending: " + pending);
                     }
                     if (pending > 0) {
                         int[] oldIds = new int[pending];
@@ -4581,43 +4651,8 @@
                             }
                         }
                         sendWidgetRestoreBroadcastLocked(
-                                AppWidgetManager.ACTION_APPWIDGET_RESTORED,
-                                provider, null, oldIds, newIds, userHandle);
-                    }
-                }
-
-                // same thing per host
-                Set<Map.Entry<Host, ArrayList<RestoreUpdateRecord>>> hostEntries
-                        = mUpdatesByHost.entrySet();
-                for (Map.Entry<Host, ArrayList<RestoreUpdateRecord>> e : hostEntries) {
-                    Host host = e.getKey();
-                    if (host.id.uid != UNKNOWN_UID) {
-                        ArrayList<RestoreUpdateRecord> updates = e.getValue();
-                        final int pending = countPendingUpdates(updates);
-                        if (DEBUG) {
-                            Slog.i(TAG, "Host " + host + " pending: " + pending);
-                        }
-                        if (pending > 0) {
-                            int[] oldIds = new int[pending];
-                            int[] newIds = new int[pending];
-                            final int N = updates.size();
-                            int nextPending = 0;
-                            for (int i = 0; i < N; i++) {
-                                RestoreUpdateRecord r = updates.get(i);
-                                if (!r.notified) {
-                                    r.notified = true;
-                                    oldIds[nextPending] = r.oldId;
-                                    newIds[nextPending] = r.newId;
-                                    nextPending++;
-                                    if (DEBUG) {
-                                        Slog.i(TAG, "   " + r.oldId + " => " + r.newId);
-                                    }
-                                }
-                            }
-                            sendWidgetRestoreBroadcastLocked(
-                                    AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED,
-                                    null, host, oldIds, newIds, userHandle);
-                        }
+                                AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED,
+                                null, host, oldIds, newIds, userHandle);
                     }
                 }
             }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 312cde6..de5f47d 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -662,6 +662,26 @@
         return false;
     }
 
+    /**
+     * Requests a count of saved passwords from the current service.
+     *
+     * @return {@code true} if the request succeeded
+     */
+    // Called by Shell command
+    boolean requestSavedPasswordCount(@UserIdInt int userId, @NonNull IResultReceiver receiver) {
+        enforceCallingPermissionForManagement();
+        synchronized (mLock) {
+            final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+            if (service != null) {
+                service.requestSavedPasswordCount(receiver);
+                return true;
+            } else if (sVerbose) {
+                Slog.v(TAG, "requestSavedPasswordCount(): no service for " + userId);
+            }
+        }
+        return false;
+    }
+
     private void setLoggingLevelsLocked(boolean debug, boolean verbose) {
         com.android.server.autofill.Helper.sDebug = debug;
         android.view.autofill.Helper.sDebug = debug;
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index a80efc4..5f2d4e8 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -76,6 +76,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.os.IResultReceiver;
 import com.android.server.LocalServices;
 import com.android.server.autofill.AutofillManagerService.AutofillCompatState;
 import com.android.server.autofill.AutofillManagerService.DisabledInfoCache;
@@ -1181,6 +1182,15 @@
     }
 
     @GuardedBy("mLock")
+    void requestSavedPasswordCount(IResultReceiver receiver) {
+        RemoteFillService remoteService =
+                new RemoteFillService(
+                        getContext(), mInfo.getServiceInfo().getComponentName(), mUserId,
+                        /* callbacks= */ null, mMaster.isInstantServiceAllowed());
+        remoteService.onSavedPasswordCountRequest(receiver);
+    }
+
+    @GuardedBy("mLock")
     @Nullable RemoteAugmentedAutofillService getRemoteAugmentedAutofillServiceLocked() {
         if (mRemoteAugmentedAutofillService == null) {
             final String serviceName = mMaster.mAugmentedAutofillResolver.getServiceName(mUserId);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
index 68e6290..1eaa59a 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
@@ -17,6 +17,7 @@
 package com.android.server.autofill;
 
 import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES;
+import static android.service.autofill.AutofillService.EXTRA_RESULT;
 
 import static com.android.server.autofill.AutofillManagerService.RECEIVER_BUNDLE_EXTRA_SESSIONS;
 
@@ -89,6 +90,9 @@
             pw.println("  get bind-instant-service-allowed");
             pw.println("    Gets whether binding to services provided by instant apps is allowed");
             pw.println("");
+            pw.println("  get saved-password-count");
+            pw.println("    Gets the number of saved passwords in the current service.");
+            pw.println("");
             pw.println("  set log_level [off | debug | verbose]");
             pw.println("    Sets the Autofill log level.");
             pw.println("");
@@ -145,6 +149,8 @@
                 return getBindInstantService(pw);
             case "default-augmented-service-enabled":
                 return getDefaultAugmentedServiceEnabled(pw);
+            case "saved-password-count":
+                return getSavedPasswordCount(pw);
             default:
                 pw.println("Invalid set: " + what);
                 return -1;
@@ -342,6 +348,25 @@
         return 0;
     }
 
+    private int getSavedPasswordCount(PrintWriter pw) {
+        final int userId = getNextIntArgRequired();
+        CountDownLatch latch = new CountDownLatch(1);
+        IResultReceiver resultReceiver = new IResultReceiver.Stub() {
+            @Override
+            public void send(int resultCode, Bundle resultData) {
+                pw.println("resultCode=" + resultCode);
+                if (resultCode == 0 && resultData != null) {
+                    pw.println("value=" + resultData.getInt(EXTRA_RESULT));
+                }
+                latch.countDown();
+            }
+        };
+        if (mService.requestSavedPasswordCount(userId, resultReceiver)) {
+            waitForLatch(pw, latch);
+        }
+        return 0;
+    }
+
     private int requestDestroy(PrintWriter pw) {
         if (!isNextArgSessions(pw)) {
             return -1;
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index b0755ac..94872b0 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -41,6 +41,7 @@
 
 import com.android.internal.infra.AbstractRemoteService;
 import com.android.internal.infra.ServiceConnector;
+import com.android.internal.os.IResultReceiver;
 
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.CompletableFuture;
@@ -225,6 +226,10 @@
                 }));
     }
 
+    void onSavedPasswordCountRequest(IResultReceiver receiver) {
+        run(service -> service.onSavedPasswordCountRequest(receiver));
+    }
+
     public void destroy() {
         unbind();
     }
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 261ebe6..f07bac8 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -381,7 +381,7 @@
 
         // If we're starting a full-system restore, set up to begin widget ID remapping
         if (mIsSystemRestore) {
-            AppWidgetBackupBridge.restoreStarting(mUserId);
+            AppWidgetBackupBridge.systemRestoreStarting(mUserId);
         }
 
         try {
@@ -1133,8 +1133,10 @@
                     restoreAgentTimeoutMillis);
         }
 
-        // Kick off any work that may be needed regarding app widget restores
-        AppWidgetBackupBridge.restoreFinished(mUserId);
+        if (mIsSystemRestore) {
+            // Kick off any work that may be needed regarding app widget restores
+            AppWidgetBackupBridge.systemRestoreFinished(mUserId);
+        }
 
         // If this was a full-system restore, record the ancestral
         // dataset information
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 05131d4..906edb3 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -753,9 +753,19 @@
             }
             return notMatch;
         }));
+        restartBleScan();
     }
 
     void onAssociationPreRemove(Association association) {
+        if (association.isNotifyOnDeviceNearby()) {
+            ServiceConnector<ICompanionDeviceService> serviceConnector =
+                    mDeviceListenerServiceConnectors.forUser(association.getUserId())
+                            .get(association.getPackageName());
+            if (serviceConnector != null) {
+                serviceConnector.unbind();
+            }
+        }
+
         String deviceProfile = association.getDeviceProfile();
         if (deviceProfile != null) {
             Association otherAssociationWithDeviceProfile = find(
@@ -787,16 +797,6 @@
                 }
             }
         }
-
-        if (association.isNotifyOnDeviceNearby()) {
-            ServiceConnector<ICompanionDeviceService> serviceConnector =
-                    mDeviceListenerServiceConnectors.forUser(association.getUserId())
-                            .get(association.getPackageName());
-            if (serviceConnector != null) {
-                serviceConnector.unbind();
-                restartBleScan();
-            }
-        }
     }
 
     private void updateSpecialAccessPermissionForAssociatedPackage(Association association) {
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 9299624..f7ddd29 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -1151,6 +1151,8 @@
 
     /**
      * Deletes the OAT artifacts of a package.
+     * @param packageName a specific package
+     * @return the number of freed bytes or -1 if there was an error in the process.
      */
-    public abstract void deleteOatArtifactsOfPackage(String packageName);
+    public abstract long deleteOatArtifactsOfPackage(String packageName);
 }
diff --git a/services/core/java/com/android/server/VpnManagerService.java b/services/core/java/com/android/server/VpnManagerService.java
index 26ecee8..70176a0 100644
--- a/services/core/java/com/android/server/VpnManagerService.java
+++ b/services/core/java/com/android/server/VpnManagerService.java
@@ -38,6 +38,7 @@
 import android.net.VpnService;
 import android.net.util.NetdService;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.INetworkManagementService;
@@ -348,9 +349,17 @@
     /**
      * Start legacy VPN, controlling native daemons as needed. Creates a
      * secondary thread to perform connection work, returning quickly.
+     *
+     * Legacy VPN is deprecated starting from Android S. So this API shouldn't be called if the
+     * initial SDK version of device is Android S+. Otherwise, UnsupportedOperationException will be
+     * thrown.
      */
+    @SuppressWarnings("AndroidFrameworkCompatChange")  // This is not an app-visible API.
     @Override
     public void startLegacyVpn(VpnProfile profile) {
+        if (Build.VERSION.DEVICE_INITIAL_SDK_INT >= Build.VERSION_CODES.S) {
+            throw new UnsupportedOperationException("Legacy VPN is deprecated");
+        }
         int user = UserHandle.getUserId(mDeps.getCallingUid());
         // Note that if the caller is not system (uid >= Process.FIRST_APPLICATION_UID),
         // the code might not work well since getActiveNetwork might return null if the uid is
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2de091b..0c97724 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5684,6 +5684,16 @@
         if (pid == MY_PID) {
             return PackageManager.PERMISSION_GRANTED;
         }
+        try {
+            if (uid != 0) { // bypass the root
+                final String[] packageNames = getPackageManager().getPackagesForUid(uid);
+                if (ArrayUtils.isEmpty(packageNames)) {
+                    // The uid is not existed or not visible to the caller.
+                    return PackageManager.PERMISSION_DENIED;
+                }
+            }
+        } catch (RemoteException e) {
+        }
         return mUgmInternal.checkUriPermission(new GrantUri(userId, uri, modeFlags), uid, modeFlags)
                 ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED;
     }
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 966e746..317b775 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -203,8 +203,6 @@
                                 updateMinOomAdjThrottle();
                             } else if (KEY_COMPACT_THROTTLE_MAX_OOM_ADJ.equals(name)) {
                                 updateMaxOomAdjThrottle();
-                            } else if (KEY_FREEZER_DEBOUNCE_TIMEOUT.equals(name)) {
-                                updateFreezerDebounceTimeout();
                             }
                         }
                     }
@@ -344,7 +342,6 @@
             updateUseFreezer();
             updateMinOomAdjThrottle();
             updateMaxOomAdjThrottle();
-            updateFreezerDebounceTimeout();
         }
     }
 
@@ -656,6 +653,7 @@
                 || DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
                     KEY_USE_FREEZER, DEFAULT_USE_FREEZER)) {
             mUseFreezer = isFreezerSupported();
+            updateFreezerDebounceTimeout();
         }
 
         final boolean useFreezer = mUseFreezer;
@@ -834,7 +832,8 @@
 
     @GuardedBy("mPhenotypeFlagLock")
     private void updateFreezerDebounceTimeout() {
-        mFreezerDebounceTimeout = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+        mFreezerDebounceTimeout = DeviceConfig.getLong(
+                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
                 KEY_FREEZER_DEBOUNCE_TIMEOUT, DEFAULT_FREEZER_DEBOUNCE_TIMEOUT);
 
         if (mFreezerDebounceTimeout < 0) {
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 697f6fa..ae1cd51 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -21,10 +21,18 @@
 import static android.content.Intent.ACTION_PACKAGE_REMOVED;
 
 import static com.android.server.wm.CompatModePackages.DOWNSCALED;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_30;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_35;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_40;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_45;
 import static com.android.server.wm.CompatModePackages.DOWNSCALE_50;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_55;
 import static com.android.server.wm.CompatModePackages.DOWNSCALE_60;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_65;
 import static com.android.server.wm.CompatModePackages.DOWNSCALE_70;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_75;
 import static com.android.server.wm.CompatModePackages.DOWNSCALE_80;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_85;
 import static com.android.server.wm.CompatModePackages.DOWNSCALE_90;
 
 import android.Manifest;
@@ -213,6 +221,39 @@
         }
     }
 
+    // Turn the raw string to the corresponding CompatChange id.
+    static long getCompatChangeId(String raw) {
+        switch (raw) {
+            case "0.3":
+                return DOWNSCALE_30;
+            case "0.35":
+                return DOWNSCALE_35;
+            case "0.4":
+                return DOWNSCALE_40;
+            case "0.45":
+                return DOWNSCALE_45;
+            case "0.5":
+                return DOWNSCALE_50;
+            case "0.55":
+                return DOWNSCALE_55;
+            case "0.6":
+                return DOWNSCALE_60;
+            case "0.65":
+                return DOWNSCALE_65;
+            case "0.7":
+                return DOWNSCALE_70;
+            case "0.75":
+                return DOWNSCALE_75;
+            case "0.8":
+                return DOWNSCALE_80;
+            case "0.85":
+                return DOWNSCALE_85;
+            case "0.9":
+                return DOWNSCALE_90;
+        }
+        return 0;
+    }
+
     /**
      * GamePackageConfiguration manages all game mode config details for its associated package.
      */
@@ -331,19 +372,7 @@
              * Get the corresponding compat change id for the current scaling string.
              */
             public long getCompatChangeId() {
-                switch (mScaling) {
-                    case "0.5":
-                        return DOWNSCALE_50;
-                    case "0.6":
-                        return DOWNSCALE_60;
-                    case "0.7":
-                        return DOWNSCALE_70;
-                    case "0.8":
-                        return DOWNSCALE_80;
-                    case "0.9":
-                        return DOWNSCALE_90;
-                }
-                return 0;
+                return GameManagerService.getCompatChangeId(mScaling);
             }
         }
 
@@ -663,10 +692,18 @@
             Slog.i(TAG, "Enabling downscale: " + scaleId + " for " + packageName);
             final ArrayMap<Long, PackageOverride> overrides = new ArrayMap<>();
             overrides.put(DOWNSCALED, COMPAT_ENABLED);
+            overrides.put(DOWNSCALE_30, COMPAT_DISABLED);
+            overrides.put(DOWNSCALE_35, COMPAT_DISABLED);
+            overrides.put(DOWNSCALE_40, COMPAT_DISABLED);
+            overrides.put(DOWNSCALE_45, COMPAT_DISABLED);
             overrides.put(DOWNSCALE_50, COMPAT_DISABLED);
+            overrides.put(DOWNSCALE_55, COMPAT_DISABLED);
             overrides.put(DOWNSCALE_60, COMPAT_DISABLED);
+            overrides.put(DOWNSCALE_65, COMPAT_DISABLED);
             overrides.put(DOWNSCALE_70, COMPAT_DISABLED);
+            overrides.put(DOWNSCALE_75, COMPAT_DISABLED);
             overrides.put(DOWNSCALE_80, COMPAT_DISABLED);
+            overrides.put(DOWNSCALE_85, COMPAT_DISABLED);
             overrides.put(DOWNSCALE_90, COMPAT_DISABLED);
             overrides.put(scaleId, COMPAT_ENABLED);
             final CompatibilityOverrideConfig changeConfig = new CompatibilityOverrideConfig(
diff --git a/services/core/java/com/android/server/app/GameManagerShellCommand.java b/services/core/java/com/android/server/app/GameManagerShellCommand.java
index 2074ffa..699f9e2 100644
--- a/services/core/java/com/android/server/app/GameManagerShellCommand.java
+++ b/services/core/java/com/android/server/app/GameManagerShellCommand.java
@@ -16,6 +16,21 @@
 
 package com.android.server.app;
 
+import static com.android.server.wm.CompatModePackages.DOWNSCALED;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_30;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_35;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_40;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_45;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_50;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_55;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_60;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_65;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_70;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_75;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_80;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_85;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_90;
+
 import android.app.ActivityManager;
 import android.app.GameManager;
 import android.app.IGameManagerService;
@@ -27,7 +42,6 @@
 
 import com.android.internal.compat.CompatibilityChangeConfig;
 import com.android.server.compat.PlatformCompat;
-import com.android.server.wm.CompatModePackages;
 
 import java.io.PrintWriter;
 import java.util.Set;
@@ -43,12 +57,21 @@
     public GameManagerShellCommand() {}
 
     private static final ArraySet<Long> DOWNSCALE_CHANGE_IDS = new ArraySet<>(new Long[]{
-            CompatModePackages.DOWNSCALED,
-            CompatModePackages.DOWNSCALE_90,
-            CompatModePackages.DOWNSCALE_80,
-            CompatModePackages.DOWNSCALE_70,
-            CompatModePackages.DOWNSCALE_60,
-            CompatModePackages.DOWNSCALE_50});
+            DOWNSCALED,
+            DOWNSCALE_90,
+            DOWNSCALE_85,
+            DOWNSCALE_80,
+            DOWNSCALE_75,
+            DOWNSCALE_70,
+            DOWNSCALE_65,
+            DOWNSCALE_60,
+            DOWNSCALE_55,
+            DOWNSCALE_50,
+            DOWNSCALE_45,
+            DOWNSCALE_40,
+            DOWNSCALE_35,
+            DOWNSCALE_30,
+    });
 
     @Override
     public int onCommand(String cmd) {
@@ -62,32 +85,9 @@
                     final String ratio = getNextArgRequired();
                     final String packageName = getNextArgRequired();
 
-                    final long changeId;
-                    switch (ratio) {
-                        case "0.5":
-                            changeId = CompatModePackages.DOWNSCALE_50;
-                            break;
-                        case "0.6":
-                            changeId = CompatModePackages.DOWNSCALE_60;
-                            break;
-                        case "0.7":
-                            changeId = CompatModePackages.DOWNSCALE_70;
-                            break;
-                        case "0.8":
-                            changeId = CompatModePackages.DOWNSCALE_80;
-                            break;
-                        case "0.9":
-                            changeId = CompatModePackages.DOWNSCALE_90;
-                            break;
-                        case "disable":
-                            changeId = 0;
-                            break;
-                        default:
-                            changeId = -1;
-                            pw.println("Invalid scaling ratio '" + ratio + "'");
-                            break;
-                    }
-                    if (changeId == -1) {
+                    final long changeId = GameManagerService.getCompatChangeId(ratio);
+                    if (changeId == 0 && !ratio.equals("disable")) {
+                        pw.println("Invalid scaling ratio '" + ratio + "'");
                         break;
                     }
 
@@ -96,10 +96,10 @@
                     if (changeId == 0) {
                         disabled = DOWNSCALE_CHANGE_IDS;
                     } else {
-                        enabled.add(CompatModePackages.DOWNSCALED);
+                        enabled.add(DOWNSCALED);
                         enabled.add(changeId);
                         disabled = DOWNSCALE_CHANGE_IDS.stream()
-                          .filter(it -> it != CompatModePackages.DOWNSCALED && it != changeId)
+                          .filter(it -> it != DOWNSCALED && it != changeId)
                           .collect(Collectors.toSet());
                     }
 
@@ -204,7 +204,7 @@
         pw.println("Game manager (game) commands:");
         pw.println("  help");
         pw.println("      Print this help text.");
-        pw.println("  downscale [0.5|0.6|0.7|0.8|0.9|disable] <PACKAGE_NAME>");
+        pw.println("  downscale [0.3|0.35|0.4|0.45|0.5|0.55|0.6|0.65|0.7|0.75|0.8|0.85|0.9|disable] <PACKAGE_NAME>");
         pw.println("      Force app to run at the specified scaling ratio.");
         pw.println("  mode [--user <USER_ID>] [1|2|3|standard|performance|battery] <PACKAGE_NAME>");
         pw.println("      Force app to run in the specified game mode, if supported.");
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index 4cb2e9b..7f1402d 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -456,7 +456,9 @@
     private void hibernatePackageGlobally(@NonNull String packageName, GlobalLevelState state) {
         Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackageGlobally");
         if (mOatArtifactDeletionEnabled) {
-            mPackageManagerInternal.deleteOatArtifactsOfPackage(packageName);
+            state.savedByte = Math.max(
+                    mPackageManagerInternal.deleteOatArtifactsOfPackage(packageName),
+                    0);
         }
         state.hibernated = true;
         Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
@@ -469,6 +471,7 @@
     private void unhibernatePackageGlobally(@NonNull String packageName, GlobalLevelState state) {
         Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackageGlobally");
         state.hibernated = false;
+        state.savedByte = 0;
         state.lastUnhibernatedMs = System.currentTimeMillis();
         Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
     }
@@ -943,6 +946,9 @@
     }
 
     private final class StatsPullAtomCallbackImpl implements StatsPullAtomCallback {
+
+        private static final int MEGABYTE_IN_BYTES = 1000000;
+
         @Override
         public int onPullAtom(int atomTag, @NonNull List<StatsEvent> data) {
             if (!isAppHibernationEnabled()
@@ -969,12 +975,21 @@
                     break;
                 case FrameworkStatsLog.GLOBAL_HIBERNATED_APPS:
                     int hibernatedAppCount = 0;
+                    long storage_saved_byte = 0;
                     synchronized (mLock) {
                         for (GlobalLevelState state : mGlobalHibernationStates.values()) {
-                            if (state.hibernated) hibernatedAppCount++;
+                            if (state.hibernated) {
+                                hibernatedAppCount++;
+                                storage_saved_byte += state.savedByte;
+                            }
                         }
                     }
-                    data.add(FrameworkStatsLog.buildStatsEvent(atomTag, hibernatedAppCount));
+                    data.add(
+                            FrameworkStatsLog.buildStatsEvent(
+                                    atomTag,
+                                    hibernatedAppCount,
+                                    storage_saved_byte / MEGABYTE_IN_BYTES)
+                    );
                     break;
                 default:
                     return StatsManager.PULL_SKIP;
diff --git a/services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java b/services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java
index 79e995b..018d602 100644
--- a/services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java
+++ b/services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java
@@ -41,6 +41,7 @@
             GlobalLevelState state = data.get(i);
             stream.write(GlobalLevelHibernationStateProto.PACKAGE_NAME, state.packageName);
             stream.write(GlobalLevelHibernationStateProto.HIBERNATED, state.hibernated);
+            stream.write(GlobalLevelHibernationStateProto.SAVED_BYTE, state.savedByte);
             stream.end(token);
         }
     }
@@ -66,6 +67,10 @@
                         state.hibernated =
                                 stream.readBoolean(GlobalLevelHibernationStateProto.HIBERNATED);
                         break;
+                    case (int) GlobalLevelHibernationStateProto.SAVED_BYTE:
+                        state.savedByte =
+                                stream.readLong(GlobalLevelHibernationStateProto.SAVED_BYTE);
+                        break;
                     default:
                         Slog.w(TAG, "Undefined field in proto: " + stream.getFieldNumber());
                 }
diff --git a/services/core/java/com/android/server/apphibernation/GlobalLevelState.java b/services/core/java/com/android/server/apphibernation/GlobalLevelState.java
index f4433a7..104ecbc 100644
--- a/services/core/java/com/android/server/apphibernation/GlobalLevelState.java
+++ b/services/core/java/com/android/server/apphibernation/GlobalLevelState.java
@@ -29,6 +29,8 @@
 
     public String packageName;
     public boolean hibernated;
+    // The number of saved bytes from the current hibernation. It will be 0 if not in hibernation.
+    public long savedByte;
     @CurrentTimeMillisLong
     public long lastUnhibernatedMs;
 
@@ -37,6 +39,7 @@
         return "GlobalLevelState{"
                 + "packageName='" + packageName + '\''
                 + ", hibernated=" + hibernated + '\''
+                + ", savedByte=" + savedByte + '\''
                 + ", lastUnhibernated=" + DATE_FORMAT.format(lastUnhibernatedMs)
                 + '}';
     }
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 99a33e4..5ba75d3 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -19,11 +19,13 @@
 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
+import static android.app.AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
 import static android.app.AppOpsManager.CALL_BACK_ON_SWITCHED_OP;
 import static android.app.AppOpsManager.FILTER_BY_ATTRIBUTION_TAG;
 import static android.app.AppOpsManager.FILTER_BY_OP_NAMES;
 import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME;
 import static android.app.AppOpsManager.FILTER_BY_UID;
+import static android.app.AppOpsManager.HISTORY_FLAG_GET_ATTRIBUTION_CHAINS;
 import static android.app.AppOpsManager.HistoricalOpsRequestFilter;
 import static android.app.AppOpsManager.KEY_BG_STATE_SETTLE_TIME;
 import static android.app.AppOpsManager.KEY_FG_SERVICE_STATE_SETTLE_TIME;
@@ -130,6 +132,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.StorageManagerInternal;
+import android.permission.PermissionManager;
 import android.provider.Settings;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -200,8 +203,8 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Scanner;
+import java.util.Set;
 import java.util.concurrent.ThreadLocalRandom;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
 
 public class AppOpsService extends IAppOpsService.Stub {
@@ -2357,10 +2360,21 @@
         final String[] opNamesArray = (opNames != null)
                 ? opNames.toArray(new String[opNames.size()]) : null;
 
+        Set<String> attributionChainExemptPackages = null;
+        if ((dataType & HISTORY_FLAG_GET_ATTRIBUTION_CHAINS) != 0) {
+            attributionChainExemptPackages =
+                    PermissionManager.getIndicatorExemptedPackages(mContext);
+        }
+
+        final String[] chainExemptPkgArray = attributionChainExemptPackages != null
+                ? attributionChainExemptPackages.toArray(
+                        new String[attributionChainExemptPackages.size()]) : null;
+
         // Must not hold the appops lock
         mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOps,
                 mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray, dataType,
-                filter, beginTimeMillis, endTimeMillis, flags, callback).recycleOnUse());
+                filter, beginTimeMillis, endTimeMillis, flags, chainExemptPkgArray,
+                callback).recycleOnUse());
     }
 
     @Override
@@ -2377,10 +2391,21 @@
         final String[] opNamesArray = (opNames != null)
                 ? opNames.toArray(new String[opNames.size()]) : null;
 
+        Set<String> attributionChainExemptPackages = null;
+        if ((dataType & HISTORY_FLAG_GET_ATTRIBUTION_CHAINS) != 0) {
+            attributionChainExemptPackages =
+                    PermissionManager.getIndicatorExemptedPackages(mContext);
+        }
+
+        final String[] chainExemptPkgArray = attributionChainExemptPackages != null
+                ? attributionChainExemptPackages.toArray(
+                new String[attributionChainExemptPackages.size()]) : null;
+
         // Must not hold the appops lock
         mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOpsFromDiskRaw,
                 mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray, dataType,
-                filter, beginTimeMillis, endTimeMillis, flags, callback).recycleOnUse());
+                filter, beginTimeMillis, endTimeMillis, flags, chainExemptPkgArray,
+                callback).recycleOnUse());
     }
 
     @Override
@@ -3838,7 +3863,8 @@
         final boolean isSelfBlame = Binder.getCallingUid() == proxiedUid;
         final boolean isProxyTrusted = mContext.checkPermission(
                 Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid)
-                == PackageManager.PERMISSION_GRANTED || isSelfBlame;
+                == PackageManager.PERMISSION_GRANTED || isSelfBlame
+                || attributionChainId != ATTRIBUTION_CHAIN_ID_NONE;
 
         String resolvedProxiedPackageName = AppOpsManager.resolvePackageName(proxiedUid,
                 proxiedPackageName);
@@ -5174,8 +5200,6 @@
     }
 
     static class Shell extends ShellCommand {
-        static final AtomicInteger sAttributionChainIds = new AtomicInteger(0);
-
         final IAppOpsService mInterface;
         final AppOpsService mInternal;
 
@@ -5645,8 +5669,7 @@
                         shell.mInterface.startOperation(shell.mToken, shell.op, shell.packageUid,
                                 shell.packageName, shell.attributionTag, true, true,
                                 "appops start shell command", true,
-                                AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR,
-                                shell.sAttributionChainIds.incrementAndGet());
+                                AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR, ATTRIBUTION_CHAIN_ID_NONE);
                     } else {
                         return -1;
                     }
diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java
index 49469cc..0439660 100644
--- a/services/core/java/com/android/server/appop/DiscreteRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java
@@ -16,6 +16,9 @@
 
 package com.android.server.appop;
 
+import static android.app.AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
+import static android.app.AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR;
+import static android.app.AppOpsManager.ATTRIBUTION_FLAG_RECEIVER;
 import static android.app.AppOpsManager.FILTER_BY_ATTRIBUTION_TAG;
 import static android.app.AppOpsManager.FILTER_BY_OP_NAMES;
 import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME;
@@ -58,7 +61,9 @@
 
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.text.SimpleDateFormat;
 import java.time.Duration;
@@ -69,6 +74,8 @@
 import java.util.Collections;
 import java.util.Date;
 import java.util.List;
+import java.util.Objects;
+import java.util.Set;
 
 /**
  * This class manages information about recent accesses to ops for permission usage timeline.
@@ -138,6 +145,7 @@
 
     private static final String TAG_HISTORY = "h";
     private static final String ATTR_VERSION = "v";
+    private static final String ATTR_LARGEST_CHAIN_ID = "lc";
     private static final int CURRENT_VERSION = 1;
 
     private static final String TAG_UID = "u";
@@ -182,6 +190,16 @@
 
     DiscreteRegistry(Object inMemoryLock) {
         mInMemoryLock = inMemoryLock;
+        synchronized (mOnDiskLock) {
+            mDiscreteAccessDir = new File(
+                    new File(Environment.getDataSystemDirectory(), "appops"),
+                    "discrete");
+            createDiscreteAccessDirLocked();
+            int largestChainId = readLargestChainIdFromDiskLocked();
+            synchronized (mInMemoryLock) {
+                mDiscreteOps = new DiscreteOps(largestChainId);
+            }
+        }
     }
 
     void systemReady() {
@@ -190,15 +208,6 @@
                     setDiscreteHistoryParameters(p);
                 });
         setDiscreteHistoryParameters(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_PRIVACY));
-        synchronized (mOnDiskLock) {
-            synchronized (mInMemoryLock) {
-                mDiscreteAccessDir = new File(
-                        new File(Environment.getDataSystemDirectory(), "appops"),
-                        "discrete");
-                createDiscreteAccessDirLocked();
-                mDiscreteOps = new DiscreteOps();
-            }
-        }
     }
 
     private void setDiscreteHistoryParameters(DeviceConfig.Properties p) {
@@ -251,7 +260,7 @@
             DiscreteOps discreteOps;
             synchronized (mInMemoryLock) {
                 discreteOps = mDiscreteOps;
-                mDiscreteOps = new DiscreteOps();
+                mDiscreteOps = new DiscreteOps(discreteOps.mChainIdOffset);
                 mCachedOps = null;
             }
             deleteOldDiscreteHistoryFilesLocked();
@@ -265,16 +274,109 @@
             long beginTimeMillis, long endTimeMillis,
             @AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
             @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
-            @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+            @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter,
+            Set<String> attributionExemptPkgs) {
+        boolean assembleChains = attributionExemptPkgs != null;
         DiscreteOps discreteOps = getAllDiscreteOps();
+        ArrayMap<Integer, AttributionChain> attributionChains = new ArrayMap<>();
+        if (assembleChains) {
+            attributionChains = createAttributionChains(discreteOps, attributionExemptPkgs);
+        }
         beginTimeMillis = max(beginTimeMillis, Instant.now().minus(sDiscreteHistoryCutoff,
                 ChronoUnit.MILLIS).toEpochMilli());
         discreteOps.filter(beginTimeMillis, endTimeMillis, filter, uidFilter, packageNameFilter,
-                opNamesFilter, attributionTagFilter, flagsFilter);
-        discreteOps.applyToHistoricalOps(result);
+                opNamesFilter, attributionTagFilter, flagsFilter, attributionChains);
+        discreteOps.applyToHistoricalOps(result, attributionChains);
         return;
     }
 
+    private int readLargestChainIdFromDiskLocked() {
+        final File[] files = mDiscreteAccessDir.listFiles();
+        if (files != null && files.length > 0) {
+            File latestFile = null;
+            long latestFileTimestamp = 0;
+            for (File f : files) {
+                final String fileName = f.getName();
+                if (!fileName.endsWith(DISCRETE_HISTORY_FILE_SUFFIX)) {
+                    continue;
+                }
+                long timestamp = Long.valueOf(fileName.substring(0,
+                        fileName.length() - DISCRETE_HISTORY_FILE_SUFFIX.length()));
+                if (latestFileTimestamp < timestamp) {
+                    latestFile = f;
+                    latestFileTimestamp = timestamp;
+                }
+            }
+            if (latestFile == null) {
+                return 0;
+            }
+            FileInputStream stream;
+            try {
+                stream = new FileInputStream(latestFile);
+            } catch (FileNotFoundException e) {
+                return 0;
+            }
+            try {
+                TypedXmlPullParser parser = Xml.resolvePullParser(stream);
+                XmlUtils.beginDocument(parser, TAG_HISTORY);
+
+                final int largestChainId = parser.getAttributeInt(null, ATTR_LARGEST_CHAIN_ID, 0);
+                return largestChainId;
+            } catch (Throwable t) {
+                return 0;
+            } finally {
+                try {
+                    stream.close();
+                } catch (IOException e) {
+                }
+            }
+        } else {
+            return 0;
+        }
+    }
+
+    private ArrayMap<Integer, AttributionChain> createAttributionChains(
+            DiscreteOps discreteOps, Set<String> attributionExemptPkgs) {
+        ArrayMap<Integer, AttributionChain> chains = new ArrayMap<>();
+        int nUids = discreteOps.mUids.size();
+        for (int uidNum = 0; uidNum < nUids; uidNum++) {
+            ArrayMap<String, DiscretePackageOps> pkgs = discreteOps.mUids.valueAt(uidNum).mPackages;
+            int uid = discreteOps.mUids.keyAt(uidNum);
+            int nPackages = pkgs.size();
+            for (int pkgNum = 0; pkgNum < nPackages; pkgNum++) {
+                ArrayMap<Integer, DiscreteOp> ops = pkgs.valueAt(pkgNum).mPackageOps;
+                String pkg = pkgs.keyAt(pkgNum);
+                int nOps = ops.size();
+                for (int opNum = 0; opNum < nOps; opNum++) {
+                    ArrayMap<String, List<DiscreteOpEvent>> attrOps =
+                            ops.valueAt(opNum).mAttributedOps;
+                    int op = ops.keyAt(opNum);
+                    int nAttrOps = attrOps.size();
+                    for (int attrOpNum = 0; attrOpNum < nAttrOps; attrOpNum++) {
+                        List<DiscreteOpEvent> opEvents = attrOps.valueAt(attrOpNum);
+                        String attributionTag = attrOps.keyAt(attrOpNum);
+                        int nOpEvents = opEvents.size();
+                        for (int opEventNum = 0; opEventNum < nOpEvents; opEventNum++) {
+                            DiscreteOpEvent event = opEvents.get(opEventNum);
+                            if (event == null
+                                    || event.mAttributionChainId == ATTRIBUTION_CHAIN_ID_NONE) {
+                                continue;
+                            }
+
+                            if (!chains.containsKey(event.mAttributionChainId)) {
+                                chains.put(event.mAttributionChainId,
+                                        new AttributionChain(attributionExemptPkgs));
+                            }
+                            chains.get(event.mAttributionChainId)
+                                    .addEvent(pkg, uid, attributionTag, op, event);
+                        }
+                    }
+                }
+            }
+        }
+        return chains;
+    }
+
     private void readDiscreteOpsFromDisk(DiscreteOps discreteOps) {
         synchronized (mOnDiskLock) {
             long beginTimeMillis = Instant.now().minus(sDiscreteHistoryCutoff,
@@ -301,7 +403,7 @@
     void clearHistory() {
         synchronized (mOnDiskLock) {
             synchronized (mInMemoryLock) {
-                mDiscreteOps = new DiscreteOps();
+                mDiscreteOps = new DiscreteOps(0);
             }
             clearOnDiskHistoryLocked();
         }
@@ -340,7 +442,11 @@
         String[] opNamesFilter = dumpOp == OP_NONE ? null
                 : new String[]{AppOpsManager.opToPublicName(dumpOp)};
         discreteOps.filter(0, Instant.now().toEpochMilli(), filter, uidFilter, packageNameFilter,
-                opNamesFilter, attributionTagFilter, OP_FLAGS_ALL);
+                opNamesFilter, attributionTagFilter, OP_FLAGS_ALL, new ArrayMap<>());
+        pw.print(prefix);
+        pw.print("Largest chain id: ");
+        pw.print(mDiscreteOps.mLargestChainId);
+        pw.println();
         discreteOps.dump(pw, sdf, date, prefix, nDiscreteOps);
     }
 
@@ -351,14 +457,14 @@
     }
 
     private DiscreteOps getAllDiscreteOps() {
-        DiscreteOps discreteOps = new DiscreteOps();
+        DiscreteOps discreteOps = new DiscreteOps(0);
 
         synchronized (mOnDiskLock) {
             synchronized (mInMemoryLock) {
                 discreteOps.merge(mDiscreteOps);
             }
             if (mCachedOps == null) {
-                mCachedOps = new DiscreteOps();
+                mCachedOps = new DiscreteOps(0);
                 readDiscreteOpsFromDisk(mCachedOps);
             }
             discreteOps.merge(mCachedOps);
@@ -366,11 +472,143 @@
         }
     }
 
+    /**
+     * Represents a chain of usages, each attributing its usage to the one before it
+     */
+    private static final class AttributionChain {
+        private static final class OpEvent {
+            String mPkgName;
+            int mUid;
+            String mAttributionTag;
+            int mOpCode;
+            DiscreteOpEvent mOpEvent;
+
+            OpEvent(String pkgName, int uid, String attributionTag, int opCode,
+                    DiscreteOpEvent event) {
+                mPkgName = pkgName;
+                mUid = uid;
+                mAttributionTag = attributionTag;
+                mOpCode = opCode;
+                mOpEvent = event;
+            }
+
+            public boolean matches(String pkgName, int uid, String attributionTag, int opCode,
+                    DiscreteOpEvent event) {
+                return Objects.equals(pkgName, mPkgName) && mUid == uid
+                        && Objects.equals(attributionTag, mAttributionTag) && mOpCode == opCode
+                        && mOpEvent.mAttributionChainId == event.mAttributionChainId
+                        && mOpEvent.mAttributionFlags == event.mAttributionFlags
+                        && mOpEvent.mNoteTime == event.mNoteTime;
+            }
+
+            public boolean packageOpEquals(OpEvent other) {
+                return Objects.equals(other.mPkgName, mPkgName) && other.mUid == mUid
+                        && Objects.equals(other.mAttributionTag, mAttributionTag)
+                        && mOpCode == other.mOpCode;
+            }
+
+            public boolean equalsExceptDuration(OpEvent other) {
+                if (other.mOpEvent.mNoteDuration == mOpEvent.mNoteDuration) {
+                    return false;
+                }
+                return packageOpEquals(other) && mOpEvent.equalsExceptDuration(other.mOpEvent);
+            }
+        }
+
+        ArrayList<OpEvent> mChain = new ArrayList<>();
+        Set<String> mExemptPkgs;
+        OpEvent mStartEvent = null;
+        OpEvent mLastVisibleEvent = null;
+
+        AttributionChain(Set<String> exemptPkgs) {
+            mExemptPkgs = exemptPkgs;
+        }
+
+        boolean isComplete() {
+            return !mChain.isEmpty() && getStart() != null && isEnd(mChain.get(mChain.size() - 1));
+        }
+
+        boolean isStart(String pkgName, int uid, String attributionTag, int op,
+                DiscreteOpEvent opEvent) {
+            if (mStartEvent == null || opEvent == null) {
+                return false;
+            }
+            return mStartEvent.matches(pkgName, uid, attributionTag, op, opEvent);
+        }
+
+        private OpEvent getStart() {
+            return mChain.isEmpty() || !isStart(mChain.get(0)) ? null : mChain.get(0);
+        }
+
+        private OpEvent getLastVisible() {
+            // Search all nodes but the first one, which is the start node
+            for (int i = mChain.size() - 1; i > 0; i--) {
+                OpEvent event = mChain.get(i);
+                if (!mExemptPkgs.contains(event.mPkgName)) {
+                    return event;
+                }
+            }
+            return null;
+        }
+
+        void addEvent(String pkgName, int uid, String attributionTag, int op,
+                DiscreteOpEvent opEvent) {
+            OpEvent event = new OpEvent(pkgName, uid, attributionTag, op, opEvent);
+
+            // check if we have a matching event, without duration, replacing duration otherwise
+            for (int i = 0; i < mChain.size(); i++) {
+                OpEvent item = mChain.get(i);
+                if (item.equalsExceptDuration(event)) {
+                    if (event.mOpEvent.mNoteDuration != -1) {
+                        item.mOpEvent = event.mOpEvent;
+                    }
+                    return;
+                }
+            }
+
+            if (mChain.isEmpty() || isEnd(event)) {
+                mChain.add(event);
+            } else if (isStart(event)) {
+                mChain.add(0, event);
+
+            } else {
+                for (int i = 0; i < mChain.size(); i++) {
+                    OpEvent currEvent = mChain.get(i);
+                    if ((!isStart(currEvent)
+                            && currEvent.mOpEvent.mNoteTime > event.mOpEvent.mNoteTime)
+                            || i == mChain.size() - 1 && isEnd(currEvent)) {
+                        mChain.add(i, event);
+                        break;
+                    } else if (i == mChain.size() - 1) {
+                        mChain.add(event);
+                        break;
+                    }
+                }
+            }
+            mStartEvent = isComplete() ? getStart() : null;
+            mLastVisibleEvent = isComplete() ? getLastVisible() : null;
+        }
+
+        private boolean isEnd(OpEvent event) {
+            return event != null
+                    && (event.mOpEvent.mAttributionFlags & ATTRIBUTION_FLAG_ACCESSOR) != 0;
+        }
+
+        private boolean isStart(OpEvent event) {
+            return event != null
+                    && (event.mOpEvent.mAttributionFlags & ATTRIBUTION_FLAG_RECEIVER) != 0;
+        }
+    }
+
     private final class DiscreteOps {
         ArrayMap<Integer, DiscreteUidOps> mUids;
+        int mChainIdOffset;
+        int mLargestChainId;
 
-        DiscreteOps() {
+        DiscreteOps(int chainIdOffset) {
             mUids = new ArrayMap<>();
+            mChainIdOffset = chainIdOffset;
+            mLargestChainId = chainIdOffset;
         }
 
         boolean isEmpty() {
@@ -378,6 +616,7 @@
         }
 
         void merge(DiscreteOps other) {
+            mLargestChainId = max(mLargestChainId, other.mLargestChainId);
             int nUids = other.mUids.size();
             for (int i = 0; i < nUids; i++) {
                 int uid = other.mUids.keyAt(i);
@@ -390,14 +629,27 @@
                 @Nullable String attributionTag, @AppOpsManager.OpFlags int flags,
                 @AppOpsManager.UidState int uidState, long accessTime, long accessDuration,
                 @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
+            int offsetChainId = attributionChainId;
+            if (attributionChainId != ATTRIBUTION_CHAIN_ID_NONE) {
+                offsetChainId = attributionChainId + mChainIdOffset;
+                if (offsetChainId > mLargestChainId) {
+                    mLargestChainId = offsetChainId;
+                } else if (offsetChainId < 0) {
+                    // handle overflow
+                    offsetChainId = 0;
+                    mLargestChainId = 0;
+                    mChainIdOffset = -1 * attributionChainId;
+                }
+            }
             getOrCreateDiscreteUidOps(uid).addDiscreteAccess(op, packageName, attributionTag, flags,
-                    uidState, accessTime, accessDuration, attributionFlags, attributionChainId);
+                    uidState, accessTime, accessDuration, attributionFlags, offsetChainId);
         }
 
         private void filter(long beginTimeMillis, long endTimeMillis,
                 @AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
                 @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
-                @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+                @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter,
+                ArrayMap<Integer, AttributionChain> attributionChains) {
             if ((filter & FILTER_BY_UID) != 0) {
                 ArrayMap<Integer, DiscreteUidOps> uids = new ArrayMap<>();
                 uids.put(uidFilter, getOrCreateDiscreteUidOps(uidFilter));
@@ -406,7 +658,8 @@
             int nUids = mUids.size();
             for (int i = nUids - 1; i >= 0; i--) {
                 mUids.valueAt(i).filter(beginTimeMillis, endTimeMillis, filter, packageNameFilter,
-                        opNamesFilter, attributionTagFilter, flagsFilter);
+                        opNamesFilter, attributionTagFilter, flagsFilter, mUids.keyAt(i),
+                        attributionChains);
                 if (mUids.valueAt(i).isEmpty()) {
                     mUids.removeAt(i);
                 }
@@ -429,10 +682,11 @@
             }
         }
 
-        private void applyToHistoricalOps(AppOpsManager.HistoricalOps result) {
+        private void applyToHistoricalOps(AppOpsManager.HistoricalOps result,
+                ArrayMap<Integer, AttributionChain> attributionChains) {
             int nUids = mUids.size();
             for (int i = 0; i < nUids; i++) {
-                mUids.valueAt(i).applyToHistory(result, mUids.keyAt(i));
+                mUids.valueAt(i).applyToHistory(result, mUids.keyAt(i), attributionChains);
             }
         }
 
@@ -442,6 +696,7 @@
             out.startDocument(null, true);
             out.startTag(null, TAG_HISTORY);
             out.attributeInt(null, ATTR_VERSION, CURRENT_VERSION);
+            out.attributeInt(null, ATTR_LARGEST_CHAIN_ID, mLargestChainId);
 
             int nUids = mUids.size();
             for (int i = 0; i < nUids; i++) {
@@ -476,8 +731,13 @@
         }
 
         private void readFromFile(File f, long beginTimeMillis) {
+            FileInputStream stream;
             try {
-                FileInputStream stream = new FileInputStream(f);
+                stream = new FileInputStream(f);
+            } catch (FileNotFoundException e) {
+                return;
+            }
+            try {
                 TypedXmlPullParser parser = Xml.resolvePullParser(stream);
                 XmlUtils.beginDocument(parser, TAG_HISTORY);
 
@@ -487,7 +747,6 @@
                 if (version != CURRENT_VERSION) {
                     throw new IllegalStateException("Dropping unsupported discrete history " + f);
                 }
-
                 int depth = parser.getDepth();
                 while (XmlUtils.nextElementWithin(parser, depth)) {
                     if (TAG_UID.equals(parser.getName())) {
@@ -498,8 +757,12 @@
             } catch (Throwable t) {
                 Slog.e(TAG, "Failed to read file " + f.getName() + " " + t.getMessage() + " "
                         + Arrays.toString(t.getStackTrace()));
+            } finally {
+                try {
+                    stream.close();
+                } catch (IOException e) {
+                }
             }
-
         }
     }
 
@@ -590,7 +853,8 @@
         private void filter(long beginTimeMillis, long endTimeMillis,
                 @AppOpsManager.HistoricalOpsRequestFilter int filter,
                 @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
-                @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+                @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter,
+                int currentUid, ArrayMap<Integer, AttributionChain> attributionChains) {
             if ((filter & FILTER_BY_PACKAGE_NAME) != 0) {
                 ArrayMap<String, DiscretePackageOps> packages = new ArrayMap<>();
                 packages.put(packageNameFilter, getOrCreateDiscretePackageOps(packageNameFilter));
@@ -599,7 +863,8 @@
             int nPackages = mPackages.size();
             for (int i = nPackages - 1; i >= 0; i--) {
                 mPackages.valueAt(i).filter(beginTimeMillis, endTimeMillis, filter, opNamesFilter,
-                        attributionTagFilter, flagsFilter);
+                        attributionTagFilter, flagsFilter, currentUid, mPackages.keyAt(i),
+                        attributionChains);
                 if (mPackages.valueAt(i).isEmpty()) {
                     mPackages.removeAt(i);
                 }
@@ -634,10 +899,12 @@
             return result;
         }
 
-        private void applyToHistory(AppOpsManager.HistoricalOps result, int uid) {
+        private void applyToHistory(AppOpsManager.HistoricalOps result, int uid,
+                @NonNull ArrayMap<Integer, AttributionChain> attributionChains) {
             int nPackages = mPackages.size();
             for (int i = 0; i < nPackages; i++) {
-                mPackages.valueAt(i).applyToHistory(result, uid, mPackages.keyAt(i));
+                mPackages.valueAt(i).applyToHistory(result, uid, mPackages.keyAt(i),
+                        attributionChains);
             }
         }
 
@@ -705,7 +972,8 @@
         private void filter(long beginTimeMillis, long endTimeMillis,
                 @AppOpsManager.HistoricalOpsRequestFilter int filter,
                 @Nullable String[] opNamesFilter, @Nullable String attributionTagFilter,
-                @AppOpsManager.OpFlags int flagsFilter) {
+                @AppOpsManager.OpFlags int flagsFilter, int currentUid, String currentPkgName,
+                ArrayMap<Integer, AttributionChain> attributionChains) {
             int nOps = mPackageOps.size();
             for (int i = nOps - 1; i >= 0; i--) {
                 int opId = mPackageOps.keyAt(i);
@@ -715,7 +983,8 @@
                     continue;
                 }
                 mPackageOps.valueAt(i).filter(beginTimeMillis, endTimeMillis, filter,
-                        attributionTagFilter, flagsFilter);
+                        attributionTagFilter, flagsFilter, currentUid, currentPkgName,
+                        mPackageOps.keyAt(i), attributionChains);
                 if (mPackageOps.valueAt(i).isEmpty()) {
                     mPackageOps.removeAt(i);
                 }
@@ -739,11 +1008,12 @@
         }
 
         private void applyToHistory(AppOpsManager.HistoricalOps result, int uid,
-                @NonNull String packageName) {
+                @NonNull String packageName,
+                @NonNull ArrayMap<Integer, AttributionChain> attributionChains) {
             int nPackageOps = mPackageOps.size();
             for (int i = 0; i < nPackageOps; i++) {
                 mPackageOps.valueAt(i).applyToHistory(result, uid, packageName,
-                        mPackageOps.keyAt(i));
+                        mPackageOps.keyAt(i), attributionChains);
             }
         }
 
@@ -802,7 +1072,9 @@
 
         private void filter(long beginTimeMillis, long endTimeMillis,
                 @AppOpsManager.HistoricalOpsRequestFilter int filter,
-                @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+                @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter,
+                int currentUid, String currentPkgName, int currentOp,
+                ArrayMap<Integer, AttributionChain> attributionChains) {
             if ((filter & FILTER_BY_ATTRIBUTION_TAG) != 0) {
                 ArrayMap<String, List<DiscreteOpEvent>> attributedOps = new ArrayMap<>();
                 attributedOps.put(attributionTagFilter,
@@ -814,7 +1086,9 @@
             for (int i = nTags - 1; i >= 0; i--) {
                 String tag = mAttributedOps.keyAt(i);
                 List<DiscreteOpEvent> list = mAttributedOps.valueAt(i);
-                list = filterEventsList(list, beginTimeMillis, endTimeMillis, flagsFilter);
+                list = filterEventsList(list, beginTimeMillis, endTimeMillis, flagsFilter,
+                        currentUid, currentPkgName, currentOp, mAttributedOps.keyAt(i),
+                        attributionChains);
                 mAttributedOps.put(tag, list);
                 if (list.size() == 0) {
                     mAttributedOps.removeAt(i);
@@ -876,7 +1150,8 @@
         }
 
         private void applyToHistory(AppOpsManager.HistoricalOps result, int uid,
-                @NonNull String packageName, int op) {
+                @NonNull String packageName, int op,
+                @NonNull ArrayMap<Integer, AttributionChain> attributionChains) {
             int nOps = mAttributedOps.size();
             for (int i = 0; i < nOps; i++) {
                 String tag = mAttributedOps.keyAt(i);
@@ -884,9 +1159,21 @@
                 int nEvents = events.size();
                 for (int j = 0; j < nEvents; j++) {
                     DiscreteOpEvent event = events.get(j);
+                    AppOpsManager.OpEventProxyInfo proxy = null;
+                    if (event.mAttributionChainId != ATTRIBUTION_CHAIN_ID_NONE
+                            && attributionChains != null) {
+                        AttributionChain chain = attributionChains.get(event.mAttributionChainId);
+                        if (chain != null && chain.isComplete()
+                                && chain.isStart(packageName, uid, tag, op, event)
+                                && chain.mLastVisibleEvent != null) {
+                            AttributionChain.OpEvent proxyEvent = chain.mLastVisibleEvent;
+                            proxy = new AppOpsManager.OpEventProxyInfo(proxyEvent.mUid,
+                                    proxyEvent.mPkgName, proxyEvent.mAttributionTag);
+                        }
+                    }
                     result.addDiscreteAccess(op, uid, packageName, tag, event.mUidState,
                             event.mOpFlag, discretizeTimeStamp(event.mNoteTime),
-                            discretizeDuration(event.mNoteDuration));
+                            discretizeDuration(event.mNoteDuration), proxy);
                 }
             }
         }
@@ -981,6 +1268,13 @@
             mAttributionChainId = attributionChainId;
         }
 
+        public boolean equalsExceptDuration(DiscreteOpEvent o) {
+            return mNoteTime == o.mNoteTime && mUidState == o.mUidState && mOpFlag == o.mOpFlag
+                    && mAttributionFlags == o.mAttributionFlags
+                    && mAttributionChainId == o.mAttributionChainId;
+
+        }
+
         private void dump(@NonNull PrintWriter pw, @NonNull SimpleDateFormat sdf,
                 @NonNull Date date, @NonNull String prefix) {
             pw.print(prefix);
@@ -1063,11 +1357,20 @@
     }
 
     private static List<DiscreteOpEvent> filterEventsList(List<DiscreteOpEvent> list,
-            long beginTimeMillis, long endTimeMillis, @AppOpsManager.OpFlags int flagsFilter) {
+            long beginTimeMillis, long endTimeMillis, @AppOpsManager.OpFlags int flagsFilter,
+            int currentUid, String currentPackageName, int currentOp, String currentAttrTag,
+            ArrayMap<Integer, AttributionChain> attributionChains) {
         int n = list.size();
         List<DiscreteOpEvent> result = new ArrayList<>(n);
         for (int i = 0; i < n; i++) {
             DiscreteOpEvent event = list.get(i);
+            AttributionChain chain = attributionChains.get(event.mAttributionChainId);
+            // If we have an attribution chain, and this event isn't the beginning node, remove it
+            if (chain != null && !chain.isStart(currentPackageName, currentUid, currentAttrTag,
+                    currentOp, event) && chain.isComplete()
+                    && event.mAttributionChainId != ATTRIBUTION_CHAIN_ID_NONE) {
+                continue;
+            }
             if ((event.mOpFlag & flagsFilter) != 0
                     && event.mNoteTime + event.mNoteDuration > beginTimeMillis
                     && event.mNoteTime < endTimeMillis) {
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index dd5df50..2c68aaf 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -209,6 +209,7 @@
         mMode = other.mMode;
         mBaseSnapshotInterval = other.mBaseSnapshotInterval;
         mIntervalCompressionMultiplier = other.mIntervalCompressionMultiplier;
+        mDiscreteRegistry = other.mDiscreteRegistry;
     }
 
     void systemReady(@NonNull ContentResolver resolver) {
@@ -369,7 +370,7 @@
             @Nullable String attributionTag, @Nullable String[] opNames,
             @OpHistoryFlags int historyFlags, @HistoricalOpsRequestFilter int filter,
             long beginTimeMillis, long endTimeMillis, @OpFlags int flags,
-            @NonNull RemoteCallback callback) {
+            String[] attributionExemptedPackages, @NonNull RemoteCallback callback) {
         if (!isApiEnabled()) {
             callback.sendResult(new Bundle());
             return;
@@ -395,7 +396,7 @@
         if ((historyFlags & HISTORY_FLAG_DISCRETE) != 0) {
             mDiscreteRegistry.addFilteredDiscreteOpsToHistoricalOps(result, beginTimeMillis,
                     endTimeMillis, filter, uid, packageName, opNames, attributionTag,
-                    flags);
+                    flags, new ArraySet<>(attributionExemptedPackages));
         }
 
         final Bundle payload = new Bundle();
@@ -406,7 +407,8 @@
     void getHistoricalOps(int uid, @Nullable String packageName, @Nullable String attributionTag,
             @Nullable String[] opNames, @OpHistoryFlags int historyFlags,
             @HistoricalOpsRequestFilter int filter, long beginTimeMillis, long endTimeMillis,
-            @OpFlags int flags, @NonNull RemoteCallback callback) {
+            @OpFlags int flags, @Nullable String[] attributionExemptPkgs,
+            @NonNull RemoteCallback callback) {
         if (!isApiEnabled()) {
             callback.sendResult(new Bundle());
             return;
@@ -428,7 +430,8 @@
 
         if ((historyFlags & HISTORY_FLAG_DISCRETE) != 0) {
             mDiscreteRegistry.addFilteredDiscreteOpsToHistoricalOps(result, beginTimeMillis,
-                    endTimeMillis, filter, uid, packageName, opNames, attributionTag, flags);
+                    endTimeMillis, filter, uid, packageName, opNames, attributionTag, flags,
+                    new ArraySet<>(attributionExemptPkgs));
         }
 
         if ((historyFlags & HISTORY_FLAG_AGGREGATE) != 0) {
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 a5bb0f4..0981f18 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
@@ -866,7 +866,7 @@
     private void scheduleUpdateActiveUserWithoutHandler(int targetUserId) {
         final boolean hasEnrolled = !getEnrolledFaces(mSensorId, targetUserId).isEmpty();
         final FaceUpdateActiveUserClient client = new FaceUpdateActiveUserClient(mContext,
-                mLazyDaemon, targetUserId, mContext.getOpPackageName(), mSensorId, mCurrentUserId,
+                mLazyDaemon, targetUserId, mContext.getOpPackageName(), mSensorId,
                 hasEnrolled, mAuthenticatorIds);
         mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
             @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
index 70e2033..5343d0d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
@@ -34,38 +34,23 @@
     private static final String TAG = "FaceUpdateActiveUserClient";
     private static final String FACE_DATA_DIR = "facedata";
 
-    private final int mCurrentUserId;
     private final boolean mHasEnrolledBiometrics;
     @NonNull private final Map<Integer, Long> mAuthenticatorIds;
 
     FaceUpdateActiveUserClient(@NonNull Context context,
-            @NonNull LazyDaemon<IBiometricsFace> lazyDaemon,  int userId, @NonNull String owner,
-            int sensorId, int currentUserId, boolean hasEnrolledBIometrics,
+            @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, int userId, @NonNull String owner,
+            int sensorId, boolean hasEnrolledBiometrics,
             @NonNull Map<Integer, Long> authenticatorIds) {
         super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
                 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
                 BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
-        mCurrentUserId = currentUserId;
-        mHasEnrolledBiometrics = hasEnrolledBIometrics;
+        mHasEnrolledBiometrics = hasEnrolledBiometrics;
         mAuthenticatorIds = authenticatorIds;
     }
 
     @Override
     public void start(@NonNull Callback callback) {
         super.start(callback);
-
-        if (mCurrentUserId == getTargetUserId()) {
-            Slog.d(TAG, "Already user: " + mCurrentUserId + ", refreshing authenticatorId");
-            try {
-                mAuthenticatorIds.put(getTargetUserId(), mHasEnrolledBiometrics
-                        ? getFreshDaemon().getAuthenticatorId().value : 0L);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Unable to refresh authenticatorId", e);
-            }
-            callback.onClientFinished(this, true /* success */);
-            return;
-        }
-
         startHalOperation();
     }
 
@@ -85,7 +70,10 @@
         }
 
         try {
-            getFreshDaemon().setActiveUser(getTargetUserId(), storePath.getAbsolutePath());
+            final IBiometricsFace daemon = getFreshDaemon();
+            daemon.setActiveUser(getTargetUserId(), storePath.getAbsolutePath());
+            mAuthenticatorIds.put(getTargetUserId(),
+                    mHasEnrolledBiometrics ? daemon.getAuthenticatorId().value : 0L);
             mCallback.onClientFinished(this, true /* success */);
         } catch (RemoteException e) {
             Slog.e(TAG, "Failed to setActiveUser: " + e);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlConversionUtils.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlConversionUtils.java
index 66142bf..0ae2e38 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlConversionUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlConversionUtils.java
@@ -46,6 +46,8 @@
             return BiometricFingerprintConstants.FINGERPRINT_ERROR_UNABLE_TO_REMOVE;
         } else if (aidlError == Error.VENDOR) {
             return BiometricFingerprintConstants.FINGERPRINT_ERROR_VENDOR;
+        } else if (aidlError == Error.BAD_CALIBRATION) {
+            return BiometricFingerprintConstants.FINGERPRINT_ERROR_BAD_CALIBARTION;
         } else {
             return BiometricFingerprintConstants.FINGERPRINT_ERROR_UNKNOWN;
         }
@@ -78,8 +80,7 @@
             // No framework constant available
             return BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_UNKNOWN;
         } else if (aidlAcquiredInfo == AcquiredInfo.IMMOBILE) {
-            // No framework constant available
-            return BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_UNKNOWN;
+            return BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_IMMOBILE;
         } else if (aidlAcquiredInfo == AcquiredInfo.RETRYING_CAPTURE) {
             // No framework constant available
             return BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_UNKNOWN;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
index 1607364..8d777e1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
@@ -124,10 +124,12 @@
         final PerformanceTracker pm = PerformanceTracker.getInstanceForSensorId(getSensorId());
         pm.incrementAuthForUser(getTargetUserId(), authenticated);
 
-        try {
-            getListener().onDetected(getSensorId(), getTargetUserId(), mIsStrongBiometric);
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Remote exception when sending onDetected", e);
+        if (getListener() != null) {
+            try {
+                getListener().onDetected(getSensorId(), getTargetUserId(), mIsStrongBiometric);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Remote exception when sending onDetected", e);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 184ddae..88f88a8 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -19,6 +19,8 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.content.res.Resources;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
 import android.os.Environment;
 import android.os.PowerManager;
 import android.text.TextUtils;
@@ -86,6 +88,9 @@
     // The details of the proximity sensor associated with this display.
     private final SensorData mProximitySensor = new SensorData();
 
+    private final List<RefreshRateLimitation> mRefreshRateLimitations =
+            new ArrayList<>(2 /*initialCapacity*/);
+
     // Nits and backlight values that are loaded from either the display device config file, or
     // config.xml. These are the raw values and just used for the dumpsys
     private float[] mRawNits;
@@ -306,6 +311,10 @@
         return hbmData;
     }
 
+    public List<RefreshRateLimitation> getRefreshRateLimitations() {
+        return mRefreshRateLimitations;
+    }
+
     @Override
     public String toString() {
         String str = "DisplayDeviceConfig{"
@@ -329,6 +338,7 @@
                 + ", mBrightnessRampSlowIncrease=" + mBrightnessRampSlowIncrease
                 + ", mAmbientLightSensor=" + mAmbientLightSensor
                 + ", mProximitySensor=" + mProximitySensor
+                + ", mRefreshRateLimitations= " + Arrays.toString(mRefreshRateLimitations.toArray())
                 + "}";
         return str;
     }
@@ -647,6 +657,13 @@
             mHbmData.timeWindowMillis = hbmTiming.getTimeWindowSecs_all().longValue() * 1000;
             mHbmData.timeMaxMillis = hbmTiming.getTimeMaxSecs_all().longValue() * 1000;
             mHbmData.timeMinMillis = hbmTiming.getTimeMinSecs_all().longValue() * 1000;
+            final RefreshRateRange rr = hbm.getRefreshRate_all();
+            if (rr != null) {
+                final float min = rr.getMinimum().floatValue();
+                final float max = rr.getMaximum().floatValue();
+                mRefreshRateLimitations.add(new RefreshRateLimitation(
+                        DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE, min, max));
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index bff39a9..182a038 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -63,6 +63,7 @@
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.display.DisplayManagerInternal.DisplayGroupListener;
 import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener;
+import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
 import android.hardware.display.DisplayManagerInternal.RefreshRateRange;
 import android.hardware.display.DisplayViewport;
 import android.hardware.display.DisplayedContentSample;
@@ -130,6 +131,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Optional;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.atomic.AtomicLong;
@@ -2111,6 +2113,11 @@
                 DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED);
     }
 
+    private DisplayDevice getDeviceForDisplayLocked(int displayId) {
+        final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
+        return display == null ? null : display.getPrimaryDisplayDeviceLocked();
+    }
+
     private final class DisplayManagerHandler extends Handler {
         public DisplayManagerHandler(Looper looper) {
             super(looper, null, true /*async*/);
@@ -3295,6 +3302,19 @@
             }
             return null;
         }
+
+        @Override
+        public List<RefreshRateLimitation> getRefreshRateLimitations(int displayId) {
+            final DisplayDeviceConfig config;
+            synchronized (mSyncRoot) {
+                final DisplayDevice device = getDeviceForDisplayLocked(displayId);
+                if (device == null) {
+                    return null;
+                }
+                config = device.getDisplayDeviceConfig();
+            }
+            return config.getRefreshRateLimitations();
+        }
     }
 
     class DesiredDisplayModeSpecsObserver
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 2364a3c..83fc966 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -16,6 +16,8 @@
 
 package com.android.server.display;
 
+import static android.hardware.display.DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ContentResolver;
@@ -26,8 +28,10 @@
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
+import android.hardware.display.BrightnessInfo;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
 import android.hardware.display.DisplayManagerInternal.RefreshRateRange;
 import android.hardware.fingerprint.IUdfpsHbmListener;
 import android.net.Uri;
@@ -102,6 +106,7 @@
     private final DisplayObserver mDisplayObserver;
     private final UdfpsObserver mUdfpsObserver;
     private final SensorObserver mSensorObserver;
+    private final HbmObserver mHbmObserver;
     private final DeviceConfigInterface mDeviceConfig;
     private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
 
@@ -127,7 +132,7 @@
     private int mModeSwitchingType = DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS;
 
     public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) {
-        this(context, handler, new RealInjector());
+        this(context, handler, new RealInjector(context));
     }
 
     public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler,
@@ -143,11 +148,13 @@
         mDisplayObserver = new DisplayObserver(context, handler);
         mBrightnessObserver = new BrightnessObserver(context, handler);
         mUdfpsObserver = new UdfpsObserver();
-        mSensorObserver = new SensorObserver(context, (displayId, priority, vote) -> {
+        final BallotBox ballotBox = (displayId, priority, vote) -> {
             synchronized (mLock) {
                 updateVoteLocked(displayId, priority, vote);
             }
-        });
+        };
+        mSensorObserver = new SensorObserver(context, ballotBox);
+        mHbmObserver = new HbmObserver(injector, ballotBox, BackgroundThread.getHandler());
         mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
         mDeviceConfig = injector.getDeviceConfig();
         mAlwaysRespectAppRequest = false;
@@ -165,6 +172,7 @@
         mDisplayObserver.observe();
         mBrightnessObserver.observe(sensorManager);
         mSensorObserver.observe();
+        mHbmObserver.observe();
         synchronized (mLock) {
             // We may have a listener already registered before the call to start, so go ahead and
             // notify them to pick up our newly initialized state.
@@ -596,6 +604,7 @@
             mBrightnessObserver.dumpLocked(pw);
             mUdfpsObserver.dumpLocked(pw);
             mSensorObserver.dumpLocked(pw);
+            mHbmObserver.dumpLocked(pw);
         }
     }
 
@@ -938,13 +947,16 @@
         // user seeing the display flickering when the switches occur.
         public static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 8;
 
+        // High-brightness-mode may need a specific range of refresh-rates to function properly.
+        public static final int PRIORITY_HIGH_BRIGHTNESS_MODE = 9;
+
         // The proximity sensor needs the refresh rate to be locked in order to function, so this is
         // set to a high priority.
-        public static final int PRIORITY_PROXIMITY = 9;
+        public static final int PRIORITY_PROXIMITY = 10;
 
         // The Under-Display Fingerprint Sensor (UDFPS) needs the refresh rate to be locked in order
         // to function, so this needs to be the highest priority of all votes.
-        public static final int PRIORITY_UDFPS = 10;
+        public static final int PRIORITY_UDFPS = 11;
 
         // Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and
         // APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString.
@@ -1021,29 +1033,30 @@
 
         public static String priorityToString(int priority) {
             switch (priority) {
+                case PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE:
+                    return "PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE";
+                case PRIORITY_APP_REQUEST_MAX_REFRESH_RATE:
+                    return "PRIORITY_APP_REQUEST_MAX_REFRESH_RATE";
+                case PRIORITY_APP_REQUEST_SIZE:
+                    return "PRIORITY_APP_REQUEST_SIZE";
                 case PRIORITY_DEFAULT_REFRESH_RATE:
                     return "PRIORITY_DEFAULT_REFRESH_RATE";
                 case PRIORITY_FLICKER_REFRESH_RATE:
                     return "PRIORITY_FLICKER_REFRESH_RATE";
                 case PRIORITY_FLICKER_REFRESH_RATE_SWITCH:
                     return "PRIORITY_FLICKER_REFRESH_RATE_SWITCH";
-                case PRIORITY_USER_SETTING_MIN_REFRESH_RATE:
-                    return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE";
-                case PRIORITY_APP_REQUEST_MAX_REFRESH_RATE:
-                    return "PRIORITY_APP_REQUEST_MAX_REFRESH_RATE";
-                case PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE:
-                    return "PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE";
-                case PRIORITY_APP_REQUEST_SIZE:
-                    return "PRIORITY_APP_REQUEST_SIZE";
-                case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE:
-                    return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE";
+                case PRIORITY_HIGH_BRIGHTNESS_MODE:
+                    return "PRIORITY_HIGH_BRIGHTNESS_MODE";
+                case PRIORITY_PROXIMITY:
+                    return "PRIORITY_PROXIMITY";
                 case PRIORITY_LOW_POWER_MODE:
                     return "PRIORITY_LOW_POWER_MODE";
                 case PRIORITY_UDFPS:
                     return "PRIORITY_UDFPS";
-                case PRIORITY_PROXIMITY:
-                    return "PRIORITY_PROXIMITY";
-
+                case PRIORITY_USER_SETTING_MIN_REFRESH_RATE:
+                    return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE";
+                case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE:
+                    return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE";
                 default:
                     return Integer.toString(priority);
             }
@@ -2155,6 +2168,75 @@
         }
     }
 
+    /**
+     * Listens to DisplayManager for HBM status and applies any refresh-rate restrictions for
+     * HBM that are associated with that display. Restrictions are retrieved from
+     * DisplayManagerInternal but originate in the display-device-config file.
+     */
+    private static class HbmObserver implements DisplayManager.DisplayListener {
+        private final BallotBox mBallotBox;
+        private final Handler mHandler;
+        private final SparseBooleanArray mHbmEnabled = new SparseBooleanArray();
+        private final Injector mInjector;
+
+        private DisplayManagerInternal mDisplayManagerInternal;
+
+        HbmObserver(Injector injector, BallotBox ballotBox, Handler handler) {
+            mInjector = injector;
+            mBallotBox = ballotBox;
+            mHandler = handler;
+        }
+
+        public void observe() {
+            mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
+            mInjector.registerDisplayListener(this, mHandler,
+                    DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
+                    | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
+        }
+
+        @Override
+        public void onDisplayAdded(int displayId) {}
+
+        @Override
+        public void onDisplayRemoved(int displayId) {
+            mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, null);
+        }
+
+        @Override
+        public void onDisplayChanged(int displayId) {
+            final BrightnessInfo info = mInjector.getBrightnessInfo(displayId);
+            if (info == null) {
+                // Display no longer there. Assume we'll get an onDisplayRemoved very soon.
+                return;
+            }
+            final boolean isHbmEnabled =
+                    info.highBrightnessMode != BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
+            if (isHbmEnabled == mHbmEnabled.get(displayId)) {
+                // no change, ignore.
+                return;
+            }
+            Vote vote = null;
+            mHbmEnabled.put(displayId, isHbmEnabled);
+            if (isHbmEnabled) {
+                final List<RefreshRateLimitation> limits =
+                        mDisplayManagerInternal.getRefreshRateLimitations(displayId);
+                for (int i = 0; limits != null && i < limits.size(); i++) {
+                    final RefreshRateLimitation limitation = limits.get(i);
+                    if (limitation.type == REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE) {
+                        vote = Vote.forRefreshRates(limitation.range.min, limitation.range.max);
+                        break;
+                    }
+                }
+            }
+            mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, vote);
+        }
+
+        void dumpLocked(PrintWriter pw) {
+            pw.println("   HbmObserver");
+            pw.println("     mHbmEnabled: " + mHbmEnabled);
+        }
+    }
+
     private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener {
         public DeviceConfigDisplaySettings() {
         }
@@ -2309,10 +2391,21 @@
 
         void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
                 @NonNull ContentObserver observer);
+
+        void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener,
+                Handler handler, long flags);
+
+        BrightnessInfo getBrightnessInfo(int displayId);
     }
 
     @VisibleForTesting
     static class RealInjector implements Injector {
+        private final Context mContext;
+        private DisplayManager mDisplayManager;
+
+        RealInjector(Context context) {
+            mContext = context;
+        }
 
         @Override
         @NonNull
@@ -2339,6 +2432,28 @@
             cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/,
                     observer, UserHandle.USER_SYSTEM);
         }
+
+        @Override
+        public void registerDisplayListener(DisplayManager.DisplayListener listener,
+                Handler handler, long flags) {
+            getDisplayManager().registerDisplayListener(listener, handler, flags);
+        }
+
+        @Override
+        public BrightnessInfo getBrightnessInfo(int displayId) {
+            final Display display = getDisplayManager().getDisplay(displayId);
+            if (display != null) {
+                return display.getBrightnessInfo();
+            }
+            return null;
+        }
+
+        private DisplayManager getDisplayManager() {
+            if (mDisplayManager == null) {
+                mDisplayManager = mContext.getSystemService(DisplayManager.class);
+            }
+            return mDisplayManager;
+        }
     }
 
     interface BallotBox {
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index aeb1893..e6210b2 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -45,6 +45,7 @@
 import android.app.compat.CompatChanges;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.location.Criteria;
 import android.location.GeocoderParams;
 import android.location.Geofence;
@@ -91,6 +92,7 @@
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.Preconditions;
 import com.android.server.FgThread;
@@ -134,6 +136,8 @@
 import com.android.server.location.provider.PassiveLocationProviderManager;
 import com.android.server.location.provider.StationaryThrottlingLocationProvider;
 import com.android.server.location.provider.proxy.ProxyLocationProvider;
+import com.android.server.location.settings.LocationSettings;
+import com.android.server.location.settings.LocationUserSettings;
 import com.android.server.pm.permission.LegacyPermissionManagerInternal;
 
 import java.io.FileDescriptor;
@@ -270,6 +274,8 @@
 
         mGeofenceManager = new GeofenceManager(mContext, injector);
 
+        mInjector.getLocationSettings().registerLocationUserSettingsListener(
+                this::onLocationUserSettingsChanged);
         mInjector.getSettingsHelper().addOnLocationEnabledChangedListener(
                 this::onLocationModeChanged);
         mInjector.getSettingsHelper().addIgnoreSettingsAllowlistChangedListener(
@@ -476,6 +482,25 @@
         }
     }
 
+    private void onLocationUserSettingsChanged(int userId, LocationUserSettings oldSettings,
+            LocationUserSettings newSettings) {
+        if (oldSettings.isAdasGnssLocationEnabled() != newSettings.isAdasGnssLocationEnabled()) {
+            boolean enabled = newSettings.isAdasGnssLocationEnabled();
+
+            if (D) {
+                Log.d(TAG, "[u" + userId + "] adas gnss location enabled = " + enabled);
+            }
+
+            EVENT_LOG.logAdasLocationEnabled(userId, enabled);
+
+            Intent intent = new Intent(LocationManager.ACTION_ADAS_GNSS_ENABLED_CHANGED)
+                    .putExtra(LocationManager.EXTRA_ADAS_GNSS_ENABLED, enabled)
+                    .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
+                    .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+            mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
+        }
+    }
+
     private void onLocationModeChanged(int userId) {
         boolean enabled = mInjector.getSettingsHelper().isLocationEnabled(userId);
         LocationManager.invalidateLocalLocationEnabledCaches();
@@ -661,7 +686,7 @@
         // clients in the system process must have an attribution tag set
         Preconditions.checkState(identity.getPid() != Process.myPid() || attributionTag != null);
 
-        request = validateLocationRequest(request, identity);
+        request = validateLocationRequest(provider, request, identity);
 
         LocationProviderManager manager = getLocationProviderManager(provider);
         Preconditions.checkArgument(manager != null,
@@ -687,7 +712,7 @@
                     new IllegalArgumentException());
         }
 
-        request = validateLocationRequest(request, identity);
+        request = validateLocationRequest(provider, request, identity);
 
         LocationProviderManager manager = getLocationProviderManager(provider);
         Preconditions.checkArgument(manager != null,
@@ -725,7 +750,7 @@
             }
         }
 
-        request = validateLocationRequest(request, identity);
+        request = validateLocationRequest(provider, request, identity);
 
         LocationProviderManager manager = getLocationProviderManager(provider);
         Preconditions.checkArgument(manager != null,
@@ -734,33 +759,27 @@
         manager.registerLocationRequest(request, identity, permissionLevel, pendingIntent);
     }
 
-    private LocationRequest validateLocationRequest(LocationRequest request,
+    private LocationRequest validateLocationRequest(String provider, LocationRequest request,
             CallerIdentity identity) {
+        // validate unsanitized request
         if (!request.getWorkSource().isEmpty()) {
             mContext.enforceCallingOrSelfPermission(
                     permission.UPDATE_DEVICE_STATS,
                     "setting a work source requires " + permission.UPDATE_DEVICE_STATS);
         }
-        if (request.isHiddenFromAppOps()) {
-            mContext.enforceCallingOrSelfPermission(
-                    permission.UPDATE_APP_OPS_STATS,
-                    "hiding from app ops requires " + permission.UPDATE_APP_OPS_STATS);
-        }
-        if (request.isLocationSettingsIgnored()) {
-            mContext.enforceCallingOrSelfPermission(
-                    permission.WRITE_SECURE_SETTINGS,
-                    "ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS);
-        }
 
+        // sanitize request
         LocationRequest.Builder sanitized = new LocationRequest.Builder(request);
 
-        if (CompatChanges.isChangeEnabled(LOW_POWER_EXCEPTIONS, Binder.getCallingUid())) {
-            if (request.isLowPower()) {
-                mContext.enforceCallingOrSelfPermission(
-                        permission.LOCATION_HARDWARE,
-                        "low power request requires " + permission.LOCATION_HARDWARE);
-            }
-        } else {
+        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+                && GPS_PROVIDER.equals(provider)
+                && ArrayUtils.contains(mContext.getResources().getStringArray(
+                com.android.internal.R.array.config_locationDriverAssistancePackageNames),
+                identity.getPackageName())) {
+            sanitized.setAdasGnssBypass(true);
+        }
+
+        if (!CompatChanges.isChangeEnabled(LOW_POWER_EXCEPTIONS, Binder.getCallingUid())) {
             if (mContext.checkCallingPermission(permission.LOCATION_HARDWARE)
                     != PERMISSION_GRANTED) {
                 sanitized.setLowPower(false);
@@ -786,7 +805,52 @@
         }
         sanitized.setWorkSource(workSource);
 
-        return sanitized.build();
+        request = sanitized.build();
+
+        // validate sanitized request
+        boolean isLocationProvider = mLocalService.isProvider(null, identity);
+
+        if (request.isLowPower() && CompatChanges.isChangeEnabled(LOW_POWER_EXCEPTIONS,
+                identity.getUid())) {
+            mContext.enforceCallingOrSelfPermission(
+                    permission.LOCATION_HARDWARE,
+                    "low power request requires " + permission.LOCATION_HARDWARE);
+        }
+        if (request.isHiddenFromAppOps()) {
+            mContext.enforceCallingOrSelfPermission(
+                    permission.UPDATE_APP_OPS_STATS,
+                    "hiding from app ops requires " + permission.UPDATE_APP_OPS_STATS);
+        }
+        if (request.isAdasGnssBypass()) {
+            if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+                throw new IllegalArgumentException(
+                        "adas gnss bypass requests are only allowed on automotive devices");
+            }
+            if (!GPS_PROVIDER.equals(provider)) {
+                throw new IllegalArgumentException(
+                        "adas gnss bypass requests are only allowed on the \"gps\" provider");
+            }
+            if (!ArrayUtils.contains(mContext.getResources().getStringArray(
+                    com.android.internal.R.array.config_locationDriverAssistancePackageNames),
+                    identity.getPackageName())) {
+                throw new SecurityException(
+                        "only verified adas packages may use adas gnss bypass requests");
+            }
+            if (!isLocationProvider) {
+                mContext.enforceCallingOrSelfPermission(
+                        permission.WRITE_SECURE_SETTINGS,
+                        "adas gnss bypass requires " + permission.WRITE_SECURE_SETTINGS);
+            }
+        }
+        if (request.isLocationSettingsIgnored()) {
+            if (!isLocationProvider) {
+                mContext.enforceCallingOrSelfPermission(
+                        permission.WRITE_SECURE_SETTINGS,
+                        "ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS);
+            }
+        }
+
+        return request;
     }
 
     @Override
@@ -834,7 +898,7 @@
         // clients in the system process must have an attribution tag set
         Preconditions.checkArgument(identity.getPid() != Process.myPid() || attributionTag != null);
 
-        request = validateLastLocationRequest(request);
+        request = validateLastLocationRequest(provider, request, identity);
 
         LocationProviderManager manager = getLocationProviderManager(provider);
         if (manager == null) {
@@ -844,16 +908,58 @@
         return manager.getLastLocation(request, identity, permissionLevel);
     }
 
-    private LastLocationRequest validateLastLocationRequest(LastLocationRequest request) {
+    private LastLocationRequest validateLastLocationRequest(String provider,
+            LastLocationRequest request,
+            CallerIdentity identity) {
+        // sanitize request
+        LastLocationRequest.Builder sanitized = new LastLocationRequest.Builder(request);
+
+        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+                && GPS_PROVIDER.equals(provider)
+                && ArrayUtils.contains(mContext.getResources().getStringArray(
+                com.android.internal.R.array.config_locationDriverAssistancePackageNames),
+                identity.getPackageName())) {
+            sanitized.setAdasGnssBypass(true);
+        }
+
+        request = sanitized.build();
+
+        // validate request
+        boolean isLocationProvider = mLocalService.isProvider(null, identity);
+
         if (request.isHiddenFromAppOps()) {
             mContext.enforceCallingOrSelfPermission(
                     permission.UPDATE_APP_OPS_STATS,
                     "hiding from app ops requires " + permission.UPDATE_APP_OPS_STATS);
         }
+
+        if (request.isAdasGnssBypass()) {
+            if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+                throw new IllegalArgumentException(
+                        "adas gnss bypass requests are only allowed on automotive devices");
+            }
+            if (!GPS_PROVIDER.equals(provider)) {
+                throw new IllegalArgumentException(
+                        "adas gnss bypass requests are only allowed on the \"gps\" provider");
+            }
+            if (!ArrayUtils.contains(mContext.getResources().getStringArray(
+                    com.android.internal.R.array.config_locationDriverAssistancePackageNames),
+                    identity.getPackageName())) {
+                throw new SecurityException(
+                        "only verified adas packages may use adas gnss bypass requests");
+            }
+            if (!isLocationProvider) {
+                mContext.enforceCallingOrSelfPermission(
+                        permission.WRITE_SECURE_SETTINGS,
+                        "adas gnss bypass requires " + permission.WRITE_SECURE_SETTINGS);
+            }
+        }
         if (request.isLocationSettingsIgnored()) {
-            mContext.enforceCallingOrSelfPermission(
-                    permission.WRITE_SECURE_SETTINGS,
-                    "ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS);
+            if (!isLocationProvider) {
+                mContext.enforceCallingOrSelfPermission(
+                        permission.WRITE_SECURE_SETTINGS,
+                        "ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS);
+            }
         }
 
         return request;
@@ -1126,6 +1232,24 @@
     }
 
     @Override
+    public void setAdasGnssLocationEnabledForUser(boolean enabled, int userId) {
+        userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+                userId, false, false, "setAdasGnssLocationEnabledForUser", null);
+
+        mContext.enforceCallingOrSelfPermission(permission.WRITE_SECURE_SETTINGS, null);
+
+        mInjector.getLocationSettings().updateUserSettings(userId,
+                settings -> settings.withAdasGnssLocationEnabled(enabled));
+    }
+
+    @Override
+    public boolean isAdasGnssLocationEnabledForUser(int userId) {
+        userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+                userId, false, false, "isAdasGnssLocationEnabledForUser", null);
+        return mInjector.getLocationSettings().getUserSettings(userId).isAdasGnssLocationEnabled();
+    }
+
+    @Override
     public boolean isProviderEnabledForUser(String provider, int userId) {
         return mLocalService.isProviderEnabledForUser(provider, userId);
     }
@@ -1555,11 +1679,12 @@
         }
     }
 
-    private static class SystemInjector implements Injector {
+    private static final class SystemInjector implements Injector {
 
         private final Context mContext;
 
         private final UserInfoHelper mUserInfoHelper;
+        private final LocationSettings mLocationSettings;
         private final AlarmHelper mAlarmHelper;
         private final SystemAppOpsHelper mAppOpsHelper;
         private final SystemLocationPermissionsHelper mLocationPermissionsHelper;
@@ -1584,6 +1709,7 @@
             mContext = context;
 
             mUserInfoHelper = userInfoHelper;
+            mLocationSettings = new LocationSettings(context);
             mAlarmHelper = new SystemAlarmHelper(context);
             mAppOpsHelper = new SystemAppOpsHelper(context);
             mLocationPermissionsHelper = new SystemLocationPermissionsHelper(context,
@@ -1621,6 +1747,11 @@
         }
 
         @Override
+        public LocationSettings getLocationSettings() {
+            return mLocationSettings;
+        }
+
+        @Override
         public AlarmHelper getAlarmHelper() {
             return mAlarmHelper;
         }
diff --git a/services/core/java/com/android/server/location/LocationShellCommand.java b/services/core/java/com/android/server/location/LocationShellCommand.java
index 9378493..b65338d 100644
--- a/services/core/java/com/android/server/location/LocationShellCommand.java
+++ b/services/core/java/com/android/server/location/LocationShellCommand.java
@@ -17,6 +17,7 @@
 package com.android.server.location;
 
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.location.Criteria;
 import android.location.Location;
 import android.location.provider.ProviderProperties;
@@ -60,6 +61,14 @@
                 handleSetLocationEnabled();
                 return 0;
             }
+            case "is-adas-gnss-location-enabled": {
+                handleIsAdasGnssLocationEnabled();
+                return 0;
+            }
+            case "set-adas-gnss-location-enabled": {
+                handleSetAdasGnssLocationEnabled();
+                return 0;
+            }
             case "providers": {
                 String command = getNextArgRequired();
                 return parseProvidersCommand(command);
@@ -134,6 +143,52 @@
         mService.setLocationEnabledForUser(enabled, userId);
     }
 
+    private void handleIsAdasGnssLocationEnabled() {
+        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+            throw new IllegalStateException("command only recognized on automotive devices");
+        }
+
+        int userId = UserHandle.USER_CURRENT_OR_SELF;
+
+        do {
+            String option = getNextOption();
+            if (option == null) {
+                break;
+            }
+            if ("--user".equals(option)) {
+                userId = UserHandle.parseUserArg(getNextArgRequired());
+            } else {
+                throw new IllegalArgumentException("Unknown option: " + option);
+            }
+        } while (true);
+
+        getOutPrintWriter().println(mService.isAdasGnssLocationEnabledForUser(userId));
+    }
+
+    private void handleSetAdasGnssLocationEnabled() {
+        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+            throw new IllegalStateException("command only recognized on automotive devices");
+        }
+
+        boolean enabled = Boolean.parseBoolean(getNextArgRequired());
+
+        int userId = UserHandle.USER_CURRENT_OR_SELF;
+
+        do {
+            String option = getNextOption();
+            if (option == null) {
+                break;
+            }
+            if ("--user".equals(option)) {
+                userId = UserHandle.parseUserArg(getNextArgRequired());
+            } else {
+                throw new IllegalArgumentException("Unknown option: " + option);
+            }
+        } while (true);
+
+        mService.setAdasGnssLocationEnabledForUser(enabled, userId);
+    }
+
     private void handleAddTestProvider() {
         String provider = getNextArgRequired();
 
@@ -297,6 +352,14 @@
         pw.println("  set-location-enabled true|false [--user <USER_ID>]");
         pw.println("    Sets the master location switch enabled state. If no user is specified,");
         pw.println("    the current user is assumed.");
+        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+            pw.println("  is-adas-gnss-location-enabled [--user <USER_ID>]");
+            pw.println("    Gets the ADAS GNSS location enabled state. If no user is specified,");
+            pw.println("    the current user is assumed.");
+            pw.println("  set-adas-gnss-location-enabled true|false [--user <USER_ID>]");
+            pw.println("    Sets the ADAS GNSS location enabled state. If no user is specified,");
+            pw.println("    the current user is assumed.");
+        }
         pw.println("  providers");
         pw.println("    The providers command is followed by a subcommand, as listed below:");
         pw.println();
@@ -323,9 +386,8 @@
         pw.println("      Common commands that may be supported by the gps provider, depending on");
         pw.println("      hardware and software configurations:");
         pw.println("        delete_aiding_data - requests deletion of any predictive aiding data");
-        pw.println("        force_time_injection - requests NTP time injection to chipset");
-        pw.println("        force_psds_injection - "
-                + "requests predictive aiding data injection to chipset");
-        pw.println("        request_power_stats - requests GNSS power stats update from chipset");
+        pw.println("        force_time_injection - requests NTP time injection");
+        pw.println("        force_psds_injection - requests predictive aiding data injection");
+        pw.println("        request_power_stats - requests GNSS power stats update");
     }
 }
diff --git a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
index e6d25ec..db2a43f 100644
--- a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
@@ -55,19 +55,20 @@
 
     private static final int EVENT_USER_SWITCHED = 1;
     private static final int EVENT_LOCATION_ENABLED = 2;
-    private static final int EVENT_PROVIDER_ENABLED = 3;
-    private static final int EVENT_PROVIDER_MOCKED = 4;
-    private static final int EVENT_PROVIDER_CLIENT_REGISTER = 5;
-    private static final int EVENT_PROVIDER_CLIENT_UNREGISTER = 6;
-    private static final int EVENT_PROVIDER_CLIENT_FOREGROUND = 7;
-    private static final int EVENT_PROVIDER_CLIENT_BACKGROUND = 8;
-    private static final int EVENT_PROVIDER_CLIENT_PERMITTED = 9;
-    private static final int EVENT_PROVIDER_CLIENT_UNPERMITTED = 10;
-    private static final int EVENT_PROVIDER_UPDATE_REQUEST = 11;
-    private static final int EVENT_PROVIDER_RECEIVE_LOCATION = 12;
-    private static final int EVENT_PROVIDER_DELIVER_LOCATION = 13;
-    private static final int EVENT_PROVIDER_STATIONARY_THROTTLED = 14;
-    private static final int EVENT_LOCATION_POWER_SAVE_MODE_CHANGE = 15;
+    private static final int EVENT_ADAS_LOCATION_ENABLED = 3;
+    private static final int EVENT_PROVIDER_ENABLED = 4;
+    private static final int EVENT_PROVIDER_MOCKED = 5;
+    private static final int EVENT_PROVIDER_CLIENT_REGISTER = 6;
+    private static final int EVENT_PROVIDER_CLIENT_UNREGISTER = 7;
+    private static final int EVENT_PROVIDER_CLIENT_FOREGROUND = 8;
+    private static final int EVENT_PROVIDER_CLIENT_BACKGROUND = 9;
+    private static final int EVENT_PROVIDER_CLIENT_PERMITTED = 10;
+    private static final int EVENT_PROVIDER_CLIENT_UNPERMITTED = 11;
+    private static final int EVENT_PROVIDER_UPDATE_REQUEST = 12;
+    private static final int EVENT_PROVIDER_RECEIVE_LOCATION = 13;
+    private static final int EVENT_PROVIDER_DELIVER_LOCATION = 14;
+    private static final int EVENT_PROVIDER_STATIONARY_THROTTLED = 15;
+    private static final int EVENT_LOCATION_POWER_SAVE_MODE_CHANGE = 16;
 
     @GuardedBy("mAggregateStats")
     private final ArrayMap<String, ArrayMap<CallerIdentity, AggregateStats>> mAggregateStats;
@@ -116,6 +117,11 @@
         addLogEvent(EVENT_LOCATION_ENABLED, userId, enabled);
     }
 
+    /** Logs a location enabled/disabled event. */
+    public void logAdasLocationEnabled(int userId, boolean enabled) {
+        addLogEvent(EVENT_ADAS_LOCATION_ENABLED, userId, enabled);
+    }
+
     /** Logs a location provider enabled/disabled event. */
     public void logProviderEnabled(String provider, int userId, boolean enabled) {
         addLogEvent(EVENT_PROVIDER_ENABLED, provider, userId, enabled);
@@ -219,6 +225,9 @@
                 return new UserSwitchedEvent(timeDelta, (Integer) args[0], (Integer) args[1]);
             case EVENT_LOCATION_ENABLED:
                 return new LocationEnabledEvent(timeDelta, (Integer) args[0], (Boolean) args[1]);
+            case EVENT_ADAS_LOCATION_ENABLED:
+                return new LocationAdasEnabledEvent(timeDelta, (Integer) args[0],
+                        (Boolean) args[1]);
             case EVENT_PROVIDER_ENABLED:
                 return new ProviderEnabledEvent(timeDelta, (String) args[0], (Integer) args[1],
                         (Boolean) args[2]);
@@ -517,6 +526,23 @@
         }
     }
 
+    private static final class LocationAdasEnabledEvent extends LogEvent {
+
+        private final int mUserId;
+        private final boolean mEnabled;
+
+        LocationAdasEnabledEvent(long timeDelta, int userId, boolean enabled) {
+            super(timeDelta);
+            mUserId = userId;
+            mEnabled = enabled;
+        }
+
+        @Override
+        public String getLogString() {
+            return "adas location [u" + mUserId + "] " + (mEnabled ? "enabled" : "disabled");
+        }
+    }
+
     /**
      * Aggregate statistics for a single package under a single provider.
      */
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 1cccf08..f3dcfbb 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -771,10 +771,10 @@
         boolean enabled = mContext.getSystemService(LocationManager.class)
                 .isLocationEnabledForUser(UserHandle.CURRENT);
 
-        // .. but enable anyway, if there's an active settings-ignored request (e.g. ELS)
+        // .. but enable anyway, if there's an active bypass request (e.g. ELS or ADAS)
         enabled |= (mProviderRequest != null
                 && mProviderRequest.isActive()
-                && mProviderRequest.isLocationSettingsIgnored());
+                && mProviderRequest.isBypass());
 
         // ... and, finally, disable anyway, if device is being shut down
         enabled &= !mShutdown;
diff --git a/services/core/java/com/android/server/location/injector/Injector.java b/services/core/java/com/android/server/location/injector/Injector.java
index b035118..173fd13 100644
--- a/services/core/java/com/android/server/location/injector/Injector.java
+++ b/services/core/java/com/android/server/location/injector/Injector.java
@@ -17,6 +17,7 @@
 package com.android.server.location.injector;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.location.settings.LocationSettings;
 
 /**
  * Injects various location dependencies so that they may be controlled by tests.
@@ -27,6 +28,9 @@
     /** Returns a UserInfoHelper. */
     UserInfoHelper getUserInfoHelper();
 
+    /** Returns a LocationSettings. */
+    LocationSettings getLocationSettings();
+
     /** Returns an AlarmHelper. */
     AlarmHelper getAlarmHelper();
 
diff --git a/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java b/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java
index c315da4..3e8da7d 100644
--- a/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java
@@ -670,8 +670,6 @@
         }
     }
 
-
-
     private static class PackageTagsListSetting extends DeviceConfigSetting {
 
         private final Supplier<ArrayMap<String, ArraySet<String>>> mBaseValuesSupplier;
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 8d335b8..43886f7 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -113,6 +113,8 @@
 import com.android.server.location.injector.UserInfoHelper.UserListener;
 import com.android.server.location.listeners.ListenerMultiplexer;
 import com.android.server.location.listeners.RemoteListenerRegistration;
+import com.android.server.location.settings.LocationSettings;
+import com.android.server.location.settings.LocationUserSettings;
 
 import java.io.FileDescriptor;
 import java.lang.annotation.Retention;
@@ -549,6 +551,19 @@
         }
 
         @GuardedBy("mLock")
+        final boolean onAdasGnssLocationEnabledChanged(int userId) {
+            if (Build.IS_DEBUGGABLE) {
+                Preconditions.checkState(Thread.holdsLock(mLock));
+            }
+
+            if (getIdentity().getUserId() == userId) {
+                return onProviderLocationRequestChanged();
+            }
+
+            return false;
+        }
+
+        @GuardedBy("mLock")
         final boolean onForegroundChanged(int uid, boolean foreground) {
             if (Build.IS_DEBUGGABLE) {
                 Preconditions.checkState(Thread.holdsLock(mLock));
@@ -592,8 +607,8 @@
             onHighPowerUsageChanged();
             updateService();
 
-            // if location settings ignored has changed then the active state may have changed
-            return oldRequest.isLocationSettingsIgnored() != newRequest.isLocationSettingsIgnored();
+            // if bypass state has changed then the active state may have changed
+            return oldRequest.isBypass() != newRequest.isBypass();
         }
 
         private LocationRequest calculateProviderLocationRequest() {
@@ -616,9 +631,24 @@
                 if (!mSettingsHelper.getIgnoreSettingsAllowlist().contains(
                         getIdentity().getPackageName(), getIdentity().getAttributionTag())
                         && !mLocationManagerInternal.isProvider(null, getIdentity())) {
-                    builder.setLocationSettingsIgnored(false);
                     locationSettingsIgnored = false;
                 }
+
+                builder.setLocationSettingsIgnored(locationSettingsIgnored);
+            }
+
+            boolean adasGnssBypass = baseRequest.isAdasGnssBypass();
+            if (adasGnssBypass) {
+                // if we are not currently allowed use adas gnss bypass, disable it
+                if (!GPS_PROVIDER.equals(mName)) {
+                    Log.e(TAG, "adas gnss bypass request received in non-gps provider");
+                    adasGnssBypass = false;
+                } else if (!mLocationSettings.getUserSettings(
+                        getIdentity().getUserId()).isAdasGnssLocationEnabled()) {
+                    adasGnssBypass = false;
+                }
+
+                builder.setAdasGnssBypass(adasGnssBypass);
             }
 
             if (!locationSettingsIgnored && !isThrottlingExempt()) {
@@ -769,7 +799,7 @@
                     Location lastLocation = getLastLocationUnsafe(
                             getIdentity().getUserId(),
                             getPermissionLevel(),
-                            getRequest().isLocationSettingsIgnored(),
+                            getRequest().isBypass(),
                             maxLocationAgeMs);
                     if (lastLocation != null) {
                         executeOperation(acceptLocationChange(LocationResult.wrap(lastLocation)));
@@ -1114,7 +1144,7 @@
             Location lastLocation = getLastLocationUnsafe(
                     getIdentity().getUserId(),
                     getPermissionLevel(),
-                    getRequest().isLocationSettingsIgnored(),
+                    getRequest().isBypass(),
                     MAX_CURRENT_LOCATION_AGE_MS);
             if (lastLocation != null) {
                 executeOperation(acceptLocationChange(LocationResult.wrap(lastLocation)));
@@ -1267,6 +1297,7 @@
     private final CopyOnWriteArrayList<IProviderRequestListener> mProviderRequestListeners;
 
     protected final LocationManagerInternal mLocationManagerInternal;
+    protected final LocationSettings mLocationSettings;
     protected final SettingsHelper mSettingsHelper;
     protected final UserInfoHelper mUserHelper;
     protected final AlarmHelper mAlarmHelper;
@@ -1280,6 +1311,8 @@
     protected final LocationFudger mLocationFudger;
 
     private final UserListener mUserChangedListener = this::onUserChanged;
+    private final LocationSettings.LocationUserSettingsListener mLocationUserSettingsListener =
+            this::onLocationUserSettingsChanged;
     private final UserSettingChangedListener mLocationEnabledChangedListener =
             this::onLocationEnabledChanged;
     private final GlobalSettingChangedListener mBackgroundThrottlePackageWhitelistChangedListener =
@@ -1332,6 +1365,7 @@
 
         mLocationManagerInternal = Objects.requireNonNull(
                 LocalServices.getService(LocationManagerInternal.class));
+        mLocationSettings = injector.getLocationSettings();
         mSettingsHelper = injector.getSettingsHelper();
         mUserHelper = injector.getUserInfoHelper();
         mAlarmHelper = injector.getAlarmHelper();
@@ -1362,6 +1396,7 @@
             mStateChangedListener = listener;
 
             mUserHelper.addListener(mUserChangedListener);
+            mLocationSettings.registerLocationUserSettingsListener(mLocationUserSettingsListener);
             mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
 
             final long identity = Binder.clearCallingIdentity();
@@ -1389,6 +1424,7 @@
             }
 
             mUserHelper.removeListener(mUserChangedListener);
+            mLocationSettings.unregisterLocationUserSettingsListener(mLocationUserSettingsListener);
             mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
 
             // if external entities are registering listeners it's their responsibility to
@@ -1550,7 +1586,7 @@
 
     public @Nullable Location getLastLocation(LastLocationRequest request,
             CallerIdentity identity, @PermissionLevel int permissionLevel) {
-        if (!isActive(request.isLocationSettingsIgnored(), identity)) {
+        if (!isActive(request.isBypass(), identity)) {
             return null;
         }
 
@@ -1564,7 +1600,7 @@
                 getLastLocationUnsafe(
                         identity.getUserId(),
                         permissionLevel,
-                        request.isLocationSettingsIgnored(),
+                        request.isBypass(),
                         Long.MAX_VALUE),
                 permissionLevel);
 
@@ -1584,7 +1620,7 @@
      * location if necessary.
      */
     public @Nullable Location getLastLocationUnsafe(int userId,
-            @PermissionLevel int permissionLevel, boolean ignoreLocationSettings,
+            @PermissionLevel int permissionLevel, boolean isBypass,
             long maximumAgeMs) {
         if (userId == UserHandle.USER_ALL) {
             // find the most recent location across all users
@@ -1592,7 +1628,7 @@
             final int[] runningUserIds = mUserHelper.getRunningUserIds();
             for (int i = 0; i < runningUserIds.length; i++) {
                 Location next = getLastLocationUnsafe(runningUserIds[i], permissionLevel,
-                        ignoreLocationSettings, maximumAgeMs);
+                        isBypass, maximumAgeMs);
                 if (lastLocation == null || (next != null && next.getElapsedRealtimeNanos()
                         > lastLocation.getElapsedRealtimeNanos())) {
                     lastLocation = next;
@@ -1601,7 +1637,7 @@
             return lastLocation;
         } else if (userId == UserHandle.USER_CURRENT) {
             return getLastLocationUnsafe(mUserHelper.getCurrentUserId(), permissionLevel,
-                    ignoreLocationSettings, maximumAgeMs);
+                    isBypass, maximumAgeMs);
         }
 
         Preconditions.checkArgument(userId >= 0);
@@ -1613,7 +1649,7 @@
             if (lastLocation == null) {
                 location = null;
             } else {
-                location = lastLocation.get(permissionLevel, ignoreLocationSettings);
+                location = lastLocation.get(permissionLevel, isBypass);
             }
         }
 
@@ -1925,7 +1961,7 @@
         // provider, under the assumption that once we send the request off, the provider will
         // immediately attempt to deliver a new location satisfying that request.
         long delayMs;
-        if (!oldRequest.isLocationSettingsIgnored() && newRequest.isLocationSettingsIgnored()) {
+        if (!oldRequest.isBypass() && newRequest.isBypass()) {
             delayMs = 0;
         } else if (newRequest.getIntervalMillis() > oldRequest.getIntervalMillis()) {
             // if the interval has increased, tell the provider immediately, so it can save power
@@ -2002,12 +2038,12 @@
             return false;
         }
 
-        boolean locationSettingsIgnored = registration.getRequest().isLocationSettingsIgnored();
-        if (!isActive(locationSettingsIgnored, registration.getIdentity())) {
+        boolean isBypass = registration.getRequest().isBypass();
+        if (!isActive(isBypass, registration.getIdentity())) {
             return false;
         }
 
-        if (!locationSettingsIgnored) {
+        if (!isBypass) {
             switch (mLocationPowerSaveModeHelper.getLocationPowerSaveMode()) {
                 case LOCATION_MODE_FOREGROUND_ONLY:
                     if (!registration.isForeground()) {
@@ -2036,15 +2072,15 @@
         return true;
     }
 
-    private boolean isActive(boolean locationSettingsIgnored, CallerIdentity identity) {
+    private boolean isActive(boolean isBypass, CallerIdentity identity) {
         if (identity.isSystemServer()) {
-            if (!locationSettingsIgnored) {
+            if (!isBypass) {
                 if (!isEnabled(mUserHelper.getCurrentUserId())) {
                     return false;
                 }
             }
         } else {
-            if (!locationSettingsIgnored) {
+            if (!isBypass) {
                 if (!isEnabled(identity.getUserId())) {
                     return false;
                 }
@@ -2071,6 +2107,7 @@
         long intervalMs = ProviderRequest.INTERVAL_DISABLED;
         int quality = LocationRequest.QUALITY_LOW_POWER;
         long maxUpdateDelayMs = Long.MAX_VALUE;
+        boolean adasGnssBypass = false;
         boolean locationSettingsIgnored = false;
         boolean lowPower = true;
 
@@ -2086,6 +2123,7 @@
             intervalMs = min(request.getIntervalMillis(), intervalMs);
             quality = min(request.getQuality(), quality);
             maxUpdateDelayMs = min(request.getMaxUpdateDelayMillis(), maxUpdateDelayMs);
+            adasGnssBypass |= request.isAdasGnssBypass();
             locationSettingsIgnored |= request.isLocationSettingsIgnored();
             lowPower &= request.isLowPower();
         }
@@ -2123,6 +2161,7 @@
                 .setIntervalMillis(intervalMs)
                 .setQuality(quality)
                 .setMaxUpdateDelayMillis(maxUpdateDelayMs)
+                .setAdasGnssBypass(adasGnssBypass)
                 .setLocationSettingsIgnored(locationSettingsIgnored)
                 .setLowPower(lowPower)
                 .setWorkSource(workSource)
@@ -2191,6 +2230,16 @@
         }
     }
 
+    private void onLocationUserSettingsChanged(int userId, LocationUserSettings oldSettings,
+            LocationUserSettings newSettings) {
+        if (oldSettings.isAdasGnssLocationEnabled() != newSettings.isAdasGnssLocationEnabled()) {
+            synchronized (mLock) {
+                updateRegistrations(
+                        registration -> registration.onAdasGnssLocationEnabledChanged(userId));
+            }
+        }
+    }
+
     private void onLocationEnabledChanged(int userId) {
         synchronized (mLock) {
             if (mState == STATE_STOPPED) {
@@ -2560,16 +2609,16 @@
         }
 
         public @Nullable Location get(@PermissionLevel int permissionLevel,
-                boolean ignoreLocationSettings) {
+                boolean isBypass) {
             switch (permissionLevel) {
                 case PERMISSION_FINE:
-                    if (ignoreLocationSettings) {
+                    if (isBypass) {
                         return mFineBypassLocation;
                     } else {
                         return mFineLocation;
                     }
                 case PERMISSION_COARSE:
-                    if (ignoreLocationSettings) {
+                    if (isBypass) {
                         return mCoarseBypassLocation;
                     } else {
                         return mCoarseLocation;
diff --git a/services/core/java/com/android/server/location/settings/LocationSettings.java b/services/core/java/com/android/server/location/settings/LocationSettings.java
new file mode 100644
index 0000000..d521538
--- /dev/null
+++ b/services/core/java/com/android/server/location/settings/LocationSettings.java
@@ -0,0 +1,173 @@
+/*
+ * 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.location.settings;
+
+import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
+
+import android.content.Context;
+import android.os.Environment;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.FgThread;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.Function;
+
+/**
+ * Accessor for location user settings. Ensure there is only ever one instance as multiple instances
+ * don't play nicely with each other.
+ */
+public class LocationSettings {
+
+    /** Listens for changes to location user settings. */
+    public interface LocationUserSettingsListener {
+        /** Invoked when location user settings have changed for the given user. */
+        void onLocationUserSettingsChanged(int userId, LocationUserSettings oldSettings,
+                LocationUserSettings newSettings);
+    }
+
+    private static final String LOCATION_DIRNAME = "location";
+    private static final String LOCATION_SETTINGS_FILENAME = "settings";
+
+    final Context mContext;
+
+    @GuardedBy("mUserSettings")
+    private final SparseArray<LocationUserSettingsStore> mUserSettings;
+    private final CopyOnWriteArrayList<LocationUserSettingsListener> mUserSettingsListeners;
+
+    public LocationSettings(Context context) {
+        mContext = context;
+        mUserSettings = new SparseArray<>(1);
+        mUserSettingsListeners = new CopyOnWriteArrayList<>();
+    }
+
+    /** Registers a listener for changes to location user settings. */
+    public final void registerLocationUserSettingsListener(LocationUserSettingsListener listener) {
+        mUserSettingsListeners.add(listener);
+    }
+
+    /** Unregisters a listener for changes to location user settings. */
+    public final void unregisterLocationUserSettingsListener(
+            LocationUserSettingsListener listener) {
+        mUserSettingsListeners.remove(listener);
+    }
+
+    protected File getUserSettingsDir(int userId) {
+        return Environment.getDataSystemDeDirectory(userId);
+    }
+
+    protected LocationUserSettingsStore createUserSettingsStore(int userId, File file) {
+        return new LocationUserSettingsStore(userId, file);
+    }
+
+    private LocationUserSettingsStore getUserSettingsStore(int userId) {
+        synchronized (mUserSettings) {
+            LocationUserSettingsStore settingsStore = mUserSettings.get(userId);
+            if (settingsStore == null) {
+                File file = new File(new File(getUserSettingsDir(userId), LOCATION_DIRNAME),
+                        LOCATION_SETTINGS_FILENAME);
+                settingsStore = createUserSettingsStore(userId, file);
+                mUserSettings.put(userId, settingsStore);
+            }
+            return settingsStore;
+        }
+    }
+
+    /** Retrieves the current state of location user settings. */
+    public final LocationUserSettings getUserSettings(int userId) {
+        return getUserSettingsStore(userId).get();
+    }
+
+    /** Updates the current state of location user settings for the given user. */
+    public final void updateUserSettings(int userId,
+            Function<LocationUserSettings, LocationUserSettings> updater) {
+        getUserSettingsStore(userId).update(updater);
+    }
+
+    @VisibleForTesting
+    final void flushFiles() throws InterruptedException {
+        synchronized (mUserSettings) {
+            int size = mUserSettings.size();
+            for (int i = 0; i < size; i++) {
+                mUserSettings.valueAt(i).flushFile();
+            }
+        }
+    }
+
+    @VisibleForTesting
+    final void deleteFiles() throws InterruptedException {
+        synchronized (mUserSettings) {
+            int size = mUserSettings.size();
+            for (int i = 0; i < size; i++) {
+                mUserSettings.valueAt(i).deleteFile();
+            }
+        }
+    }
+
+    protected final void fireListeners(int userId, LocationUserSettings oldSettings,
+            LocationUserSettings newSettings) {
+        for (LocationUserSettingsListener listener : mUserSettingsListeners) {
+            listener.onLocationUserSettingsChanged(userId, oldSettings, newSettings);
+        }
+    }
+
+    class LocationUserSettingsStore extends SettingsStore<LocationUserSettings> {
+
+        protected final int mUserId;
+
+        LocationUserSettingsStore(int userId, File file) {
+            super(file);
+            mUserId = userId;
+        }
+
+        @Override
+        protected LocationUserSettings read(int version, DataInput in) throws IOException {
+            return filterSettings(LocationUserSettings.read(mContext.getResources(), version, in));
+        }
+
+        @Override
+        protected void write(DataOutput out, LocationUserSettings settings) throws IOException {
+            settings.write(out);
+        }
+
+        @Override
+        public void update(Function<LocationUserSettings, LocationUserSettings> updater) {
+            super.update(settings -> filterSettings(updater.apply(settings)));
+        }
+
+        @Override
+        protected void onChange(LocationUserSettings oldSettings,
+                LocationUserSettings newSettings) {
+            FgThread.getExecutor().execute(() -> fireListeners(mUserId, oldSettings, newSettings));
+        }
+
+        private LocationUserSettings filterSettings(LocationUserSettings settings) {
+            if (settings.isAdasGnssLocationEnabled()
+                    && !mContext.getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE)) {
+                // prevent non-automotive devices from ever enabling this
+                settings = settings.withAdasGnssLocationEnabled(false);
+            }
+            return settings;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/location/settings/LocationUserSettings.java b/services/core/java/com/android/server/location/settings/LocationUserSettings.java
new file mode 100644
index 0000000..283255e
--- /dev/null
+++ b/services/core/java/com/android/server/location/settings/LocationUserSettings.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.settings;
+
+import android.content.res.Resources;
+
+import com.android.internal.R;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.Objects;
+
+/** Holds the state of location user settings. */
+public final class LocationUserSettings implements SettingsStore.VersionedSettings {
+
+    // remember to bump this version code and add the appropriate upgrade logic whenever the format
+    // is changed.
+    private static final int VERSION = 1;
+
+    private final boolean mAdasGnssLocationEnabled;
+
+    private LocationUserSettings(boolean adasGnssLocationEnabled) {
+        mAdasGnssLocationEnabled = adasGnssLocationEnabled;
+    }
+
+    @Override
+    public int getVersion() {
+        return VERSION;
+    }
+
+    public boolean isAdasGnssLocationEnabled() {
+        return mAdasGnssLocationEnabled;
+    }
+
+    /** Returns an instance with ADAS GNSS location enabled state set as given. */
+    public LocationUserSettings withAdasGnssLocationEnabled(boolean adasEnabled) {
+        if (adasEnabled == mAdasGnssLocationEnabled) {
+            return this;
+        }
+
+        return new LocationUserSettings(adasEnabled);
+    }
+
+    void write(DataOutput out) throws IOException {
+        out.writeBoolean(mAdasGnssLocationEnabled);
+    }
+
+    static LocationUserSettings read(Resources resources, int version, DataInput in)
+            throws IOException {
+        boolean adasGnssLocationEnabled;
+
+        // upgrade code goes here. remember to bump the version field when changing the format
+        switch (version) {
+            default:
+                // set all fields to defaults
+                adasGnssLocationEnabled = resources.getBoolean(
+                        R.bool.config_defaultAdasGnssLocationEnabled);
+                break;
+            case 1:
+                adasGnssLocationEnabled = in.readBoolean();
+                // fall through
+        }
+
+        return new LocationUserSettings(adasGnssLocationEnabled);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof LocationUserSettings)) {
+            return false;
+        }
+        LocationUserSettings that = (LocationUserSettings) o;
+        return mAdasGnssLocationEnabled == that.mAdasGnssLocationEnabled;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mAdasGnssLocationEnabled);
+    }
+}
diff --git a/services/core/java/com/android/server/location/settings/SettingsStore.java b/services/core/java/com/android/server/location/settings/SettingsStore.java
new file mode 100644
index 0000000..01338a3
--- /dev/null
+++ b/services/core/java/com/android/server/location/settings/SettingsStore.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.server.location.settings;
+
+import static com.android.server.location.LocationManagerService.TAG;
+import static com.android.server.location.settings.SettingsStore.VersionedSettings.VERSION_DOES_NOT_EXIST;
+
+import android.util.AtomicFile;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.Preconditions;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.DataOutput;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.function.Function;
+
+/** Base class for read/write/versioning functionality for storing persistent settings to a file. */
+abstract class SettingsStore<T extends SettingsStore.VersionedSettings> {
+
+    interface VersionedSettings {
+        /** Represents that the settings do not exist. */
+        int VERSION_DOES_NOT_EXIST = Integer.MAX_VALUE;
+
+        /** Must always return a version number less than {@link #VERSION_DOES_NOT_EXIST}. */
+        int getVersion();
+    }
+
+    private final AtomicFile mFile;
+
+    @GuardedBy("this")
+    private boolean mInitialized;
+    @GuardedBy("this")
+    private T mCache;
+
+    protected SettingsStore(File file) {
+        mFile = new AtomicFile(file);
+    }
+
+    /**
+     * Must be implemented to read in a settings instance, and upgrade to the appropriate version
+     * where necessary. If the provided version is {@link VersionedSettings#VERSION_DOES_NOT_EXIST}
+     * then the DataInput will be empty, and the method should return a settings instance with all
+     * settings set to the default value.
+     */
+    protected abstract T read(int version, DataInput in) throws IOException;
+
+    /**
+     * Must be implemented to write the given settings to the given DataOutput.
+     */
+    protected abstract void write(DataOutput out, T settings) throws IOException;
+
+    /**
+     * Invoked when settings change, and while holding the internal lock. If used to invoke
+     * listeners, ensure they are not invoked while holding the lock (ie, asynchronously).
+     */
+    protected abstract void onChange(T oldSettings, T newSettings);
+
+    public final synchronized void initializeCache() {
+        if (!mInitialized) {
+            if (mFile.exists()) {
+                try (DataInputStream is = new DataInputStream(mFile.openRead())) {
+                    mCache = read(is.readInt(), is);
+                    Preconditions.checkState(mCache.getVersion() < VERSION_DOES_NOT_EXIST);
+                } catch (IOException e) {
+                    Log.e(TAG, "error reading location settings (" + mFile
+                            + "), falling back to defaults", e);
+                }
+            }
+
+            if (mCache == null) {
+                try {
+                    mCache = read(VERSION_DOES_NOT_EXIST,
+                            new DataInputStream(new ByteArrayInputStream(new byte[0])));
+                    Preconditions.checkState(mCache.getVersion() < VERSION_DOES_NOT_EXIST);
+                } catch (IOException e) {
+                    throw new AssertionError(e);
+                }
+            }
+
+            mInitialized = true;
+        }
+    }
+
+    public final synchronized T get() {
+        initializeCache();
+        return mCache;
+    }
+
+    public synchronized void update(Function<T, T> updater) {
+        initializeCache();
+
+        T oldSettings = mCache;
+        T newSettings = Objects.requireNonNull(updater.apply(oldSettings));
+        if (oldSettings.equals(newSettings)) {
+            return;
+        }
+
+        mCache = newSettings;
+        Preconditions.checkState(mCache.getVersion() < VERSION_DOES_NOT_EXIST);
+
+        writeLazily(newSettings);
+
+        onChange(oldSettings, newSettings);
+    }
+
+    @VisibleForTesting
+    synchronized void flushFile() throws InterruptedException {
+        CountDownLatch latch = new CountDownLatch(1);
+        BackgroundThread.getExecutor().execute(latch::countDown);
+        latch.await();
+    }
+
+    @VisibleForTesting
+    synchronized void deleteFile() throws InterruptedException {
+        CountDownLatch latch = new CountDownLatch(1);
+        BackgroundThread.getExecutor().execute(() -> {
+            mFile.delete();
+            latch.countDown();
+        });
+        latch.await();
+    }
+
+    private void writeLazily(T settings) {
+        BackgroundThread.getExecutor().execute(() -> {
+            FileOutputStream os = null;
+            try {
+                os = mFile.startWrite();
+                DataOutputStream out = new DataOutputStream(os);
+                out.writeInt(settings.getVersion());
+                write(out, settings);
+                mFile.finishWrite(os);
+            } catch (IOException e) {
+                mFile.failWrite(os);
+                Log.e(TAG, "failure serializing location settings", e);
+            } catch (Throwable e) {
+                mFile.failWrite(os);
+                throw e;
+            }
+        });
+    }
+}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 21f68ae..d791bd6 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1235,11 +1235,7 @@
     final private BroadcastReceiver mWifiReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            synchronized (mUidRulesFirstLock) {
-                synchronized (mNetworkPoliciesSecondLock) {
-                    upgradeWifiMeteredOverrideAL();
-                }
-            }
+            upgradeWifiMeteredOverride();
             // Only need to perform upgrade logic once
             mContext.unregisterReceiver(this);
         }
@@ -2617,34 +2613,43 @@
      * Perform upgrade step of moving any user-defined meterness overrides over
      * into {@link WifiConfiguration}.
      */
-    @GuardedBy({"mNetworkPoliciesSecondLock", "mUidRulesFirstLock"})
-    private void upgradeWifiMeteredOverrideAL() {
-        boolean modified = false;
-        final WifiManager wm = mContext.getSystemService(WifiManager.class);
-        final List<WifiConfiguration> configs = wm.getConfiguredNetworks();
-        for (int i = 0; i < mNetworkPolicy.size(); ) {
-            final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
-            if (policy.template.getMatchRule() == NetworkTemplate.MATCH_WIFI
-                    && !policy.inferred) {
-                mNetworkPolicy.removeAt(i);
-                modified = true;
-
-                final String networkId = resolveNetworkId(policy.template.getNetworkId());
-                for (WifiConfiguration config : configs) {
-                    if (Objects.equals(resolveNetworkId(config), networkId)) {
-                        Slog.d(TAG, "Found network " + networkId + "; upgrading metered hint");
-                        config.meteredOverride = policy.metered
-                                ? WifiConfiguration.METERED_OVERRIDE_METERED
-                                : WifiConfiguration.METERED_OVERRIDE_NOT_METERED;
-                        wm.updateNetwork(config);
-                    }
+    private void upgradeWifiMeteredOverride() {
+        final ArrayMap<String, Boolean> wifiNetworkIds = new ArrayMap<>();
+        synchronized (mNetworkPoliciesSecondLock) {
+            for (int i = 0; i < mNetworkPolicy.size();) {
+                final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
+                if (policy.template.getMatchRule() == NetworkTemplate.MATCH_WIFI
+                        && !policy.inferred) {
+                    mNetworkPolicy.removeAt(i);
+                    wifiNetworkIds.put(policy.template.getNetworkId(), policy.metered);
+                } else {
+                    i++;
                 }
-            } else {
-                i++;
             }
         }
-        if (modified) {
-            writePolicyAL();
+
+        if (wifiNetworkIds.isEmpty()) {
+            return;
+        }
+        final WifiManager wm = mContext.getSystemService(WifiManager.class);
+        final List<WifiConfiguration> configs = wm.getConfiguredNetworks();
+        for (int i = 0; i < configs.size(); ++i) {
+            final WifiConfiguration config = configs.get(i);
+            final String networkId = resolveNetworkId(config);
+            final Boolean metered = wifiNetworkIds.get(networkId);
+            if (metered != null) {
+                Slog.d(TAG, "Found network " + networkId + "; upgrading metered hint");
+                config.meteredOverride = metered
+                        ? WifiConfiguration.METERED_OVERRIDE_METERED
+                        : WifiConfiguration.METERED_OVERRIDE_NOT_METERED;
+                wm.updateNetwork(config);
+            }
+        }
+
+        synchronized (mUidRulesFirstLock) {
+            synchronized (mNetworkPoliciesSecondLock) {
+                writePolicyAL();
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index a3f3a3a..d78fbdb 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -140,6 +140,7 @@
 import android.app.ITransientNotification;
 import android.app.ITransientNotificationCallback;
 import android.app.IUriGrantsManager;
+import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
@@ -549,6 +550,8 @@
     // Used for rate limiting toasts by package.
     private MultiRateLimiter mToastRateLimiter;
 
+    private KeyguardManager mKeyguardManager;
+
     // The last key in this list owns the hardware.
     ArrayList<String> mLights = new ArrayList<>();
 
@@ -2008,6 +2011,11 @@
     }
 
     @VisibleForTesting
+    void setKeyguardManager(KeyguardManager keyguardManager) {
+        mKeyguardManager = keyguardManager;
+    }
+
+    @VisibleForTesting
     ShortcutHelper getShortcutHelper() {
         return mShortcutHelper;
     }
@@ -2653,6 +2661,7 @@
             mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
             mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
             mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
+            mKeyguardManager = getContext().getSystemService(KeyguardManager.class);
             mZenModeHelper.onSystemReady();
             RoleObserver roleObserver = new RoleObserver(getContext(),
                     getContext().getSystemService(RoleManager.class),
@@ -3806,15 +3815,18 @@
             enforceDeletingChannelHasNoFgService(pkg, callingUser, channelId);
             cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
                     callingUser, REASON_CHANNEL_REMOVED, null);
-            mPreferencesHelper.deleteNotificationChannel(pkg, callingUid, channelId);
-            // Remove from both recent notification archive and notification history
-            mArchive.removeChannelNotifications(pkg, callingUser, channelId);
-            mHistoryManager.deleteNotificationChannel(pkg, callingUid, channelId);
-            mListeners.notifyNotificationChannelChanged(pkg,
-                    UserHandle.getUserHandleForUid(callingUid),
-                    mPreferencesHelper.getNotificationChannel(pkg, callingUid, channelId, true),
-                    NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
-            handleSavePolicyFile();
+            boolean previouslyExisted = mPreferencesHelper.deleteNotificationChannel(
+                    pkg, callingUid, channelId);
+            if (previouslyExisted) {
+                // Remove from both recent notification archive and notification history
+                mArchive.removeChannelNotifications(pkg, callingUser, channelId);
+                mHistoryManager.deleteNotificationChannel(pkg, callingUid, channelId);
+                mListeners.notifyNotificationChannelChanged(pkg,
+                        UserHandle.getUserHandleForUid(callingUid),
+                        mPreferencesHelper.getNotificationChannel(pkg, callingUid, channelId, true),
+                        NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
+                handleSavePolicyFile();
+            }
         }
 
         @Override
@@ -7388,7 +7400,6 @@
         boolean beep = false;
         boolean blink = false;
 
-        final Notification notification = record.getSbn().getNotification();
         final String key = record.getKey();
 
         // Should this notification make noise, vibe, or use the LED?
@@ -7410,7 +7421,7 @@
         if (!record.isUpdate
                 && record.getImportance() > IMPORTANCE_MIN
                 && !suppressedByDnd) {
-            sendAccessibilityEvent(notification, record.getSbn().getPackageName());
+            sendAccessibilityEvent(record);
             sentAccessibilityEvent = true;
         }
 
@@ -7433,7 +7444,7 @@
                 boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
                 if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) {
                     if (!sentAccessibilityEvent) {
-                        sendAccessibilityEvent(notification, record.getSbn().getPackageName());
+                        sendAccessibilityEvent(record);
                         sentAccessibilityEvent = true;
                     }
                     if (DBG) Slog.v(TAG, "Interrupting!");
@@ -8261,17 +8272,30 @@
         return (x < low) ? low : ((x > high) ? high : x);
     }
 
-    void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
+    void sendAccessibilityEvent(NotificationRecord record) {
         if (!mAccessibilityManager.isEnabled()) {
             return;
         }
 
-        AccessibilityEvent event =
+        final Notification notification = record.getNotification();
+        final CharSequence packageName = record.getSbn().getPackageName();
+        final AccessibilityEvent event =
             AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
         event.setPackageName(packageName);
         event.setClassName(Notification.class.getName());
-        event.setParcelableData(notification);
-        CharSequence tickerText = notification.tickerText;
+        final int visibilityOverride = record.getPackageVisibilityOverride();
+        final int notifVisibility = visibilityOverride == NotificationManager.VISIBILITY_NO_OVERRIDE
+                ? notification.visibility : visibilityOverride;
+        final int userId = record.getUser().getIdentifier();
+        final boolean needPublic = userId >= 0 && mKeyguardManager.isDeviceLocked(userId);
+        if (needPublic && notifVisibility != Notification.VISIBILITY_PUBLIC) {
+            // Emit the public version if we're on the lockscreen and this notification isn't
+            // publicly visible.
+            event.setParcelableData(notification.publicVersion);
+        } else {
+            event.setParcelableData(notification);
+        }
+        final CharSequence tickerText = notification.tickerText;
         if (!TextUtils.isEmpty(tickerText)) {
             event.getText().add(tickerText);
         }
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 03676b55..96bde3d 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1126,20 +1126,21 @@
     }
 
     @Override
-    public void deleteNotificationChannel(String pkg, int uid, String channelId) {
+    public boolean deleteNotificationChannel(String pkg, int uid, String channelId) {
         synchronized (mPackagePreferences) {
             PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
             if (r == null) {
-                return;
+                return false;
             }
             NotificationChannel channel = r.channels.get(channelId);
             if (channel != null) {
-                deleteNotificationChannelLocked(channel, pkg, uid);
+                return deleteNotificationChannelLocked(channel, pkg, uid);
             }
+            return false;
         }
     }
 
-    private void deleteNotificationChannelLocked(NotificationChannel channel, String pkg, int uid) {
+    private boolean deleteNotificationChannelLocked(NotificationChannel channel, String pkg, int uid) {
         if (!channel.isDeleted()) {
             channel.setDeleted(true);
             channel.setDeletedTimeMs(System.currentTimeMillis());
@@ -1151,7 +1152,9 @@
             if (mAreChannelsBypassingDnd && channel.canBypassDnd()) {
                 updateChannelsBypassingDnd();
             }
+            return true;
         }
+        return false;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index b1d6546..3982593 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -53,7 +53,7 @@
     NotificationChannel getConversationNotificationChannel(String pkg, int uid, String channelId,
             String conversationId, boolean returnParentIfNoConversationChannel,
             boolean includeDeleted);
-    void deleteNotificationChannel(String pkg, int uid, String channelId);
+    boolean deleteNotificationChannel(String pkg, int uid, String channelId);
     void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId);
     void permanentlyDeleteNotificationChannels(String pkg, int uid);
     ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index a98f113..b144ff2 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -417,7 +417,14 @@
             newConfig = mConfig.copy();
             for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) {
                 ZenRule rule = newConfig.automaticRules.get(newConfig.automaticRules.keyAt(i));
-                if (rule.pkg.equals(packageName) && canManageAutomaticZenRule(rule)) {
+                String pkg = rule.pkg != null
+                        ? rule.pkg
+                        : (rule.component != null)
+                                ? rule.component.getPackageName()
+                                : (rule.configurationActivity != null)
+                                        ? rule.configurationActivity.getPackageName()
+                                        : null;
+                if (Objects.equals(pkg, packageName) && canManageAutomaticZenRule(rule)) {
                     newConfig.automaticRules.removeAt(i);
                 }
             }
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 5fd8e3c..44f7d88 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -177,8 +177,10 @@
     private int performDexOptLI(AndroidPackage pkg, @NonNull PackageSetting pkgSetting,
             String[] targetInstructionSets, CompilerStats.PackageStats packageStats,
             PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
+        // ClassLoader only refers non-native (jar) shared libraries and must ignore
+        // native (so) shared libraries. See also LoadedApk#createSharedLibraryLoader().
         final List<SharedLibraryInfo> sharedLibraries = pkgSetting.getPkgState()
-                .getUsesLibraryInfos();
+                .getNonNativeUsesLibraryInfos();
         final String[] instructionSets = targetInstructionSets != null ?
                 targetInstructionSets : getAppDexInstructionSets(
                 AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting),
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 60a7571..d2ed08f 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -157,6 +157,7 @@
 
     private volatile boolean mOkToSendBroadcasts = false;
     private volatile boolean mBypassNextStagedInstallerCheck = false;
+    private volatile boolean mBypassNextAllowedApexUpdateCheck = false;
 
     /**
      * File storing persisted {@link #mSessions} metadata.
@@ -650,6 +651,13 @@
                 throw new IllegalArgumentException(
                     "Non-staged APEX session doesn't support INSTALL_ENABLE_ROLLBACK");
             }
+            if (isCalledBySystemOrShell(callingUid) || mBypassNextAllowedApexUpdateCheck) {
+                params.installFlags |= PackageManager.INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK;
+            } else {
+                // Only specific APEX updates (installed through ADB, or for CTS tests) can disable
+                // allowed APEX update check.
+                params.installFlags &= ~PackageManager.INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK;
+            }
         }
 
         if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0
@@ -674,6 +682,8 @@
         }
 
         mBypassNextStagedInstallerCheck = false;
+        mBypassNextAllowedApexUpdateCheck = false;
+
         if (!params.isMultiPackage) {
             // Only system components can circumvent runtime permissions when installing.
             if ((params.installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
@@ -1106,6 +1116,14 @@
         mBypassNextStagedInstallerCheck = value;
     }
 
+    @Override
+    public void bypassNextAllowedApexUpdateCheck(boolean value) {
+        if (!isCalledBySystemOrShell(Binder.getCallingUid())) {
+            throw new SecurityException("Caller not allowed to bypass allowed apex update check");
+        }
+        mBypassNextAllowedApexUpdateCheck = value;
+    }
+
     /**
      * Set an installer to allow for the unlimited silent updates.
      */
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 4b0eb65..acc83cf 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -147,6 +147,7 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
+import com.android.server.SystemConfig;
 import com.android.server.pm.Installer.InstallerException;
 import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -2238,6 +2239,26 @@
                     .setAdmin(mInstallSource.installerPackageName)
                     .write();
         }
+
+        // Check if APEX update is allowed. We do this check in handleInstall, since this is one of
+        // the places that:
+        //   * Shared between staged and non-staged APEX update flows.
+        //   * Only is called after boot completes.
+        // The later is important, since isApexUpdateAllowed check depends on the
+        // ModuleInfoProvider, which is only populated after device has booted.
+        if (isApexSession()) {
+            boolean checkApexUpdateAllowed =
+                    (params.installFlags & PackageManager.INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK)
+                        == 0;
+            synchronized (mLock) {
+                if (checkApexUpdateAllowed && !isApexUpdateAllowed(mPackageName)) {
+                    onSessionValidationFailure(PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
+                            "Update of APEX package " + mPackageName + " is not allowed");
+                    return;
+                }
+            }
+        }
+
         if (params.isStaged) {
             mStagingManager.commitSession(mStagedSession);
             // TODO(b/136257624): CTS test fails if we don't send session finished broadcast, even
@@ -2776,6 +2797,11 @@
         return sessionContains((s) -> !s.isApexSession());
     }
 
+    private boolean isApexUpdateAllowed(String apexPackageName) {
+        return mPm.getModuleInfo(apexPackageName, 0) != null
+                || SystemConfig.getInstance().getAllowedVendorApexes().contains(apexPackageName);
+    }
+
     /**
      * Validate apex install.
      * <p>
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 364ac42..2419873 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -436,8 +436,10 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintWriter;
+import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 import java.nio.charset.StandardCharsets;
 import java.security.DigestException;
 import java.security.DigestInputStream;
@@ -1918,14 +1920,65 @@
     }
 
     /**
-     * A computer provides the functional interface to the snapshot.
+     * A {@link Computer} provides a set of functions that can operate on live data or snapshot
+     * data.  At this time, the {@link Computer} is implemented by the
+     * {@link ComputerEngine}, which is in turn extended by {@link ComputerLocked}.
+     *
+     * New functions must be added carefully.
+     * <ol>
+     * <li> New functions must be true functions with respect to data collected in a
+     * {@link Snapshot}.  Such data may never be modified from inside a {@link Computer}
+     * function.
+     * </li>
+     *
+     * <li> A new function must be implemented in {@link ComputerEngine}.
+     * </li>
+     *
+     * <li> A new function must be overridden in {@link ComputerLocked} if the function
+     * cannot safely access live data without holding the PackageManagerService lock.  The
+     * form of the {@link ComputerLocked} function must be a single call to the
+     * {@link ComputerEngine} implementation, wrapped in a <code>synchronized</code>
+     * block.  Functions in {@link ComputerLocked} should never include any other code.
+     * </li>
+     *
+     * Care must be taken when deciding if a function should be overridden in
+     * {@link ComputerLocked}.  The complex lock relationships of PackageManagerService
+     * and other managers (like PermissionManager) mean deadlock is possible.  On the
+     * other hand, not overriding in {@link ComputerLocked} may leave a function walking
+     * unstable data.
+     *
+     * To coax developers to consider such issues carefully, all methods in
+     * {@link Computer} must be annotated with <code>@LiveImplementation(override =
+     * MANDATORY)</code> or <code>LiveImplementation(locked = NOT_ALLOWED)</code>.  A unit
+     * test verifies the annotation and that the annotation corresponds to the code in
+     * {@link ComputerEngine} and {@link ComputerLocked}.
      */
-    private interface Computer {
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    protected interface Computer {
+
+        /**
+         * Every method must be annotated.
+         */
+        @Target({ ElementType.METHOD })
+        @Retention(RetentionPolicy.RUNTIME)
+        public @interface LiveImplementation {
+            // A Computer method must be annotated with one of the following values:
+            //   MANDATORY - the method must be overridden in ComputerEngineLive.  The
+            //     format of the override is a call to the super method, wrapped in a
+            //     synchronization block.
+            //   NOT_ALLOWED - the method may not appear in the live computer.  It must
+            //     be final in the ComputerEngine.
+            int MANDATORY = 1;
+            int NOT_ALLOWED = 2;
+            int override() default MANDATORY;
+            String rationale() default "";
+        }
 
         /**
          * Administrative statistics: record that the snapshot has been used.  Every call
          * to use() increments the usage counter.
          */
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         default void use() {
         }
 
@@ -1933,95 +1986,154 @@
          * Fetch the snapshot usage counter.
          * @return The number of times this snapshot was used.
          */
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         default int getUsed() {
             return 0;
         }
 
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType,
                 int flags, @PrivateResolveFlags int privateResolveFlags, int filterCallingUid,
                 int userId, boolean resolveForStart, boolean allowDynamicSplits);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType,
                 int flags, int userId);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent, String resolvedType,
                 int flags, int userId, int callingUid, boolean includeInstantApps);
+        @LiveImplementation(override = LiveImplementation.MANDATORY)
         @NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(Intent intent,
                 String resolvedType, int flags, int filterCallingUid, int userId,
                 boolean resolveForStart, boolean allowDynamicSplits, String pkgName,
                 String instantAppPkgName);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         ActivityInfo getActivityInfo(ComponentName component, int flags, int userId);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         ActivityInfo getActivityInfoInternal(ComponentName component, int flags,
                 int filterCallingUid, int userId);
+        @LiveImplementation(override = LiveImplementation.MANDATORY)
         AndroidPackage getPackage(String packageName);
+        @LiveImplementation(override = LiveImplementation.MANDATORY)
         AndroidPackage getPackage(int uid);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags,
                 int filterCallingUid, int userId);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         ApplicationInfo getApplicationInfo(String packageName, int flags, int userId);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         ApplicationInfo getApplicationInfoInternal(String packageName, int flags,
                 int filterCallingUid, int userId);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         ComponentName getDefaultHomeActivity(int userId);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates, int userId);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent, String resolvedType,
                 int flags, int sourceUserId, int parentUserId);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         Intent getHomeIntent();
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent,
                 String resolvedType, int userId);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         List<ResolveInfo> applyPostResolutionFilter(@NonNull List<ResolveInfo> resolveInfos,
                 String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
                 boolean resolveForStart, int userId, Intent intent);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         PackageInfo getPackageInfo(String packageName, int flags, int userId);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         PackageInfo getPackageInfoInternal(String packageName, long versionCode, int flags,
                 int filterCallingUid, int userId);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         PackageSetting getPackageSetting(String packageName);
+        @LiveImplementation(override = LiveImplementation.MANDATORY)
         PackageSetting getPackageSettingInternal(String packageName, int callingUid);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter,
                 int sourceUserId, int targetUserId);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         ServiceInfo getServiceInfo(ComponentName component, int flags, int userId);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version);
+        @LiveImplementation(override = LiveImplementation.MANDATORY)
         String getInstantAppPackageName(int callingUid);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         String resolveExternalPackageNameLPr(AndroidPackage pkg);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         String resolveInternalPackageNameLPr(String packageName, long versionCode);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         String[] getPackagesForUid(int uid);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         UserInfo getProfileParent(int userId);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         boolean canViewInstantApps(int callingUid, int userId);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId,
                 int flags);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         boolean isCallerSameApp(String packageName, int uid);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         boolean isComponentVisibleToInstantApp(@Nullable ComponentName component);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         boolean isComponentVisibleToInstantApp(@Nullable ComponentName component,
                 @ComponentType int type);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent, int userId,
                 String resolvedType, int flags);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         boolean isInstantApp(String packageName, int userId);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         boolean isInstantAppInternal(String packageName, @UserIdInt int userId, int callingUid);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, int callingUid,
                 @Nullable ComponentName component, @ComponentType int componentType, int userId);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, int callingUid,
                 int userId);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         boolean shouldFilterApplicationLocked(@NonNull SharedUserSetting sus, int callingUid,
                 int userId);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         int checkUidPermission(String permName, int uid);
+        @LiveImplementation(override = LiveImplementation.MANDATORY)
         int getPackageUidInternal(String packageName, int flags, int userId, int callingUid);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         int updateFlagsForApplication(int flags, int userId);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         int updateFlagsForComponent(int flags, int userId);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         int updateFlagsForPackage(int flags, int userId);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps,
                 boolean isImplicitImageCaptureIntentAndNotSetByDpc);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps,
                 boolean onlyExposedExplicitly, boolean isImplicitImageCaptureIntentAndNotSetByDpc);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId,
                 boolean requireFullPermission, boolean checkShell, String message);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
                 boolean requireFullPermission, boolean checkShell, String message);
+        @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
         void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
                 boolean requireFullPermission, boolean checkShell,
                 boolean requirePermissionWhenSameUser, String message);
+        @LiveImplementation(override = LiveImplementation.MANDATORY)
         SigningDetails getSigningDetails(@NonNull String packageName);
+        @LiveImplementation(override = LiveImplementation.MANDATORY)
         SigningDetails getSigningDetails(int uid);
+        @LiveImplementation(override = LiveImplementation.MANDATORY)
         boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId);
+        @LiveImplementation(override = LiveImplementation.MANDATORY)
         boolean filterAppAccess(String packageName, int callingUid, int userId);
+        @LiveImplementation(override = LiveImplementation.MANDATORY)
         void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState);
     }
 
@@ -2030,7 +2142,8 @@
      * is entirely self-contained - it has no implicit access to
      * PackageManagerService.
      */
-    private static class ComputerEngine implements Computer {
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    protected static class ComputerEngine implements Computer {
 
         // The administrative use counter.
         private int mUsed = 0;
@@ -2076,7 +2189,7 @@
         // PackageManagerService attributes that are primitives are referenced through the
         // pms object directly.  Primitives are the only attributes so referenced.
         protected final PackageManagerService mService;
-        protected boolean safeMode() {
+        private boolean safeMode() {
             return mService.mSafeMode;
         }
         protected ComponentName resolveComponentName() {
@@ -2130,18 +2243,18 @@
         /**
          * Record that the snapshot was used.
          */
-        public void use() {
+        public final void use() {
             mUsed++;
         }
 
         /**
          * Return the usage counter.
          */
-        public int getUsed() {
+        public final int getUsed() {
             return mUsed;
         }
 
-        public @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
+        public final @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
                 String resolvedType, int flags, @PrivateResolveFlags int privateResolveFlags,
                 int filterCallingUid, int userId, boolean resolveForStart,
                 boolean allowDynamicSplits) {
@@ -2240,14 +2353,14 @@
                     resolveForStart, userId, intent);
         }
 
-        public @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
+        public final @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
                 String resolvedType, int flags, int userId) {
             return queryIntentActivitiesInternal(
                     intent, resolvedType, flags, 0 /*privateResolveFlags*/, Binder.getCallingUid(),
                     userId, false /*resolveForStart*/, true /*allowDynamicSplits*/);
         }
 
-        public @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent,
+        public final @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent,
                 String resolvedType, int flags, int userId, int callingUid,
                 boolean includeInstantApps) {
             if (!mUserManager.exists(userId)) return Collections.emptyList();
@@ -2471,7 +2584,7 @@
             return null;
         }
 
-        public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
+        public final ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
             return getActivityInfoInternal(component, flags, Binder.getCallingUid(), userId);
         }
 
@@ -2481,7 +2594,7 @@
          * to clearing. Because it can only be provided by trusted code, its value can be
          * trusted and will be used as-is; unlike userId which will be validated by this method.
          */
-        public ActivityInfo getActivityInfoInternal(ComponentName component, int flags,
+        public final ActivityInfo getActivityInfoInternal(ComponentName component, int flags,
                 int filterCallingUid, int userId) {
             if (!mUserManager.exists(userId)) return null;
             flags = updateFlagsForComponent(flags, userId);
@@ -2535,8 +2648,8 @@
             return pkg;
         }
 
-        public ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags,
-                int filterCallingUid, int userId) {
+        public final ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName,
+                int flags, int filterCallingUid, int userId) {
             if (!mUserManager.exists(userId)) return null;
             PackageSetting ps = mSettings.getPackageLPr(packageName);
             if (ps != null) {
@@ -2563,7 +2676,7 @@
             return null;
         }
 
-        public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {
+        public final ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {
             return getApplicationInfoInternal(packageName, flags, Binder.getCallingUid(), userId);
         }
 
@@ -2573,7 +2686,7 @@
          * to clearing. Because it can only be provided by trusted code, its value can be
          * trusted and will be used as-is; unlike userId which will be validated by this method.
          */
-        public ApplicationInfo getApplicationInfoInternal(String packageName, int flags,
+        public final ApplicationInfo getApplicationInfoInternal(String packageName, int flags,
                 int filterCallingUid, int userId) {
             if (!mUserManager.exists(userId)) return null;
             flags = updateFlagsForApplication(flags, userId);
@@ -2765,7 +2878,7 @@
          * Report the 'Home' activity which is currently set as "always use this one". If non is set
          * then reports the most likely home activity or null if there are more than one.
          */
-        public ComponentName getDefaultHomeActivity(int userId) {
+        public final ComponentName getDefaultHomeActivity(int userId) {
             List<ResolveInfo> allHomeCandidates = new ArrayList<>();
             ComponentName cn = getHomeActivitiesAsUser(allHomeCandidates, userId);
             if (cn != null) {
@@ -2793,7 +2906,7 @@
             return lastComponent;
         }
 
-        public ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
+        public final ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
                 int userId) {
             Intent intent  = getHomeIntent();
             List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null,
@@ -2822,7 +2935,7 @@
             return null;
         }
 
-        public CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
+        public final CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
                 String resolvedType, int flags, int sourceUserId, int parentUserId) {
             if (!mUserManager.hasUserRestriction(UserManager.ALLOW_PARENT_PROFILE_APP_LINKING,
                     sourceUserId)) {
@@ -2868,15 +2981,15 @@
             return result;
         }
 
-        public Intent getHomeIntent() {
+        public final Intent getHomeIntent() {
             Intent intent = new Intent(Intent.ACTION_MAIN);
             intent.addCategory(Intent.CATEGORY_HOME);
             intent.addCategory(Intent.CATEGORY_DEFAULT);
             return intent;
         }
 
-        public List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent,
-                String resolvedType, int userId) {
+        public final List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(
+                Intent intent, String resolvedType, int userId) {
             CrossProfileIntentResolver resolver = mSettings.getCrossProfileIntentResolver(userId);
             if (resolver != null) {
                 return resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId);
@@ -2895,7 +3008,8 @@
          * @param intent
          * @return A filtered list of resolved activities.
          */
-        public List<ResolveInfo> applyPostResolutionFilter(@NonNull List<ResolveInfo> resolveInfos,
+        public final List<ResolveInfo> applyPostResolutionFilter(
+                @NonNull List<ResolveInfo> resolveInfos,
                 String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
                 boolean resolveForStart, int userId, Intent intent) {
             final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled(userId);
@@ -3041,7 +3155,7 @@
             return resolveInfos;
         }
 
-        public List<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent,
+        private List<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent,
                 int matchFlags, List<ResolveInfo> candidates, CrossProfileDomainInfo xpDomainInfo,
                 int userId) {
             final boolean debug = (intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0;
@@ -3188,7 +3302,7 @@
             return result;
         }
 
-        public PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId) {
+        public final PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId) {
             if (!mUserManager.exists(userId)) return null;
             if (ps == null) {
                 return null;
@@ -3258,7 +3372,7 @@
             }
         }
 
-        public PackageInfo getPackageInfo(String packageName, int flags, int userId) {
+        public final PackageInfo getPackageInfo(String packageName, int flags, int userId) {
             return getPackageInfoInternal(packageName, PackageManager.VERSION_CODE_HIGHEST,
                     flags, Binder.getCallingUid(), userId);
         }
@@ -3269,7 +3383,7 @@
          * to clearing. Because it can only be provided by trusted code, its value can be
          * trusted and will be used as-is; unlike userId which will be validated by this method.
          */
-        public PackageInfo getPackageInfoInternal(String packageName, long versionCode,
+        public final PackageInfo getPackageInfoInternal(String packageName, long versionCode,
                 int flags, int filterCallingUid, int userId) {
             if (!mUserManager.exists(userId)) return null;
             flags = updateFlagsForPackage(flags, userId);
@@ -3340,7 +3454,7 @@
         }
 
         @Nullable
-        public PackageSetting getPackageSetting(String packageName) {
+        public final PackageSetting getPackageSetting(String packageName) {
             return getPackageSettingInternal(packageName, Binder.getCallingUid());
         }
 
@@ -3350,7 +3464,7 @@
             return mSettings.getPackageLPr(packageName);
         }
 
-        public ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) {
+        public final ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) {
             final int callingUid = Binder.getCallingUid();
             if (getInstantAppPackageName(callingUid) != null) {
                 return ParceledListSlice.emptyList();
@@ -3489,7 +3603,7 @@
             return new CrossProfileDomainInfo(forwardingInfo, highestApprovalLevel);
         }
 
-        public ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter,
+        public final ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter,
                 int sourceUserId, int targetUserId) {
             ResolveInfo forwardingResolveInfo = new ResolveInfo();
             final long ident = Binder.clearCallingIdentity();
@@ -3601,7 +3715,7 @@
             return null;
         }
 
-        public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {
+        public final ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {
             if (!mUserManager.exists(userId)) return null;
             final int callingUid = Binder.getCallingUid();
             flags = updateFlagsForComponent(flags, userId);
@@ -3635,7 +3749,7 @@
         }
 
         @Nullable
-        public SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) {
+        public final SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) {
             return getSharedLibraryInfo(name, version, mSharedLibraries, null);
         }
 
@@ -3671,7 +3785,7 @@
             return ownerUid;
         }
 
-        public String resolveExternalPackageNameLPr(AndroidPackage pkg) {
+        public final String resolveExternalPackageNameLPr(AndroidPackage pkg) {
             if (pkg.getStaticSharedLibName() != null) {
                 return pkg.getManifestPackageName();
             }
@@ -3745,7 +3859,7 @@
             return packageName;
         }
 
-        public String resolveInternalPackageNameLPr(String packageName, long versionCode) {
+        public final String resolveInternalPackageNameLPr(String packageName, long versionCode) {
             final int callingUid = Binder.getCallingUid();
             return resolveInternalPackageNameInternalLocked(packageName, versionCode,
                     callingUid);
@@ -3766,7 +3880,7 @@
          * calls to invalidateGetPackagesForUidCache() to locate the points at
          * which the cache is invalidated.
          */
-        public String[] getPackagesForUid(int uid) {
+        public final String[] getPackagesForUid(int uid) {
             return getPackagesForUidInternal(uid, Binder.getCallingUid());
         }
 
@@ -3807,7 +3921,7 @@
             return null;
         }
 
-        public UserInfo getProfileParent(int userId) {
+        public final UserInfo getProfileParent(int userId) {
             final long identity = Binder.clearCallingIdentity();
             try {
                 return mUserManager.getProfileParent(userId);
@@ -3837,7 +3951,7 @@
          * <li>The calling application is the default app prediction service.</li>
          * </ol>
          */
-        public boolean canViewInstantApps(int callingUid, int userId) {
+        public final boolean canViewInstantApps(int callingUid, int userId) {
             if (callingUid < Process.FIRST_APPLICATION_UID) {
                 return true;
             }
@@ -3861,8 +3975,8 @@
             return false;
         }
 
-        public boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId,
-                int flags) {
+        public final boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid,
+                int userId, int flags) {
             // Callers can access only the libs they depend on, otherwise they need to explicitly
             // ask for the shared libraries given the caller is allowed to access all static libs.
             if ((flags & PackageManager.MATCH_STATIC_SHARED_LIBRARIES) != 0) {
@@ -3945,13 +4059,13 @@
                     == PackageManager.PERMISSION_GRANTED;
         }
 
-        public boolean isCallerSameApp(String packageName, int uid) {
+        public final boolean isCallerSameApp(String packageName, int uid) {
             AndroidPackage pkg = mPackages.get(packageName);
             return pkg != null
                     && UserHandle.getAppId(uid) == pkg.getUid();
         }
 
-        public boolean isComponentVisibleToInstantApp(@Nullable ComponentName component) {
+        public final boolean isComponentVisibleToInstantApp(@Nullable ComponentName component) {
             if (isComponentVisibleToInstantApp(component, TYPE_ACTIVITY)) {
                 return true;
             }
@@ -3964,7 +4078,7 @@
             return false;
         }
 
-        public boolean isComponentVisibleToInstantApp(
+        public final boolean isComponentVisibleToInstantApp(
                 @Nullable ComponentName component, @ComponentType int type) {
             if (type == TYPE_ACTIVITY) {
                 final ParsedActivity activity = mComponentResolver.getActivity(component);
@@ -4012,13 +4126,13 @@
          * @return {@code true} if the intent is a camera intent and the persistent preferred
          * activity was not set by the DPC.
          */
-        public boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent, int userId,
-                String resolvedType, int flags) {
+        public final boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent,
+                int userId, String resolvedType, int flags) {
             return intent.isImplicitImageCaptureIntent() && !isPersistentPreferredActivitySetByDpm(
                     intent, userId, resolvedType, flags);
         }
 
-        public boolean isInstantApp(String packageName, int userId) {
+        public final boolean isInstantApp(String packageName, int userId) {
             final int callingUid = Binder.getCallingUid();
             enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
                     false /* checkShell */, "isInstantApp");
@@ -4026,7 +4140,7 @@
             return isInstantAppInternal(packageName, userId, callingUid);
         }
 
-        public boolean isInstantAppInternal(String packageName, @UserIdInt int userId,
+        public final boolean isInstantAppInternal(String packageName, @UserIdInt int userId,
                 int callingUid) {
             if (HIDE_EPHEMERAL_APIS) {
                 return false;
@@ -4160,7 +4274,8 @@
             }
         }
 
-        public boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) {
+        public final boolean isSameProfileGroup(@UserIdInt int callerUserId,
+                @UserIdInt int userId) {
             final long identity = Binder.clearCallingIdentity();
             try {
                 return UserManagerService.getInstance().isSameProfileGroup(callerUserId, userId);
@@ -4187,7 +4302,8 @@
          *
          * @see #canViewInstantApps(int, int)
          */
-        public boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, int callingUid,
+        public final boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps,
+                int callingUid,
                 @Nullable ComponentName component, @ComponentType int componentType, int userId) {
             // if we're in an isolated process, get the real calling UID
             if (Process.isIsolated(callingUid)) {
@@ -4247,7 +4363,7 @@
         /**
          * @see #shouldFilterApplicationLocked(PackageSetting, int, ComponentName, int, int)
          */
-        public boolean shouldFilterApplicationLocked(
+        public final boolean shouldFilterApplicationLocked(
                 @Nullable PackageSetting ps, int callingUid, int userId) {
             return shouldFilterApplicationLocked(ps, callingUid, null, TYPE_UNKNOWN, userId);
         }
@@ -4255,8 +4371,8 @@
         /**
          * @see #shouldFilterApplicationLocked(PackageSetting, int, ComponentName, int, int)
          */
-        public boolean shouldFilterApplicationLocked(@NonNull SharedUserSetting sus, int callingUid,
-                int userId) {
+        public final boolean shouldFilterApplicationLocked(@NonNull SharedUserSetting sus,
+                int callingUid, int userId) {
             boolean filterApp = true;
             for (int index = sus.packages.size() - 1; index >= 0 && filterApp; index--) {
                 filterApp &= shouldFilterApplicationLocked(sus.packages.valueAt(index),
@@ -4280,7 +4396,7 @@
         }
 
         // NOTE: Can't remove without a major refactor. Keep around for now.
-        public int checkUidPermission(String permName, int uid) {
+        public final int checkUidPermission(String permName, int uid) {
             return mPermissionManager.checkUidPermission(uid, permName);
         }
 
@@ -4330,21 +4446,21 @@
         /**
          * Update given flags when being used to request {@link ApplicationInfo}.
          */
-        public int updateFlagsForApplication(int flags, int userId) {
+        public final int updateFlagsForApplication(int flags, int userId) {
             return updateFlagsForPackage(flags, userId);
         }
 
         /**
          * Update given flags when being used to request {@link ComponentInfo}.
          */
-        public int updateFlagsForComponent(int flags, int userId) {
+        public final int updateFlagsForComponent(int flags, int userId) {
             return updateFlags(flags, userId);
         }
 
         /**
          * Update given flags when being used to request {@link PackageInfo}.
          */
-        public int updateFlagsForPackage(int flags, int userId) {
+        public final int updateFlagsForPackage(int flags, int userId) {
             final boolean isCallerSystemUser = UserHandle.getCallingUserId()
                                                == UserHandle.USER_SYSTEM;
             if ((flags & PackageManager.MATCH_ANY_USER) != 0) {
@@ -4380,14 +4496,14 @@
          * action and a {@code android.intent.category.BROWSABLE} category</li>
          * </ul>
          */
-        public int updateFlagsForResolve(int flags, int userId, int callingUid,
+        public final int updateFlagsForResolve(int flags, int userId, int callingUid,
                 boolean wantInstantApps, boolean isImplicitImageCaptureIntentAndNotSetByDpc) {
             return updateFlagsForResolve(flags, userId, callingUid,
                     wantInstantApps, false /*onlyExposedExplicitly*/,
                     isImplicitImageCaptureIntentAndNotSetByDpc);
         }
 
-        public int updateFlagsForResolve(int flags, int userId, int callingUid,
+        public final int updateFlagsForResolve(int flags, int userId, int callingUid,
                 boolean wantInstantApps, boolean onlyExposedExplicitly,
                 boolean isImplicitImageCaptureIntentAndNotSetByDpc) {
             // Safe mode means we shouldn't match any third-party components
@@ -4429,7 +4545,7 @@
          * @param checkShell whether to prevent shell from access if there's a debugging restriction
          * @param message the message to log on security exception
          */
-        public void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId,
+        public final void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId,
                 boolean requireFullPermission, boolean checkShell, String message) {
             if (userId < 0) {
                 throw new IllegalArgumentException("Invalid userId " + userId);
@@ -4467,7 +4583,7 @@
          * @param checkShell whether to prevent shell from access if there's a debugging restriction
          * @param message the message to log on security exception
          */
-        public void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
+        public final void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
                 boolean requireFullPermission, boolean checkShell, String message) {
             enforceCrossUserPermission(callingUid, userId, requireFullPermission, checkShell, false,
                     message);
@@ -4484,7 +4600,7 @@
          *                                      reference the same user.
          * @param message the message to log on security exception
          */
-        public void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
+        public final void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
                 boolean requireFullPermission, boolean checkShell,
                 boolean requirePermissionWhenSameUser, String message) {
             if (userId < 0) {
@@ -4736,31 +4852,14 @@
     }
 
     /**
-     * The live computer differs from the ComputerEngine in the methods that fetch data
-     * from PackageManagerService.
-     **/
-    private static class ComputerEngineLive extends ComputerEngine {
-        ComputerEngineLive(Snapshot args) {
-            super(args);
-        }
-        protected ComponentName resolveComponentName() {
-            return mService.mResolveComponentName;
-        }
-        protected ActivityInfo instantAppInstallerActivity() {
-            return mService.mInstantAppInstallerActivity;
-        }
-        protected ApplicationInfo androidApplication() {
-            return mService.mAndroidApplication;
-        }
-    }
-
-    /**
-     * This subclass is the external interface to the live computer.  For each
-     * interface, it takes the PM lock and then delegates to the live
-     * computer engine.  This is required because there are no locks taken in
-     * the engine itself.
+     * This subclass is the external interface to the live computer.  Some internal helper
+     * methods are overridden to fetch live data instead of snapshot data.  For each
+     * Computer interface that is overridden in this class, the override takes the PM lock
+     * and then delegates to the live computer engine.  This is required because there are
+     * no locks taken in the engine itself.
      */
-    private static class ComputerLocked extends ComputerEngineLive {
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    protected static class ComputerLocked extends ComputerEngine {
         private final Object mLock;
 
         ComputerLocked(Snapshot args) {
@@ -4768,18 +4867,17 @@
             mLock = mService.mLock;
         }
 
-        /**
-         * Explicilty snapshot {@link Settings#mPackages} for cases where the caller must not lock
-         * in order to get package data. It is expected that the caller locks itself to be able
-         * to block on changes to the package data and bring itself up to date once the change
-         * propagates to it. Use with heavy caution.
-         * @return
-         */
-        private Map<String, PackageSetting> snapshotPackageSettings() {
-            return mSettings.snapshot().mPackages;
+        protected final ComponentName resolveComponentName() {
+            return mService.mResolveComponentName;
+        }
+        protected final ActivityInfo instantAppInstallerActivity() {
+            return mService.mInstantAppInstallerActivity;
+        }
+        protected final ApplicationInfo androidApplication() {
+            return mService.mAndroidApplication;
         }
 
-        public @NonNull List<ResolveInfo> queryIntentServicesInternalBody(Intent intent,
+        public final @NonNull List<ResolveInfo> queryIntentServicesInternalBody(Intent intent,
                 String resolvedType, int flags, int userId, int callingUid,
                 String instantAppPkgName) {
             synchronized (mLock) {
@@ -4787,8 +4885,8 @@
                         callingUid, instantAppPkgName);
             }
         }
-        public @NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(Intent intent,
-                String resolvedType, int flags, int filterCallingUid, int userId,
+        public final @NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(
+                Intent intent, String resolvedType, int flags, int filterCallingUid, int userId,
                 boolean resolveForStart, boolean allowDynamicSplits, String pkgName,
                 String instantAppPkgName) {
             synchronized (mLock) {
@@ -4797,31 +4895,31 @@
                         instantAppPkgName);
             }
         }
-        public ActivityInfo getActivityInfoInternalBody(ComponentName component, int flags,
+        public final ActivityInfo getActivityInfoInternalBody(ComponentName component, int flags,
                 int filterCallingUid, int userId) {
             synchronized (mLock) {
                 return super.getActivityInfoInternalBody(component, flags, filterCallingUid,
                         userId);
             }
         }
-        public AndroidPackage getPackage(String packageName) {
+        public final AndroidPackage getPackage(String packageName) {
             synchronized (mLock) {
                 return super.getPackage(packageName);
             }
         }
-        public AndroidPackage getPackage(int uid) {
+        public final AndroidPackage getPackage(int uid) {
             synchronized (mLock) {
                 return super.getPackage(uid);
             }
         }
-        public ApplicationInfo getApplicationInfoInternalBody(String packageName, int flags,
+        public final ApplicationInfo getApplicationInfoInternalBody(String packageName, int flags,
                 int filterCallingUid, int userId) {
             synchronized (mLock) {
                 return super.getApplicationInfoInternalBody(packageName, flags, filterCallingUid,
                         userId);
             }
         }
-        public ArrayList<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPrBody(
+        public final ArrayList<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPrBody(
                 Intent intent, int matchFlags, List<ResolveInfo> candidates,
                 CrossProfileDomainInfo xpDomainInfo, int userId, boolean debug) {
             synchronized (mLock) {
@@ -4829,49 +4927,49 @@
                         matchFlags, candidates, xpDomainInfo, userId, debug);
             }
         }
-        public PackageInfo getPackageInfoInternalBody(String packageName, long versionCode,
+        public final PackageInfo getPackageInfoInternalBody(String packageName, long versionCode,
                 int flags, int filterCallingUid, int userId) {
             synchronized (mLock) {
                 return super.getPackageInfoInternalBody(packageName, versionCode, flags,
                         filterCallingUid, userId);
             }
         }
-        public PackageSetting getPackageSettingInternal(String packageName, int callingUid) {
+        public final PackageSetting getPackageSettingInternal(String packageName, int callingUid) {
             synchronized (mLock) {
                 return super.getPackageSettingInternal(packageName, callingUid);
             }
         }
-        public ParceledListSlice<PackageInfo> getInstalledPackagesBody(int flags, int userId,
+        public final ParceledListSlice<PackageInfo> getInstalledPackagesBody(int flags, int userId,
                 int callingUid) {
             synchronized (mLock) {
                 return super.getInstalledPackagesBody(flags, userId, callingUid);
             }
         }
-        public ServiceInfo getServiceInfoBody(ComponentName component, int flags, int userId,
+        public final ServiceInfo getServiceInfoBody(ComponentName component, int flags, int userId,
                 int callingUid) {
             synchronized (mLock) {
                 return super.getServiceInfoBody(component, flags, userId, callingUid);
             }
         }
-        public String getInstantAppPackageName(int callingUid) {
+        public final String getInstantAppPackageName(int callingUid) {
             synchronized (mLock) {
                 return super.getInstantAppPackageName(callingUid);
             }
         }
-        public String[] getPackagesForUidInternalBody(int callingUid, int userId, int appId,
+        public final String[] getPackagesForUidInternalBody(int callingUid, int userId, int appId,
                 boolean isCallerInstantApp) {
             synchronized (mLock) {
                 return super.getPackagesForUidInternalBody(callingUid, userId, appId,
                         isCallerInstantApp);
             }
         }
-        public boolean isInstantAppInternalBody(String packageName, @UserIdInt int userId,
+        public final boolean isInstantAppInternalBody(String packageName, @UserIdInt int userId,
                 int callingUid) {
             synchronized (mLock) {
                 return super.isInstantAppInternalBody(packageName, userId, callingUid);
             }
         }
-        public boolean isInstantAppResolutionAllowedBody(Intent intent,
+        public final boolean isInstantAppResolutionAllowedBody(Intent intent,
                 List<ResolveInfo> resolvedActivities, int userId, boolean skipPackageCheck,
                 int flags) {
             synchronized (mLock) {
@@ -4879,33 +4977,33 @@
                         skipPackageCheck, flags);
             }
         }
-        public int getPackageUidInternal(String packageName, int flags, int userId,
+        public final int getPackageUidInternal(String packageName, int flags, int userId,
                 int callingUid) {
             synchronized (mLock) {
                 return super.getPackageUidInternal(packageName, flags, userId, callingUid);
             }
         }
-        public SigningDetails getSigningDetails(@NonNull String packageName) {
+        public final SigningDetails getSigningDetails(@NonNull String packageName) {
             synchronized (mLock) {
                 return super.getSigningDetails(packageName);
             }
         }
-        public SigningDetails getSigningDetails(int uid) {
+        public final SigningDetails getSigningDetails(int uid) {
             synchronized (mLock) {
                 return super.getSigningDetails(uid);
             }
         }
-        public boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) {
+        public final boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) {
             synchronized (mLock) {
                 return super.filterAppAccess(pkg, callingUid, userId);
             }
         }
-        public boolean filterAppAccess(String packageName, int callingUid, int userId) {
+        public final boolean filterAppAccess(String packageName, int callingUid, int userId) {
             synchronized (mLock) {
                 return super.filterAppAccess(packageName, callingUid, userId);
             }
         }
-        public void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) {
+        public final void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) {
             synchronized (mLock) {
                 super.dump(type, fd, pw, dumpState);
             }
@@ -12700,21 +12798,6 @@
         return performDexOpt(new DexoptOptions(packageName, compilerFilter, flags));
     }
 
-    /**
-    * Ask the package manager to compile layouts in the given package.
-    */
-    @Override
-    public boolean compileLayouts(String packageName) {
-        AndroidPackage pkg;
-        synchronized (mLock) {
-            pkg = mPackages.get(packageName);
-            if (pkg == null) {
-                return false;
-            }
-        }
-        return mViewCompiler.compileLayouts(pkg);
-    }
-
     /*package*/ boolean performDexOpt(DexoptOptions options) {
         if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
             return false;
@@ -21397,6 +21480,8 @@
         // for the uninstall-updates case and restricted profiles, remember the per-
         // user handle installed state
         int[] allUsers;
+        final int freezeUser;
+        final SparseArray<Pair<Integer, String>> enabledStateAndCallerPerUser;
         /** enabled state of the uninstalled application */
         synchronized (mLock) {
             uninstalledPs = mSettings.getPackageLPr(packageName);
@@ -21441,16 +21526,23 @@
             }
 
             info.origUsers = uninstalledPs.queryInstalledUsers(allUsers, true);
-        }
 
-        final int freezeUser;
-        if (isUpdatedSystemApp(uninstalledPs)
-                && ((deleteFlags & PackageManager.DELETE_SYSTEM_APP) == 0)) {
-            // We're downgrading a system app, which will apply to all users, so
-            // freeze them all during the downgrade
-            freezeUser = UserHandle.USER_ALL;
-        } else {
-            freezeUser = removeUser;
+            if (isUpdatedSystemApp(uninstalledPs)
+                    && ((deleteFlags & PackageManager.DELETE_SYSTEM_APP) == 0)) {
+                // We're downgrading a system app, which will apply to all users, so
+                // freeze them all during the downgrade
+                freezeUser = UserHandle.USER_ALL;
+                enabledStateAndCallerPerUser = new SparseArray<>();
+                for (int i = 0; i < allUsers.length; i++) {
+                    PackageUserState userState = uninstalledPs.readUserState(allUsers[i]);
+                    Pair<Integer, String> enabledStateAndCaller =
+                            new Pair<>(userState.enabled, userState.lastDisableAppCaller);
+                    enabledStateAndCallerPerUser.put(allUsers[i], enabledStateAndCaller);
+                }
+            } else {
+                freezeUser = removeUser;
+                enabledStateAndCallerPerUser = null;
+            }
         }
 
         synchronized (mInstallLock) {
@@ -21519,6 +21611,19 @@
                     }
                 }
             }
+            if (enabledStateAndCallerPerUser != null) {
+                synchronized (mLock) {
+                    for (int i = 0; i < allUsers.length; i++) {
+                        Pair<Integer, String> enabledStateAndCaller =
+                                enabledStateAndCallerPerUser.get(allUsers[i]);
+                        getPackageSetting(packageName)
+                                .setEnabled(enabledStateAndCaller.first,
+                                        allUsers[i],
+                                        enabledStateAndCaller.second);
+                    }
+                    mSettings.writeAllUsersPackageRestrictionsLPr();
+                }
+            }
         }
 
         return res ? PackageManager.DELETE_SUCCEEDED : PackageManager.DELETE_FAILED_INTERNAL_ERROR;
@@ -28043,8 +28148,8 @@
         }
 
         @Override
-        public void deleteOatArtifactsOfPackage(String packageName) {
-            PackageManagerService.this.deleteOatArtifactsOfPackage(packageName);
+        public long deleteOatArtifactsOfPackage(String packageName) {
+            return PackageManagerService.this.deleteOatArtifactsOfPackage(packageName);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 49559f29..1aa80a9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -307,6 +307,8 @@
                     return runLogVisibility();
                 case "bypass-staged-installer-check":
                     return runBypassStagedInstallerCheck();
+                case "bypass-allowed-apex-update-check":
+                    return runBypassAllowedApexUpdateCheck();
                 case "set-silent-updates-policy":
                     return runSetSilentUpdatesPolicy();
                 default: {
@@ -424,6 +426,20 @@
         }
     }
 
+    private int runBypassAllowedApexUpdateCheck() {
+        final PrintWriter pw = getOutPrintWriter();
+        try {
+            mInterface.getPackageInstaller()
+                    .bypassNextAllowedApexUpdateCheck(Boolean.parseBoolean(getNextArg()));
+            return 0;
+        } catch (RemoteException e) {
+            pw.println("Failure ["
+                    + e.getClass().getName() + " - "
+                    + e.getMessage() + "]");
+            return -1;
+        }
+    }
+
     private int uninstallSystemUpdates(String packageName) {
         final PrintWriter pw = getOutPrintWriter();
         boolean failedUninstalls = false;
diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
index 83d4ce7..b26b694 100644
--- a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
+++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
@@ -22,6 +22,7 @@
 import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK;
 import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK;
 
+import android.os.SystemClock;
 import android.util.Slog;
 import android.util.jar.StrictJarFile;
 
@@ -288,7 +289,7 @@
                             ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_UNKNOWN),
                     COMPILATION_REASON_MAP.getOrDefault(compilationReason, ArtStatsLog.
                             ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_UNKNOWN),
-                    /*timestamp_millis=*/ 0L,
+                    /*timestamp_millis=*/ SystemClock.uptimeMillis(),
                     ArtStatsLog.ART_DATUM_REPORTED__THREAD_TYPE__ART_THREAD_MAIN,
                     kind,
                     value,
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index bad7e5c..dab980a 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -641,7 +641,7 @@
         // Cell Broadcast Receiver
         grantSystemFixedPermissionsToSystemPackage(pm,
                 getDefaultSystemHandlerActivityPackage(pm, Intents.SMS_CB_RECEIVED_ACTION, userId),
-                userId, SMS_PERMISSIONS);
+                userId, SMS_PERMISSIONS, NEARBY_DEVICES_PERMISSIONS);
 
         // Carrier Provisioning Service
         grantPermissionsToSystemPackage(pm,
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index a391dbc..38e9d3e 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -5720,10 +5720,8 @@
                 boolean fromDatasource, int attributedOp) {
             // Now let's check the identity chain...
             final int op = AppOpsManager.permissionToOpCode(permission);
-            final int attributionChainId = (startDataDelivery)
-                    ? sAttributionChainIds.incrementAndGet()
-                    : AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
-
+            final int attributionChainId =
+                    getAttributionChainId(startDataDelivery, attributionSource);
             AttributionSource current = attributionSource;
             AttributionSource next = null;
 
@@ -5879,9 +5877,8 @@
                 return PermissionChecker.PERMISSION_HARD_DENIED;
             }
 
-            final int attributionChainId = (startDataDelivery)
-                    ? sAttributionChainIds.incrementAndGet()
-                    : AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
+            final int attributionChainId =
+                    getAttributionChainId(startDataDelivery, attributionSource);
 
             AttributionSource current = attributionSource;
             AttributionSource next = null;
@@ -6064,6 +6061,21 @@
             }
         }
 
+        private static int getAttributionChainId(boolean startDataDelivery,
+                AttributionSource source) {
+            if (source == null || source.getNext() == null || !startDataDelivery) {
+                return AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
+            }
+            int attributionChainId = sAttributionChainIds.incrementAndGet();
+
+            // handle overflow
+            if (attributionChainId < 0) {
+                attributionChainId = 0;
+                sAttributionChainIds.set(0);
+            }
+            return attributionChainId;
+        }
+
         private static @Nullable String resolvePackageName(@NonNull Context context,
                 @NonNull AttributionSource attributionSource) {
             if (attributionSource.getPackageName() != null) {
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
index 05879ec..fad0aef 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
@@ -28,6 +28,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * For use by {@link PackageSetting} to maintain functionality that used to exist in
@@ -110,6 +111,10 @@
         this.overrideSeInfo = other.overrideSeInfo;
     }
 
+    public @NonNull List<SharedLibraryInfo> getNonNativeUsesLibraryInfos() {
+        return getUsesLibraryInfos().stream()
+                .filter((l) -> !l.isNative()).collect(Collectors.toList());
+    }
 
 
     // Code below generated by codegen v1.0.14.
diff --git a/services/core/java/com/android/server/policy/LegacyGlobalActions.java b/services/core/java/com/android/server/policy/LegacyGlobalActions.java
index 5b48abb..a5969a8 100644
--- a/services/core/java/com/android/server/policy/LegacyGlobalActions.java
+++ b/services/core/java/com/android/server/policy/LegacyGlobalActions.java
@@ -135,7 +135,10 @@
         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
         filter.addAction(Intent.ACTION_SCREEN_OFF);
         filter.addAction(TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
-        context.registerReceiver(mBroadcastReceiver, filter);
+        // By default CLOSE_SYSTEM_DIALOGS broadcast is sent only for current user, which is user
+        // 10 on devices with headless system user enabled.
+        // In order to receive the broadcast, register the broadcast receiver with UserHandle.ALL.
+        context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
 
         mHasTelephony =
                 context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
index ed4a7bf..f72adb60 100644
--- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
+++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
@@ -961,11 +961,13 @@
 
     @Override
     public boolean allocateSpaceForUpdate(String packageFile) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
         if (!isUpdatableApexSupported()) {
             Log.i(TAG, "Updatable Apex not supported, "
                     + "allocateSpaceForUpdate does nothing.");
             return true;
         }
+        final long token = Binder.clearCallingIdentity();
         try {
             CompressedApexInfoList apexInfoList = getCompressedApexInfoList(packageFile);
             ApexManager apexManager = ApexManager.getInstance();
@@ -975,6 +977,8 @@
             e.rethrowAsRuntimeException();
         } catch (IOException | UnsupportedOperationException e) {
             Slog.e(TAG, "Failed to reserve space for compressed apex: ", e);
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
         return false;
     }
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index f7d6136..95a06fcf 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -453,6 +453,10 @@
         for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) {
             gatewayConnection.updateSubscriptionSnapshot(mLastSnapshot);
         }
+
+        // Update the mobile data state after updating the subscription snapshot as a change in
+        // subIds for a subGroup may affect the mobile data state.
+        handleMobileDataToggled();
     }
 
     private void handleMobileDataToggled() {
diff --git a/services/core/java/com/android/server/vibrator/StepToRampAdapter.java b/services/core/java/com/android/server/vibrator/StepToRampAdapter.java
index d439b94..1d8c64b 100644
--- a/services/core/java/com/android/server/vibrator/StepToRampAdapter.java
+++ b/services/core/java/com/android/server/vibrator/StepToRampAdapter.java
@@ -22,6 +22,7 @@
 import android.os.vibrator.StepSegment;
 import android.os.vibrator.VibrationEffectSegment;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -59,6 +60,7 @@
         convertStepsToRamps(segments);
         int newRepeatIndex = addRampDownToZeroAmplitudeSegments(segments, repeatIndex);
         newRepeatIndex = addRampDownToLoop(segments, newRepeatIndex);
+        newRepeatIndex = splitLongRampSegments(info, segments, newRepeatIndex);
         return newRepeatIndex;
     }
 
@@ -210,11 +212,70 @@
         return repeatIndex;
     }
 
+    /**
+     * Split {@link RampSegment} entries that have duration longer than {@link
+     * VibratorInfo#getPwlePrimitiveDurationMax()}.
+     */
+    private int splitLongRampSegments(VibratorInfo info, List<VibrationEffectSegment> segments,
+            int repeatIndex) {
+        int maxDuration = info.getPwlePrimitiveDurationMax();
+        if (maxDuration <= 0) {
+            // No limit set to PWLE primitive duration.
+            return repeatIndex;
+        }
+
+        int segmentCount = segments.size();
+        for (int i = 0; i < segmentCount; i++) {
+            if (!(segments.get(i) instanceof RampSegment)) {
+                continue;
+            }
+            RampSegment ramp = (RampSegment) segments.get(i);
+            int splits = ((int) ramp.getDuration() + maxDuration - 1) / maxDuration;
+            if (splits <= 1) {
+                continue;
+            }
+            segments.remove(i);
+            segments.addAll(i, splitRampSegment(ramp, splits));
+            int addedSegments = splits - 1;
+            if (repeatIndex > i) {
+                repeatIndex += addedSegments;
+            }
+            i += addedSegments;
+            segmentCount += addedSegments;
+        }
+
+        return repeatIndex;
+    }
+
     private static RampSegment apply(StepSegment segment) {
         return new RampSegment(segment.getAmplitude(), segment.getAmplitude(),
                 segment.getFrequency(), segment.getFrequency(), (int) segment.getDuration());
     }
 
+    private static List<RampSegment> splitRampSegment(RampSegment ramp, int splits) {
+        List<RampSegment> ramps = new ArrayList<>(splits);
+        long splitDuration = ramp.getDuration() / splits;
+        float previousAmplitude = ramp.getStartAmplitude();
+        float previousFrequency = ramp.getStartFrequency();
+        long accumulatedDuration = 0;
+
+        for (int i = 1; i < splits; i++) {
+            accumulatedDuration += splitDuration;
+            RampSegment rampSplit = new RampSegment(
+                    previousAmplitude, interpolateAmplitude(ramp, accumulatedDuration),
+                    previousFrequency, interpolateFrequency(ramp, accumulatedDuration),
+                    (int) splitDuration);
+            ramps.add(rampSplit);
+            previousAmplitude = rampSplit.getEndAmplitude();
+            previousFrequency = rampSplit.getEndFrequency();
+        }
+
+        ramps.add(new RampSegment(previousAmplitude, ramp.getEndAmplitude(), previousFrequency,
+                ramp.getEndFrequency(), (int) (ramp.getDuration() - accumulatedDuration)));
+
+        return ramps;
+    }
+
     private static RampSegment createRampDown(float amplitude, float frequency, long duration) {
         return new RampSegment(amplitude, /* endAmplitude= */ 0, frequency, frequency,
                 (int) duration);
@@ -244,4 +305,19 @@
         }
         return false;
     }
+
+    private static float interpolateAmplitude(RampSegment ramp, long duration) {
+        return interpolate(ramp.getStartAmplitude(), ramp.getEndAmplitude(), duration,
+                ramp.getDuration());
+    }
+
+    private static float interpolateFrequency(RampSegment ramp, long duration) {
+        return interpolate(ramp.getStartFrequency(), ramp.getEndFrequency(), duration,
+                ramp.getDuration());
+    }
+
+    private static float interpolate(float start, float end, long duration, long totalDuration) {
+        float position = (float) duration / totalDuration;
+        return start + position * (end - start);
+    }
 }
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 4a07c1a..885f0e4 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -19,7 +19,10 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.IUidObserver;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.media.AudioManager;
@@ -61,6 +64,9 @@
     private final SettingsObserver mSettingObserver;
     @VisibleForTesting
     final UidObserver mUidObserver;
+    @VisibleForTesting
+    final UserObserver mUserReceiver;
+
 
     @GuardedBy("mLock")
     private final List<OnVibratorSettingsChanged> mListeners = new ArrayList<>();
@@ -94,6 +100,7 @@
         mContext = context;
         mSettingObserver = new SettingsObserver(handler);
         mUidObserver = new UidObserver();
+        mUserReceiver = new UserObserver();
 
         VibrationEffect clickEffect = createEffectFromResource(
                 com.android.internal.R.array.config_virtualKeyVibePattern);
@@ -150,6 +157,7 @@
                     }
                 });
 
+        mContext.registerReceiver(mUserReceiver, new IntentFilter(Intent.ACTION_USER_SWITCHED));
         registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES));
         registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_WHEN_RINGING));
         registerSettingsObserver(Settings.Global.getUriFor(Settings.Global.APPLY_RAMPING_RINGER));
@@ -345,6 +353,7 @@
                 + ", mLowPowerMode=" + mLowPowerMode
                 + ", mZenMode=" + Settings.Global.zenModeToString(mZenMode)
                 + ", mProcStatesCache=" + mUidObserver.mProcStatesCache
+                + ", mHapticChannelMaxVibrationAmplitude=" + getHapticChannelMaxVibrationAmplitude()
                 + ", mHapticFeedbackIntensity="
                 + intensityToString(getCurrentIntensity(VibrationAttributes.USAGE_TOUCH))
                 + ", mHapticFeedbackDefaultIntensity="
@@ -403,6 +412,12 @@
         }
     }
 
+    private float getHapticChannelMaxVibrationAmplitude() {
+        synchronized (mLock) {
+            return mVibrator == null ? Float.NaN : mVibrator.getHapticChannelMaximumAmplitude();
+        }
+    }
+
     private int getSystemSetting(String settingName, int defaultValue) {
         return Settings.System.getIntForUser(mContext.getContentResolver(),
                 settingName, defaultValue, UserHandle.USER_CURRENT);
@@ -457,6 +472,17 @@
         }
     }
 
+    /** Implementation of {@link BroadcastReceiver} to update settings on current user change. */
+    @VisibleForTesting
+    final class UserObserver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
+                updateSettings();
+            }
+        }
+    }
+
     /** Implementation of {@link ContentObserver} to be registered to a setting {@link Uri}. */
     @VisibleForTesting
     final class UidObserver extends IUidObserver.Stub {
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index 150fde9..45d5111 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -844,7 +844,12 @@
         public List<Step> play() {
             Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePrimitivesStep");
             try {
-                int segmentCount = effect.getSegments().size();
+                // Load the next PrimitiveSegments to create a single compose call to the vibrator,
+                // limited to the vibrator composition maximum size.
+                int limit = controller.getVibratorInfo().getCompositionSizeMax();
+                int segmentCount = limit > 0
+                        ? Math.min(effect.getSegments().size(), segmentIndex + limit)
+                        : effect.getSegments().size();
                 List<PrimitiveSegment> primitives = new ArrayList<>();
                 for (int i = segmentIndex; i < segmentCount; i++) {
                     VibrationEffectSegment segment = effect.getSegments().get(i);
@@ -896,7 +901,12 @@
         public List<Step> play() {
             Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePwleStep");
             try {
-                int segmentCount = effect.getSegments().size();
+                // Load the next RampSegments to create a single composePwle call to the vibrator,
+                // limited to the vibrator PWLE maximum size.
+                int limit = controller.getVibratorInfo().getPwleSizeMax();
+                int segmentCount = limit > 0
+                        ? Math.min(effect.getSegments().size(), segmentIndex + limit)
+                        : effect.getSegments().size();
                 List<RampSegment> pwles = new ArrayList<>();
                 for (int i = segmentIndex; i < segmentCount; i++) {
                     VibrationEffectSegment segment = effect.getSegments().get(i);
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index 5dceac2d0..001d5c4 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -30,19 +30,24 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
 
 import libcore.util.NativeAllocationRegistry;
 
 /** Controls a single vibrator. */
 final class VibratorController {
     private static final String TAG = "VibratorController";
+    // TODO(b/167947076): load suggested range from config
+    private static final int SUGGESTED_FREQUENCY_SAFE_RANGE = 200;
 
     private final Object mLock = new Object();
     private final NativeWrapper mNativeWrapper;
-    private final VibratorInfo mVibratorInfo;
+    private final VibratorInfo.Builder mVibratorInfoBuilder;
 
     @GuardedBy("mLock")
+    private VibratorInfo mVibratorInfo;
+    @GuardedBy("mLock")
+    private boolean mVibratorInfoLoaded;
+    @GuardedBy("mLock")
     private final RemoteCallbackList<IVibratorStateListener> mVibratorStateListeners =
             new RemoteCallbackList<>();
     @GuardedBy("mLock")
@@ -66,10 +71,10 @@
             NativeWrapper nativeWrapper) {
         mNativeWrapper = nativeWrapper;
         mNativeWrapper.init(vibratorId, listener);
-        // TODO(b/167947076): load suggested range from config
-        mVibratorInfo = mNativeWrapper.getInfo(/* suggestedFrequencyRange= */ 200);
-        Preconditions.checkNotNull(mVibratorInfo, "Failed to retrieve data for vibrator %d",
-                vibratorId);
+        mVibratorInfoBuilder = new VibratorInfo.Builder(vibratorId);
+        mVibratorInfoLoaded = mNativeWrapper.getInfo(SUGGESTED_FREQUENCY_SAFE_RANGE,
+                mVibratorInfoBuilder);
+        mVibratorInfo = mVibratorInfoBuilder.build();
     }
 
     /** Register state listener for this vibrator. */
@@ -103,7 +108,15 @@
 
     /** Return the {@link VibratorInfo} representing the vibrator controlled by this instance. */
     public VibratorInfo getVibratorInfo() {
-        return mVibratorInfo;
+        synchronized (mLock) {
+            if (!mVibratorInfoLoaded) {
+                // Try to load the vibrator metadata that has failed in the last attempt.
+                mVibratorInfoLoaded = mNativeWrapper.getInfo(SUGGESTED_FREQUENCY_SAFE_RANGE,
+                        mVibratorInfoBuilder);
+                mVibratorInfo = mVibratorInfoBuilder.build();
+            }
+            return mVibratorInfo;
+        }
     }
 
     /**
@@ -361,7 +374,8 @@
 
         private static native void alwaysOnDisable(long nativePtr, long id);
 
-        private static native VibratorInfo getInfo(long nativePtr, float suggestedFrequencyRange);
+        private static native boolean getInfo(long nativePtr, float suggestedFrequencyRange,
+                VibratorInfo.Builder infoBuilder);
 
         private long mNativePtr = 0;
 
@@ -428,9 +442,11 @@
             alwaysOnDisable(mNativePtr, id);
         }
 
-        /** Return device vibrator metadata. */
-        public VibratorInfo getInfo(float suggestedFrequencyRange) {
-            return getInfo(mNativePtr, suggestedFrequencyRange);
+        /**
+         * Loads device vibrator metadata and returns true if all metadata was loaded successfully.
+         */
+        public boolean getInfo(float suggestedFrequencyRange, VibratorInfo.Builder infoBuilder) {
+            return getInfo(mNativePtr, suggestedFrequencyRange, infoBuilder);
         }
     }
 }
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 53f1035..782e18b 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -129,7 +129,9 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
@@ -3140,7 +3142,9 @@
         }
     }
 
-    private void writeWallpaperAttributes(TypedXmlSerializer out, String tag,
+
+    @VisibleForTesting
+    void writeWallpaperAttributes(TypedXmlSerializer out, String tag,
             WallpaperData wallpaper)
             throws IllegalArgumentException, IllegalStateException, IOException {
         if (DEBUG) {
@@ -3179,6 +3183,19 @@
                     out.attributeInt(null, "colorValue" + i, wc.toArgb());
                 }
             }
+
+            int allColorsCount = wallpaper.primaryColors.getAllColors().size();
+            out.attributeInt(null, "allColorsCount", allColorsCount);
+            if (allColorsCount > 0) {
+                int index = 0;
+                for (Map.Entry<Integer, Integer> entry : wallpaper.primaryColors.getAllColors()
+                        .entrySet()) {
+                    out.attributeInt(null, "allColorsValue" + index, entry.getKey());
+                    out.attributeInt(null, "allColorsPopulation" + index, entry.getValue());
+                    index++;
+                }
+            }
+
             out.attributeInt(null, "colorHints", wallpaper.primaryColors.getColorHints());
         }
 
@@ -3410,7 +3427,8 @@
         }
     }
 
-    private void parseWallpaperAttributes(TypedXmlPullParser parser, WallpaperData wallpaper,
+    @VisibleForTesting
+    void parseWallpaperAttributes(TypedXmlPullParser parser, WallpaperData wallpaper,
             boolean keepDimensionHints) throws XmlPullParserException {
         final int id = parser.getAttributeInt(null, "id", -1);
         if (id != -1) {
@@ -3437,7 +3455,17 @@
         wpData.mPadding.right = getAttributeInt(parser, "paddingRight", 0);
         wpData.mPadding.bottom = getAttributeInt(parser, "paddingBottom", 0);
         int colorsCount = getAttributeInt(parser, "colorsCount", 0);
-        if (colorsCount > 0) {
+        int allColorsCount =  getAttributeInt(parser, "allColorsCount", 0);
+        if (allColorsCount > 0) {
+            Map<Integer, Integer> allColors = new HashMap<>(allColorsCount);
+            for (int i = 0; i < allColorsCount; i++) {
+                int colorInt = getAttributeInt(parser, "allColorsValue" + i, 0);
+                int population = getAttributeInt(parser, "allColorsPopulation" + i, 0);
+                allColors.put(colorInt, population);
+            }
+            int colorHints = getAttributeInt(parser, "colorHints", 0);
+            wallpaper.primaryColors = new WallpaperColors(allColors, colorHints);
+        } else if (colorsCount > 0) {
             Color primary = null, secondary = null, tertiary = null;
             for (int i = 0; i < colorsCount; i++) {
                 Color color = Color.valueOf(getAttributeInt(parser, "colorValue" + i, 0));
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index ac0665a..a24319f 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1542,6 +1542,52 @@
             mEmbeddedDisplayIdList.add(displayId);
         }
 
+        boolean shellRootIsAbove(WindowState windowState, ShellRoot shellRoot) {
+            int wsLayer = mService.mPolicy.getWindowLayerLw(windowState);
+            int shellLayer = mService.mPolicy.getWindowLayerFromTypeLw(shellRoot.getWindowType(),
+                    true);
+            return shellLayer >= wsLayer;
+        }
+
+        int addShellRootsIfAbove(WindowState windowState, ArrayList<ShellRoot> shellRoots,
+                int shellRootIndex, List<WindowInfo> windows, Set<IBinder> addedWindows,
+                Region unaccountedSpace, boolean focusedWindowAdded) {
+            while (shellRootIndex < shellRoots.size()
+                    && shellRootIsAbove(windowState, shellRoots.get(shellRootIndex))) {
+                ShellRoot shellRoot = shellRoots.get(shellRootIndex);
+                shellRootIndex++;
+                final WindowInfo info = shellRoot.getWindowInfo();
+                if (info == null) {
+                    continue;
+                }
+
+                info.layer = addedWindows.size();
+                windows.add(info);
+                addedWindows.add(info.token);
+                unaccountedSpace.op(info.regionInScreen, unaccountedSpace,
+                        Region.Op.REVERSE_DIFFERENCE);
+                if (unaccountedSpace.isEmpty() && focusedWindowAdded) {
+                    break;
+                }
+            }
+            return shellRootIndex;
+        }
+
+        private ArrayList<ShellRoot> getSortedShellRoots(
+                SparseArray<ShellRoot> originalShellRoots) {
+            ArrayList<ShellRoot> sortedShellRoots = new ArrayList<>(originalShellRoots.size());
+            for (int i = originalShellRoots.size() - 1; i >= 0; --i) {
+                sortedShellRoots.add(originalShellRoots.valueAt(i));
+            }
+
+            sortedShellRoots.sort((left, right) ->
+                    mService.mPolicy.getWindowLayerFromTypeLw(right.getWindowType(), true)
+                            - mService.mPolicy.getWindowLayerFromTypeLw(left.getWindowType(),
+                            true));
+
+            return sortedShellRoots;
+        }
+
         /**
          * Check if windows have changed, and send them to the accessibility subsystem if they have.
          *
@@ -1601,9 +1647,22 @@
                 final int visibleWindowCount = visibleWindows.size();
                 HashSet<Integer> skipRemainingWindowsForTasks = new HashSet<>();
 
+                ArrayList<ShellRoot> shellRoots = getSortedShellRoots(dc.mShellRoots);
+
                 // Iterate until we figure out what is touchable for the entire screen.
+                int shellRootIndex = 0;
                 for (int i = visibleWindowCount - 1; i >= 0; i--) {
                     final WindowState windowState = visibleWindows.valueAt(i);
+                    int prevShellRootIndex = shellRootIndex;
+                    shellRootIndex = addShellRootsIfAbove(windowState, shellRoots, shellRootIndex,
+                            windows, addedWindows, unaccountedSpace, focusedWindowAdded);
+
+                    // If a Shell Root was added, it could have accounted for all the space already.
+                    if (shellRootIndex > prevShellRootIndex && unaccountedSpace.isEmpty()
+                            && focusedWindowAdded) {
+                        break;
+                    }
+
                     final Region regionInScreen = new Region();
                     computeWindowRegionInScreen(windowState, regionInScreen);
 
@@ -1627,16 +1686,6 @@
                     }
                 }
 
-                for (int i = dc.mShellRoots.size() - 1; i >= 0; --i) {
-                    final WindowInfo info = dc.mShellRoots.valueAt(i).getWindowInfo();
-                    if (info == null) {
-                        continue;
-                    }
-                    info.layer = addedWindows.size();
-                    windows.add(info);
-                    addedWindows.add(info.token);
-                }
-
                 // Remove child/parent references to windows that were not added.
                 final int windowCount = windows.size();
                 for (int i = 0; i < windowCount; i++) {
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 14f6fb3..f6217bc 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -206,8 +206,8 @@
          * observer to identify which callbacks belong to a launch event.
          */
         final long mTransitionStartTimeNs;
-        /** The device uptime in seconds when this transition info is created. */
-        final int mCurrentTransitionDeviceUptime;
+        /** The device uptime in millis when this transition info is created. */
+        final long mTransitionDeviceUptimeMs;
         /** The type can be cold (new process), warm (new activity), or hot (bring to front). */
         final int mTransitionType;
         /** Whether the process was already running when the transition started. */
@@ -277,8 +277,7 @@
             mTransitionType = transitionType;
             mProcessRunning = processRunning;
             mProcessSwitch = processSwitch;
-            mCurrentTransitionDeviceUptime =
-                    (int) TimeUnit.MILLISECONDS.toSeconds(launchingState.mCurrentUpTimeMs);
+            mTransitionDeviceUptimeMs = launchingState.mCurrentUpTimeMs;
             setLatestLaunchedActivity(r);
             launchingState.mAssociatedTransitionInfo = this;
             if (options != null) {
@@ -908,7 +907,7 @@
         final TransitionInfoSnapshot infoSnapshot = new TransitionInfoSnapshot(info);
         if (info.isInterestingToLoggerAndObserver()) {
             mLoggerHandler.post(() -> logAppTransition(
-                    info.mCurrentTransitionDeviceUptime, info.mCurrentTransitionDelayMs,
+                    info.mTransitionDeviceUptimeMs, info.mCurrentTransitionDelayMs,
                     infoSnapshot, isHibernating));
         }
         mLoggerHandler.post(() -> logAppDisplayed(infoSnapshot));
@@ -920,7 +919,7 @@
     }
 
     // This gets called on another thread without holding the activity manager lock.
-    private void logAppTransition(int currentTransitionDeviceUptime, int currentTransitionDelayMs,
+    private void logAppTransition(long transitionDeviceUptimeMs, int currentTransitionDelayMs,
             TransitionInfoSnapshot info, boolean isHibernating) {
         final LogMaker builder = new LogMaker(APP_TRANSITION);
         builder.setPackageName(info.packageName);
@@ -937,7 +936,7 @@
         }
         builder.addTaggedData(APP_TRANSITION_IS_EPHEMERAL, isInstantApp ? 1 : 0);
         builder.addTaggedData(APP_TRANSITION_DEVICE_UPTIME_SECONDS,
-                currentTransitionDeviceUptime);
+                TimeUnit.MILLISECONDS.toSeconds(transitionDeviceUptimeMs));
         builder.addTaggedData(APP_TRANSITION_DELAY_MS, currentTransitionDelayMs);
         builder.setSubtype(info.reason);
         if (info.startingWindowDelayMs != INVALID_DELAY) {
@@ -972,7 +971,7 @@
                 info.launchedActivityName,
                 info.launchedActivityLaunchedFromPackage,
                 isInstantApp,
-                currentTransitionDeviceUptime * 1000,
+                transitionDeviceUptimeMs,
                 info.reason,
                 currentTransitionDelayMs,
                 info.startingWindowDelayMs,
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 2cebc3b..0ad8782 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -38,6 +38,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -277,6 +278,7 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
+import android.service.contentcapture.ActivityEvent;
 import android.service.dreams.DreamActivity;
 import android.service.dreams.DreamManagerInternal;
 import android.service.voice.IVoiceInteractionSession;
@@ -327,6 +329,7 @@
 import com.android.server.LocalServices;
 import com.android.server.am.AppTimeTracker;
 import com.android.server.am.PendingIntentRecord;
+import com.android.server.contentcapture.ContentCaptureManagerInternal;
 import com.android.server.display.color.ColorDisplayService;
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.uri.NeededUriGrants;
@@ -647,6 +650,9 @@
     private boolean mLastContainsDismissKeyguardWindow;
     private boolean mLastContainsTurnScreenOnWindow;
 
+    /** Whether the IME is showing when transitioning away from this activity. */
+    boolean mLastImeShown;
+
     /**
      * A flag to determine if this AR is in the process of closing or entering PIP. This is needed
      * to help AR know that the app is in the process of closing but hasn't yet started closing on
@@ -674,8 +680,6 @@
     private boolean mInSizeCompatModeForBounds = false;
 
     // Whether the aspect ratio restrictions applied to the activity bounds in applyAspectRatio().
-    // TODO(b/182268157): Aspect ratio can also be applie in resolveFixedOrientationConfiguration
-    // but that isn't reflected in this boolean.
     private boolean mIsAspectRatioApplied = false;
 
     // Bounds populated in resolveFixedOrientationConfiguration when this activity is letterboxed
@@ -1785,17 +1789,19 @@
 
     /**
      * Evaluate the theme for a starting window.
+     * @param prev Previous activity which may have a starting window.
      * @param originalTheme The original theme which read from activity or application.
      * @param replaceTheme The replace theme which requested from starter.
      * @return Resolved theme.
      */
-    private int evaluateStartingWindowTheme(String pkg, int originalTheme, int replaceTheme) {
+    private int evaluateStartingWindowTheme(ActivityRecord prev, String pkg, int originalTheme,
+            int replaceTheme) {
         // Skip if the package doesn't want a starting window.
-        if (!validateStartingWindowTheme(pkg, originalTheme)) {
+        if (!validateStartingWindowTheme(prev, pkg, originalTheme)) {
             return 0;
         }
         int selectedTheme = originalTheme;
-        if (replaceTheme != 0 && validateStartingWindowTheme(pkg, replaceTheme)) {
+        if (replaceTheme != 0 && validateStartingWindowTheme(prev, pkg, replaceTheme)) {
             // allow to replace theme
             selectedTheme = replaceTheme;
         }
@@ -1832,7 +1838,7 @@
         return LAUNCH_SOURCE_TYPE_APPLICATION;
     }
 
-    private boolean validateStartingWindowTheme(String pkg, int theme) {
+    private boolean validateStartingWindowTheme(ActivityRecord prev, String pkg, int theme) {
         // If this is a translucent window, then don't show a starting window -- the current
         // effect (a full-screen opaque starting window that fades away to the real contents
         // when it is ready) does not work for this.
@@ -1869,7 +1875,11 @@
             return false;
         }
         if (windowDisableStarting && !launchedFromSystemSurface()) {
-            return false;
+            // Check if previous activity can transfer the starting window to this activity.
+            return prev != null && prev.getActivityType() == ACTIVITY_TYPE_STANDARD
+                    && prev.mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_IDLE
+                    && (prev.mStartingData != null
+                    || (prev.mStartingWindow != null && prev.mStartingSurface != null));
         }
         return true;
     }
@@ -4722,6 +4732,15 @@
             setClientVisible(visible);
         }
 
+        if (!visible) {
+            final InsetsControlTarget imeInputTarget = mDisplayContent.getImeTarget(
+                    DisplayContent.IME_TARGET_INPUT);
+            mLastImeShown = imeInputTarget != null && imeInputTarget.getWindow() != null
+                    && imeInputTarget.getWindow().mActivityRecord == this
+                    && mDisplayContent.mInputMethodWindow != null
+                    && mDisplayContent.mInputMethodWindow.isVisible();
+        }
+
         final DisplayContent displayContent = getDisplayContent();
         if (!displayContent.mClosingApps.contains(this)
                 && !displayContent.mOpeningApps.contains(this)) {
@@ -4861,6 +4880,12 @@
                             true /* activityChange */, true /* updateOomAdj */,
                             true /* addPendingTopUid */);
                 }
+                final ContentCaptureManagerInternal contentCaptureService =
+                        LocalServices.getService(ContentCaptureManagerInternal.class);
+                if (contentCaptureService != null) {
+                    contentCaptureService.notifyActivityEvent(mUserId, mActivityComponent,
+                            ActivityEvent.TYPE_ACTIVITY_STARTED);
+                }
                 break;
             case PAUSED:
                 mAtmService.updateBatteryStats(this, false);
@@ -5765,10 +5790,24 @@
     }
 
     void onStartingWindowDrawn() {
+        boolean wasTaskVisible = false;
         if (task != null) {
             mSplashScreenStyleEmpty = true;
+            wasTaskVisible = task.getHasBeenVisible();
             task.setHasBeenVisible(true);
         }
+
+        // The transition may not be executed if the starting process hasn't attached. But if the
+        // starting window is drawn, the transition can start earlier. Exclude finishing and bubble
+        // because it may be a trampoline.
+        if (!wasTaskVisible && mStartingData != null && !finishing && !mLaunchedFromBubble
+                && !mDisplayContent.mAppTransition.isReady()
+                && !mDisplayContent.mAppTransition.isRunning()) {
+            // The pending transition state will be cleared after the transition is started, so
+            // save the state for launching the client later (used by LaunchActivityItem).
+            mStartingData.mIsTransitionForward = mDisplayContent.isNextTransitionForward();
+            mDisplayContent.executeAppTransition();
+        }
     }
 
     /** Called when the windows associated app window container are drawn. */
@@ -6252,15 +6291,21 @@
 
         mSplashScreenStyleEmpty = shouldUseEmptySplashScreen(sourceRecord);
 
-        final int resolvedTheme = evaluateStartingWindowTheme(packageName, theme,
+        final int resolvedTheme = evaluateStartingWindowTheme(prev, packageName, theme,
                 splashScreenTheme);
 
+        final boolean activityCreated =
+                mState.ordinal() >= STARTED.ordinal() && mState.ordinal() <= STOPPED.ordinal();
+        // If this activity is just created and all activities below are finish, treat this
+        // scenario as warm launch.
+        final boolean newSingleActivity = !newTask && !activityCreated
+                && task.getActivity((r) -> !r.finishing && r != this) == null;
+
         final boolean shown = addStartingWindow(packageName, resolvedTheme,
                 compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
-                prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(),
-                allowTaskSnapshot(),
-                mState.ordinal() >= STARTED.ordinal() && mState.ordinal() <= STOPPED.ordinal(),
-                mSplashScreenStyleEmpty);
+                prev != null ? prev.appToken : null,
+                newTask || newSingleActivity, taskSwitch, isProcessRunning(),
+                allowTaskSnapshot(), activityCreated, mSplashScreenStyleEmpty);
         if (shown) {
             mStartingWindowState = STARTING_WINDOW_SHOWN;
         }
@@ -6481,6 +6526,11 @@
                 || dc.mChangingContainers.contains(this));
     }
 
+    boolean isTransitionForward() {
+        return (mStartingData != null && mStartingData.mIsTransitionForward)
+                || mDisplayContent.isNextTransitionForward();
+    }
+
     private int getAnimationLayer() {
         // The leash is parented to the animation layer. We need to preserve the z-order by using
         // the prefix order index, but we boost if necessary.
@@ -7235,41 +7285,48 @@
         }
 
         final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
-        final int parentWidth = parentBounds.width();
-        final int parentHeight = parentBounds.height();
-        float aspect = Math.max(parentWidth, parentHeight)
-                / (float) Math.min(parentWidth, parentHeight);
+        final Rect parentAppBounds = newParentConfig.windowConfiguration.getAppBounds();
+        final Rect containingBounds = new Rect();
+        final Rect containingAppBounds = new Rect();
+        // Need to shrink the containing bounds into a square because the parent orientation does
+        // not match the activity requested orientation.
+        if (forcedOrientation == ORIENTATION_LANDSCAPE) {
+            // Shrink height to match width. Position height within app bounds.
+            final int bottom = Math.min(parentAppBounds.top + parentBounds.width(),
+                    parentAppBounds.bottom);
+            containingBounds.set(parentBounds.left, parentAppBounds.top, parentBounds.right,
+                    bottom);
+            containingAppBounds.set(parentAppBounds.left, parentAppBounds.top,
+                    parentAppBounds.right, bottom);
+        } else {
+            // Shrink width to match height. Position width within app bounds.
+            final int right = Math.min(parentAppBounds.left + parentBounds.height(),
+                    parentAppBounds.right);
+            containingBounds.set(parentAppBounds.left, parentBounds.top, right,
+                    parentBounds.bottom);
+            containingAppBounds.set(parentAppBounds.left, parentAppBounds.top, right,
+                    parentAppBounds.bottom);
+        }
+
+        Rect mTmpFullBounds = new Rect(resolvedBounds);
+        resolvedBounds.set(containingBounds);
 
         // Override from config_fixedOrientationLetterboxAspectRatio or via ADB with
         // set-fixed-orientation-letterbox-aspect-ratio.
         final float letterboxAspectRatioOverride =
                 mWmService.mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio();
-        aspect = letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
-                ? letterboxAspectRatioOverride : aspect;
+        final float desiredAspectRatio =
+                letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
+                        ? letterboxAspectRatioOverride : computeAspectRatio(parentBounds);
+        // Apply aspect ratio to resolved bounds
+        mIsAspectRatioApplied = applyAspectRatio(resolvedBounds, containingAppBounds,
+                containingBounds, desiredAspectRatio, true);
 
-        // Adjust the fixed orientation letterbox bounds to fit the app request aspect ratio in
-        // order to use the extra available space.
-        final float maxAspectRatio = info.getMaxAspectRatio();
-        final float minAspectRatio = info.getMinAspectRatio();
-        if (aspect > maxAspectRatio && maxAspectRatio != 0) {
-            aspect = maxAspectRatio;
-        } else if (aspect < minAspectRatio) {
-            aspect = minAspectRatio;
-        }
-
-        // Store the current bounds to be able to revert to size compat mode values below if needed.
-        Rect mTmpFullBounds = new Rect(resolvedBounds);
+        // Vertically center if orientation is landscape. Bounds will later be horizontally centered
+        // in {@link updateResolvedBoundsHorizontalPosition()} regardless of orientation.
         if (forcedOrientation == ORIENTATION_LANDSCAPE) {
-            final int height = (int) Math.rint(parentWidth / aspect);
-            final int top = parentBounds.centerY() - height / 2;
-            resolvedBounds.set(parentBounds.left, top, parentBounds.right, top + height);
-        } else {
-            final int width = (int) Math.rint(parentHeight / aspect);
-            final Rect parentAppBounds = newParentConfig.windowConfiguration.getAppBounds();
-            final int left = width <= parentAppBounds.width()
-                    // Avoid overlapping with the horizontal decor area when possible.
-                    ? parentAppBounds.left : parentBounds.centerX() - width / 2;
-            resolvedBounds.set(left, parentBounds.top, left + width, parentBounds.bottom);
+            final int offsetY = parentBounds.centerY() - resolvedBounds.centerY();
+            resolvedBounds.offset(0, offsetY);
         }
 
         if (mCompatDisplayInsets != null) {
@@ -7311,8 +7368,6 @@
         // then they should be aligned later in #updateResolvedBoundsHorizontalPosition().
         if (!mTmpBounds.isEmpty()) {
             resolvedBounds.set(mTmpBounds);
-            // Exclude the horizontal decor area.
-            resolvedBounds.left = parentAppBounds.left;
         }
         if (!resolvedBounds.isEmpty() && !resolvedBounds.equals(parentBounds)) {
             // Compute the configuration based on the resolved bounds. If aspect ratio doesn't
@@ -7373,13 +7428,6 @@
             mIsAspectRatioApplied =
                     applyAspectRatio(resolvedBounds, containingAppBounds, containingBounds);
         }
-        // If the bounds are restricted by fixed aspect ratio, the resolved bounds should be put in
-        // the container app bounds. Otherwise the entire container bounds are available.
-        final boolean fillContainer = resolvedBounds.equals(containingBounds);
-        if (!fillContainer) {
-            // The horizontal position should not cover insets.
-            resolvedBounds.left = containingAppBounds.left;
-        }
 
         // Use resolvedBounds to compute other override configurations such as appBounds. The bounds
         // are calculated in compat container space. The actual position on screen will be applied
@@ -7446,6 +7494,7 @@
         // Align to top of parent (bounds) - this is a UX choice and exclude the horizontal decor
         // if needed. Horizontal position is adjusted in updateResolvedBoundsHorizontalPosition.
         // Above coordinates are in "@" space, now place "*" and "#" to screen space.
+        final boolean fillContainer = resolvedBounds.equals(containingBounds);
         final int screenPosX = fillContainer ? containerBounds.left : containerAppBounds.left;
         final int screenPosY = containerBounds.top;
         if (screenPosX != 0 || screenPosY != 0) {
@@ -7682,6 +7731,12 @@
         return true;
     }
 
+    private boolean applyAspectRatio(Rect outBounds, Rect containingAppBounds,
+            Rect containingBounds) {
+        return applyAspectRatio(outBounds, containingAppBounds, containingBounds,
+                0 /* desiredAspectRatio */, false /* fixedOrientationLetterboxed */);
+    }
+
     /**
      * Applies aspect ratio restrictions to outBounds. If no restrictions, then no change is
      * made to outBounds.
@@ -7690,17 +7745,19 @@
      */
     // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
     private boolean applyAspectRatio(Rect outBounds, Rect containingAppBounds,
-            Rect containingBounds) {
+            Rect containingBounds, float desiredAspectRatio, boolean fixedOrientationLetterboxed) {
         final float maxAspectRatio = info.getMaxAspectRatio();
         final Task rootTask = getRootTask();
         final float minAspectRatio = info.getMinAspectRatio();
 
         if (task == null || rootTask == null
-                || (inMultiWindowMode() && !shouldCreateCompatDisplayInsets())
-                || (maxAspectRatio == 0 && minAspectRatio == 0)
+                || (inMultiWindowMode() && !shouldCreateCompatDisplayInsets()
+                    && !fixedOrientationLetterboxed)
+                || (maxAspectRatio < 1 && minAspectRatio < 1 && desiredAspectRatio < 1)
                 || isInVrUiMode(getConfiguration())) {
-            // We don't enforce aspect ratio if the activity task is in multiwindow unless it
-            // is in size-compat mode. We also don't set it if we are in VR mode.
+            // We don't enforce aspect ratio if the activity task is in multiwindow unless it is in
+            // size-compat mode or is letterboxed from fixed orientation. We also don't set it if we
+            // are in VR mode.
             return false;
         }
 
@@ -7708,20 +7765,30 @@
         final int containingAppHeight = containingAppBounds.height();
         final float containingRatio = computeAspectRatio(containingAppBounds);
 
+        if (desiredAspectRatio < 1) {
+            desiredAspectRatio = containingRatio;
+        }
+
+        if (maxAspectRatio >= 1 && desiredAspectRatio > maxAspectRatio) {
+            desiredAspectRatio = maxAspectRatio;
+        } else if (minAspectRatio >= 1 && desiredAspectRatio < minAspectRatio) {
+            desiredAspectRatio = minAspectRatio;
+        }
+
         int activityWidth = containingAppWidth;
         int activityHeight = containingAppHeight;
 
-        if (containingRatio > maxAspectRatio && maxAspectRatio != 0) {
+        if (containingRatio > desiredAspectRatio) {
             if (containingAppWidth < containingAppHeight) {
                 // Width is the shorter side, so we use that to figure-out what the max. height
                 // should be given the aspect ratio.
-                activityHeight = (int) ((activityWidth * maxAspectRatio) + 0.5f);
+                activityHeight = (int) ((activityWidth * desiredAspectRatio) + 0.5f);
             } else {
                 // Height is the shorter side, so we use that to figure-out what the max. width
                 // should be given the aspect ratio.
-                activityWidth = (int) ((activityHeight * maxAspectRatio) + 0.5f);
+                activityWidth = (int) ((activityHeight * desiredAspectRatio) + 0.5f);
             }
-        } else if (containingRatio < minAspectRatio) {
+        } else if (containingRatio < desiredAspectRatio) {
             boolean adjustWidth;
             switch (getRequestedConfigurationOrientation()) {
                 case ORIENTATION_LANDSCAPE:
@@ -7749,9 +7816,9 @@
                     break;
             }
             if (adjustWidth) {
-                activityWidth = (int) ((activityHeight / minAspectRatio) + 0.5f);
+                activityWidth = (int) ((activityHeight / desiredAspectRatio) + 0.5f);
             } else {
-                activityHeight = (int) ((activityWidth / minAspectRatio) + 0.5f);
+                activityHeight = (int) ((activityWidth / desiredAspectRatio) + 0.5f);
             }
         }
 
@@ -7775,6 +7842,13 @@
         }
         outBounds.set(containingBounds.left, containingBounds.top, right, bottom);
 
+        // If the bounds are restricted by fixed aspect ratio, then out bounds should be put in the
+        // container app bounds. Otherwise the entire container bounds are available.
+        if (!outBounds.equals(containingBounds)) {
+            // The horizontal position should not cover insets.
+            outBounds.left = containingAppBounds.left;
+        }
+
         return true;
     }
 
@@ -8082,8 +8156,7 @@
                     preserveWindow);
             final ActivityLifecycleItem lifecycleItem;
             if (andResume) {
-                lifecycleItem = ResumeActivityItem.obtain(
-                        mDisplayContent.isNextTransitionForward());
+                lifecycleItem = ResumeActivityItem.obtain(isTransitionForward());
             } else {
                 lifecycleItem = PauseActivityItem.obtain();
             }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 7fe0f5b..d3d1c1c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -836,7 +836,7 @@
                 final ClientTransaction clientTransaction = ClientTransaction.obtain(
                         proc.getThread(), r.appToken);
 
-                final DisplayContent dc = r.mDisplayContent;
+                final boolean isTransitionForward = r.isTransitionForward();
                 clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
                         System.identityHashCode(r), r.info,
                         // TODO: Have this take the merged configuration instead of separate global
@@ -845,7 +845,7 @@
                         mergedConfiguration.getOverrideConfiguration(), r.compat,
                         r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
                         r.getSavedState(), r.getPersistentSavedState(), results, newIntents,
-                        r.takeOptions(), dc.isNextTransitionForward(),
+                        r.takeOptions(), isTransitionForward,
                         proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController,
                         r.createFixedRotationAdjustmentsIfNeeded(), r.shareableActivityToken,
                         r.getLaunchedFromBubble()));
@@ -853,7 +853,7 @@
                 // Set desired final state.
                 final ActivityLifecycleItem lifecycleItem;
                 if (andResume) {
-                    lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
+                    lifecycleItem = ResumeActivityItem.obtain(isTransitionForward);
                 } else {
                     lifecycleItem = PauseActivityItem.obtain();
                 }
diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java
index 0ec0142..2ea043a 100644
--- a/services/core/java/com/android/server/wm/CompatModePackages.java
+++ b/services/core/java/com/android/server/wm/CompatModePackages.java
@@ -75,10 +75,18 @@
      * CompatModePackages#DOWNSCALED is the gatekeeper of all per-app buffer downscaling
      * changes.  Disabling this change will prevent the following scaling factors from working:
      * CompatModePackages#DOWNSCALE_90
+     * CompatModePackages#DOWNSCALE_85
      * CompatModePackages#DOWNSCALE_80
+     * CompatModePackages#DOWNSCALE_75
      * CompatModePackages#DOWNSCALE_70
+     * CompatModePackages#DOWNSCALE_65
      * CompatModePackages#DOWNSCALE_60
+     * CompatModePackages#DOWNSCALE_55
      * CompatModePackages#DOWNSCALE_50
+     * CompatModePackages#DOWNSCALE_45
+     * CompatModePackages#DOWNSCALE_40
+     * CompatModePackages#DOWNSCALE_35
+     * CompatModePackages#DOWNSCALE_30
      *
      * If CompatModePackages#DOWNSCALED is enabled for an app package, then the app will be forcibly
      * resized to the highest enabled scaling factor e.g. 80% if both 80% and 70% were enabled.
@@ -100,6 +108,16 @@
 
     /**
      * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+     * CompatModePackages#DOWNSCALE_85 for a package will force the app to assume it's
+     * running on a display with 85% the vertical and horizontal resolution of the real display.
+     */
+    @ChangeId
+    @Disabled
+    @Overridable
+    public static final long DOWNSCALE_85 = 189969734L;
+
+    /**
+     * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
      * CompatModePackages#DOWNSCALE_80 for a package will force the app to assume it's
      * running on a display with 80% the vertical and horizontal resolution of the real display.
      */
@@ -110,6 +128,16 @@
 
     /**
      * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+     * CompatModePackages#DOWNSCALE_75 for a package will force the app to assume it's
+     * running on a display with 75% the vertical and horizontal resolution of the real display.
+     */
+    @ChangeId
+    @Disabled
+    @Overridable
+    public static final long DOWNSCALE_75 = 189969779L;
+
+    /**
+     * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
      * CompatModePackages#DOWNSCALE_70 for a package will force the app to assume it's
      * running on a display with 70% the vertical and horizontal resolution of the real display.
      */
@@ -120,6 +148,16 @@
 
     /**
      * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+     * CompatModePackages#DOWNSCALE_65 for a package will force the app to assume it's
+     * running on a display with 65% the vertical and horizontal resolution of the real display.
+     */
+    @ChangeId
+    @Disabled
+    @Overridable
+    public static final long DOWNSCALE_65 = 189969744L;
+
+    /**
+     * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
      * CompatModePackages#DOWNSCALE_60 for a package will force the app to assume it's
      * running on a display with 60% the vertical and horizontal resolution of the real display.
      */
@@ -130,6 +168,16 @@
 
     /**
      * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+     * CompatModePackages#DOWNSCALE_55 for a package will force the app to assume it's
+     * running on a display with 55% the vertical and horizontal resolution of the real display.
+     */
+    @ChangeId
+    @Disabled
+    @Overridable
+    public static final long DOWNSCALE_55 = 189970036L;
+
+    /**
+     * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
      * CompatModePackages#DOWNSCALE_50 for a package will force the app to assume it's
      * running on a display with 50% vertical and horizontal resolution of the real display.
      */
@@ -139,6 +187,46 @@
     public static final long DOWNSCALE_50 = 176926741L;
 
     /**
+     * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+     * CompatModePackages#DOWNSCALE_45 for a package will force the app to assume it's
+     * running on a display with 45% the vertical and horizontal resolution of the real display.
+     */
+    @ChangeId
+    @Disabled
+    @Overridable
+    public static final long DOWNSCALE_45 = 189969782L;
+
+    /**
+     * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+     * CompatModePackages#DOWNSCALE_40 for a package will force the app to assume it's
+     * running on a display with 40% the vertical and horizontal resolution of the real display.
+     */
+    @ChangeId
+    @Disabled
+    @Overridable
+    public static final long DOWNSCALE_40 = 189970038L;
+
+    /**
+     * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+     * CompatModePackages#DOWNSCALE_35 for a package will force the app to assume it's
+     * running on a display with 35% the vertical and horizontal resolution of the real display.
+     */
+    @ChangeId
+    @Disabled
+    @Overridable
+    public static final long DOWNSCALE_35 = 189969749L;
+
+    /**
+     * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+     * CompatModePackages#DOWNSCALE_30 for a package will force the app to assume it's
+     * running on a display with 30% the vertical and horizontal resolution of the real display.
+     */
+    @ChangeId
+    @Disabled
+    @Overridable
+    public static final long DOWNSCALE_30 = 189970040L;
+
+    /**
      * On Android TV applications that target pre-S are not expecting to receive a Window larger
      * than 1080p, so if needed we are downscaling their Windows to 1080p.
      * However, applications that target S and greater release version are expected to be able to
@@ -291,18 +379,42 @@
             if (CompatChanges.isChangeEnabled(DOWNSCALE_90, packageName, userHandle)) {
                 return 1f / 0.9f;
             }
+            if (CompatChanges.isChangeEnabled(DOWNSCALE_85, packageName, userHandle)) {
+                return 1f / 0.85f;
+            }
             if (CompatChanges.isChangeEnabled(DOWNSCALE_80, packageName, userHandle)) {
                 return 1f / 0.8f;
             }
+            if (CompatChanges.isChangeEnabled(DOWNSCALE_75, packageName, userHandle)) {
+                return 1f / 0.75f;
+            }
             if (CompatChanges.isChangeEnabled(DOWNSCALE_70, packageName, userHandle)) {
                 return 1f / 0.7f;
             }
+            if (CompatChanges.isChangeEnabled(DOWNSCALE_65, packageName, userHandle)) {
+                return 1f / 0.65f;
+            }
             if (CompatChanges.isChangeEnabled(DOWNSCALE_60, packageName, userHandle)) {
                 return 1f / 0.6f;
             }
+            if (CompatChanges.isChangeEnabled(DOWNSCALE_55, packageName, userHandle)) {
+                return 1f / 0.55f;
+            }
             if (CompatChanges.isChangeEnabled(DOWNSCALE_50, packageName, userHandle)) {
                 return 1f / 0.5f;
             }
+            if (CompatChanges.isChangeEnabled(DOWNSCALE_45, packageName, userHandle)) {
+                return 1f / 0.45f;
+            }
+            if (CompatChanges.isChangeEnabled(DOWNSCALE_40, packageName, userHandle)) {
+                return 1f / 0.4f;
+            }
+            if (CompatChanges.isChangeEnabled(DOWNSCALE_35, packageName, userHandle)) {
+                return 1f / 0.35f;
+            }
+            if (CompatChanges.isChangeEnabled(DOWNSCALE_30, packageName, userHandle)) {
+                return 1f / 0.3f;
+            }
         }
 
         if (mService.mHasLeanbackFeature) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e0bae9d..c7cf4b0 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -63,6 +63,8 @@
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
 import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
@@ -208,6 +210,7 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 import android.view.SurfaceSession;
+import android.view.View;
 import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.WindowManager.DisplayImePolicy;
@@ -1549,9 +1552,7 @@
             // to cover the activity configuration change.
             return false;
         }
-        if ((r.mStartingData != null && r.mStartingData.hasImeSurface())
-                || (mInsetsStateController.getImeSourceProvider()
-                        .getSource().getVisibleFrame() != null)) {
+        if (r.attachedToProcess() && mayImeShowOnLaunchingActivity(r)) {
             // Currently it is unknown that when will IME window be ready. Reject the case to
             // avoid flickering by showing IME in inconsistent orientation.
             return false;
@@ -1607,6 +1608,24 @@
         return true;
     }
 
+    /** Returns {@code true} if the IME is possible to show on the launching activity. */
+    private boolean mayImeShowOnLaunchingActivity(@NonNull ActivityRecord r) {
+        final WindowState win = r.findMainWindow();
+        if (win == null) {
+            return false;
+        }
+        // See InputMethodManagerService#shouldRestoreImeVisibility that we expecting the IME
+        // should be hidden when the window set the hidden softInputMode.
+        final int softInputMode = win.mAttrs.softInputMode;
+        switch (softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) {
+            case SOFT_INPUT_STATE_ALWAYS_HIDDEN:
+            case SOFT_INPUT_STATE_HIDDEN:
+                return false;
+        }
+        return r.mLastImeShown && mInputMethodWindow != null && mInputMethodWindow.mHasSurface
+                && mInputMethodWindow.mViewVisibility == View.VISIBLE;
+    }
+
     /** Returns {@code true} if the top activity is transformed with the new rotation of display. */
     boolean hasTopFixedRotationLaunchingApp() {
         return mFixedRotationLaunchingApp != null
@@ -3814,7 +3833,7 @@
         // 4. Update the IME control target to apply any inset change and animation.
         // 5. Reparent the IME container surface to either the input target app, or the IME window
         // parent.
-        updateImeControlTarget();
+        updateImeControlTarget(true /* forceUpdateImeParent */);
     }
 
     @VisibleForTesting
@@ -3946,12 +3965,17 @@
     }
 
     void updateImeControlTarget() {
+        updateImeControlTarget(false /* forceUpdateImeParent */);
+    }
+
+    void updateImeControlTarget(boolean forceUpdateImeParent) {
         InsetsControlTarget prevImeControlTarget = mImeControlTarget;
         mImeControlTarget = computeImeControlTarget();
         mInsetsStateController.onImeControlTargetChanged(mImeControlTarget);
-        // Update Ime parent when IME insets leash created, which is the best time that default
-        // IME visibility has been settled down after IME control target changed.
-        if (prevImeControlTarget != mImeControlTarget) {
+        // Update Ime parent when IME insets leash created or the new IME layering target might
+        // updated from setImeLayeringTarget, which is the best time that default IME visibility
+        // has been settled down after IME control target changed.
+        if (prevImeControlTarget != mImeControlTarget || forceUpdateImeParent) {
             updateImeParent();
         }
 
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ea80b8b..c510603 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.KeyguardManager.ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
@@ -3383,6 +3384,15 @@
      */
     void lockAllProfileTasks(@UserIdInt int userId) {
         forAllLeafTasks(task -> {
+            final ActivityRecord top = task.topRunningActivity();
+            if (top != null && !top.finishing
+                    && ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER.equals(top.intent.getAction())
+                    && top.packageName.equals(
+                            mService.getSysUiServiceComponentLocked().getPackageName())) {
+                // Do nothing since the task is already secure by sysui.
+                return;
+            }
+
             if (task.getActivity(activity -> !activity.finishing && activity.mUserId == userId)
                     != null) {
                 mService.getTaskChangeNotificationController().notifyTaskProfileLocked(
diff --git a/services/core/java/com/android/server/wm/ShellRoot.java b/services/core/java/com/android/server/wm/ShellRoot.java
index b56e76d..be6a5d2 100644
--- a/services/core/java/com/android/server/wm/ShellRoot.java
+++ b/services/core/java/com/android/server/wm/ShellRoot.java
@@ -50,6 +50,7 @@
     private SurfaceControl mSurfaceControl = null;
     private IWindow mAccessibilityWindow;
     private IBinder.DeathRecipient mAccessibilityWindowDeath;
+    private int mWindowType;
 
     ShellRoot(@NonNull IWindow client, @NonNull DisplayContent dc,
             @WindowManager.ShellRootLayer final int shellRootLayer) {
@@ -64,19 +65,18 @@
             return;
         }
         mClient = client;
-        int windowType;
         switch (shellRootLayer) {
             case SHELL_ROOT_LAYER_DIVIDER:
-                windowType = TYPE_DOCK_DIVIDER;
+                mWindowType = TYPE_DOCK_DIVIDER;
                 break;
             case SHELL_ROOT_LAYER_PIP:
-                windowType = TYPE_APPLICATION_OVERLAY;
+                mWindowType = TYPE_APPLICATION_OVERLAY;
                 break;
             default:
                 throw new IllegalArgumentException(shellRootLayer
                         + " is not an acceptable shell root layer.");
         }
-        mToken = new WindowToken.Builder(dc.mWmService, client.asBinder(), windowType)
+        mToken = new WindowToken.Builder(dc.mWmService, client.asBinder(), mWindowType)
                 .setDisplayContent(dc)
                 .setPersistOnEmpty(true)
                 .setOwnerCanManageAppTokens(true)
@@ -89,6 +89,10 @@
         mToken.getPendingTransaction().show(mSurfaceControl);
     }
 
+    int getWindowType() {
+        return mWindowType;
+    }
+
     void clear() {
         if (mClient != null) {
             mClient.asBinder().unlinkToDeath(mDeathRecipient, 0);
diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java
index 59de43a..3f9c93b 100644
--- a/services/core/java/com/android/server/wm/StartingData.java
+++ b/services/core/java/com/android/server/wm/StartingData.java
@@ -26,6 +26,12 @@
     protected final WindowManagerService mService;
     protected final int mTypeParams;
 
+    /**
+     * Tell whether the launching activity should use
+     * {@link android.view.WindowManager.LayoutParams#SOFT_INPUT_IS_FORWARD_NAVIGATION}.
+     */
+    boolean mIsTransitionForward;
+
     protected StartingData(WindowManagerService service, int typeParams) {
         mService = service;
         mTypeParams = typeParams;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 565f99f..325f10f 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -974,10 +974,7 @@
     }
 
     void removeIfPossible(String reason) {
-        final boolean isRootTask = isRootTask();
-        if (!isRootTask) {
-            mAtmService.getLockTaskController().clearLockedTask(this);
-        }
+        mAtmService.getLockTaskController().clearLockedTask(this);
         if (shouldDeferRemoval()) {
             if (DEBUG_ROOT_TASK) Slog.i(TAG,
                     "removeTask:" + reason + " deferring removing taskId=" + mTaskId);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 9ffb2b1..0000f95 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -443,7 +443,8 @@
         } else {
             excludeLayers = new SurfaceControl[0];
         }
-        builder.setHasImeSurface(!excludeIme && imeWindow != null && imeWindow.isDrawn());
+        builder.setHasImeSurface(!excludeIme && imeWindow != null && imeWindow.isVisible());
+
         final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
                 SurfaceControl.captureLayersExcluding(
                         task.getSurfaceControl(), mTmpRect, scaleFraction,
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e9dd521..2c8fceb 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2463,6 +2463,17 @@
             configChanged = displayContent.updateOrientation();
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
 
+            final DisplayInfo rotatedDisplayInfo =
+                    win.mToken.getFixedRotationTransformDisplayInfo();
+            if (rotatedDisplayInfo != null) {
+                outSurfaceControl.setTransformHint(rotatedDisplayInfo.rotation);
+            } else {
+                // We have to update the transform hint of display here, but we need to get if from
+                // SurfaceFlinger, so set it as rotation of display for most cases, then
+                // SurfaceFlinger would still update the transform hint of display in next frame.
+                outSurfaceControl.setTransformHint(displayContent.getDisplayInfo().rotation);
+            }
+
             if (toBeDisplayed && win.mIsWallpaper) {
                 displayContent.mWallpaperController.updateWallpaperOffset(win, false /* sync */);
             }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 07ac73b..bba50a7 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2568,6 +2568,7 @@
     private void setupWindowForRemoveOnExit() {
         mRemoveOnExit = true;
         setDisplayLayoutNeeded();
+        getDisplayContent().getDisplayPolicy().removeWindowLw(this);
         // Request a focus update as this window's input channel is already gone. Otherwise
         // we could have no focused window in input manager.
         final boolean focusChanged = mWmService.updateFocusedWindowLocked(
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 9a6e444..4471f6c 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -683,8 +683,9 @@
         }
 
         // We don't apply animation for application main window here since this window type
-        // should be controlled by AppWindowToken in general.
-        if (mAttrType != TYPE_BASE_APPLICATION) {
+        // should be controlled by ActivityRecord in general. Wallpaper is also excluded because
+        // WallpaperController should handle it.
+        if (mAttrType != TYPE_BASE_APPLICATION && !mIsWallpaper) {
             applyAnimationLocked(transit, true);
         }
 
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index e13ef99..a94ad4a 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -997,10 +997,7 @@
 }
 
 void NativeInputManager::displayRemoved(JNIEnv* env, int32_t displayId) {
-    // Set an empty list to remove all handles from the specific display.
-    std::vector<sp<InputWindowHandle>> windowHandles;
-    mInputManager->getDispatcher()->setInputWindows({{displayId, windowHandles}});
-    mInputManager->getDispatcher()->setFocusedApplication(displayId, nullptr);
+    mInputManager->getDispatcher()->displayRemoved(displayId);
 }
 
 void NativeInputManager::setFocusedApplication(JNIEnv* env, int32_t displayId,
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index 698e3f7..9029fe7 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -41,8 +41,18 @@
 static jmethodID sMethodIdOnComplete;
 static jclass sFrequencyMappingClass;
 static jmethodID sFrequencyMappingCtor;
-static jclass sVibratorInfoClass;
-static jmethodID sVibratorInfoCtor;
+static struct {
+    jmethodID setCapabilities;
+    jmethodID setSupportedEffects;
+    jmethodID setSupportedBraking;
+    jmethodID setPwlePrimitiveDurationMax;
+    jmethodID setPwleSizeMax;
+    jmethodID setSupportedPrimitive;
+    jmethodID setPrimitiveDelayMax;
+    jmethodID setCompositionSizeMax;
+    jmethodID setQFactor;
+    jmethodID setFrequencyMapping;
+} sVibratorInfoBuilderClassInfo;
 static struct {
     jfieldID id;
     jfieldID scale;
@@ -352,68 +362,88 @@
     wrapper->halCall<void>(alwaysOnDisableFn, "alwaysOnDisable");
 }
 
-static jobject vibratorGetInfo(JNIEnv* env, jclass /* clazz */, jlong ptr,
-                               jfloat suggestedSafeRange) {
+static jboolean vibratorGetInfo(JNIEnv* env, jclass /* clazz */, jlong ptr,
+                                jfloat suggestedSafeRange, jobject vibratorInfoBuilder) {
     VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
     if (wrapper == nullptr) {
         ALOGE("vibratorGetInfo failed because native wrapper was not initialized");
-        return nullptr;
+        return JNI_FALSE;
     }
     vibrator::Info info = wrapper->getVibratorInfo();
 
-    jlong capabilities =
-            static_cast<jlong>(info.capabilities.valueOr(vibrator::Capabilities::NONE));
-    jfloat minFrequency = static_cast<jfloat>(info.minFrequency.valueOr(NAN));
-    jfloat resonantFrequency = static_cast<jfloat>(info.resonantFrequency.valueOr(NAN));
-    jfloat frequencyResolution = static_cast<jfloat>(info.frequencyResolution.valueOr(NAN));
-    jfloat qFactor = static_cast<jfloat>(info.qFactor.valueOr(NAN));
-    jintArray supportedEffects = nullptr;
-    jintArray supportedBraking = nullptr;
-    jintArray supportedPrimitives = nullptr;
-    jintArray primitiveDurations = nullptr;
-    jfloatArray maxAmplitudes = nullptr;
-
+    if (info.capabilities.isOk()) {
+        env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setCapabilities,
+                              static_cast<jlong>(info.capabilities.value()));
+    }
     if (info.supportedEffects.isOk()) {
         std::vector<aidl::Effect> effects = info.supportedEffects.value();
-        supportedEffects = env->NewIntArray(effects.size());
+        jintArray supportedEffects = env->NewIntArray(effects.size());
         env->SetIntArrayRegion(supportedEffects, 0, effects.size(),
                                reinterpret_cast<jint*>(effects.data()));
+        env->CallObjectMethod(vibratorInfoBuilder,
+                              sVibratorInfoBuilderClassInfo.setSupportedEffects, supportedEffects);
     }
     if (info.supportedBraking.isOk()) {
         std::vector<aidl::Braking> braking = info.supportedBraking.value();
-        supportedBraking = env->NewIntArray(braking.size());
+        jintArray supportedBraking = env->NewIntArray(braking.size());
         env->SetIntArrayRegion(supportedBraking, 0, braking.size(),
                                reinterpret_cast<jint*>(braking.data()));
+        env->CallObjectMethod(vibratorInfoBuilder,
+                              sVibratorInfoBuilderClassInfo.setSupportedBraking, supportedBraking);
+    }
+    if (info.pwlePrimitiveDurationMax.isOk()) {
+        env->CallObjectMethod(vibratorInfoBuilder,
+                              sVibratorInfoBuilderClassInfo.setPwlePrimitiveDurationMax,
+                              static_cast<jint>(info.pwlePrimitiveDurationMax.value().count()));
+    }
+    if (info.pwleSizeMax.isOk()) {
+        // Use (pwleMaxSize - 1) to account for a possible extra braking segment added by the
+        // vibratorPerformPwleEffect method.
+        env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setPwleSizeMax,
+                              static_cast<jint>(info.pwleSizeMax.value() - 1));
     }
     if (info.supportedPrimitives.isOk()) {
-        std::vector<aidl::CompositePrimitive> primitives = info.supportedPrimitives.value();
-        supportedPrimitives = env->NewIntArray(primitives.size());
-        env->SetIntArrayRegion(supportedPrimitives, 0, primitives.size(),
-                               reinterpret_cast<jint*>(primitives.data()));
-    }
-    if (info.primitiveDurations.isOk()) {
-        std::vector<int32_t> durations;
-        for (auto duration : info.primitiveDurations.value()) {
-            durations.push_back(duration.count());
+        auto durations = info.primitiveDurations.valueOr({});
+        for (auto& primitive : info.supportedPrimitives.value()) {
+            auto primitiveIdx = static_cast<size_t>(primitive);
+            auto duration = durations.size() > primitiveIdx ? durations[primitiveIdx].count() : 0;
+            env->CallObjectMethod(vibratorInfoBuilder,
+                                  sVibratorInfoBuilderClassInfo.setSupportedPrimitive,
+                                  static_cast<jint>(primitive), static_cast<jint>(duration));
         }
-        primitiveDurations = env->NewIntArray(durations.size());
-        env->SetIntArrayRegion(primitiveDurations, 0, durations.size(),
-                               reinterpret_cast<jint*>(durations.data()));
     }
+    if (info.primitiveDelayMax.isOk()) {
+        env->CallObjectMethod(vibratorInfoBuilder,
+                              sVibratorInfoBuilderClassInfo.setPrimitiveDelayMax,
+                              static_cast<jint>(info.primitiveDelayMax.value().count()));
+    }
+    if (info.compositionSizeMax.isOk()) {
+        env->CallObjectMethod(vibratorInfoBuilder,
+                              sVibratorInfoBuilderClassInfo.setCompositionSizeMax,
+                              static_cast<jint>(info.compositionSizeMax.value()));
+    }
+    if (info.qFactor.isOk()) {
+        env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setQFactor,
+                              static_cast<jfloat>(info.qFactor.value()));
+    }
+
+    jfloat minFrequency = static_cast<jfloat>(info.minFrequency.valueOr(NAN));
+    jfloat resonantFrequency = static_cast<jfloat>(info.resonantFrequency.valueOr(NAN));
+    jfloat frequencyResolution = static_cast<jfloat>(info.frequencyResolution.valueOr(NAN));
+    jfloatArray maxAmplitudes = nullptr;
     if (info.maxAmplitudes.isOk()) {
         std::vector<float> amplitudes = info.maxAmplitudes.value();
         maxAmplitudes = env->NewFloatArray(amplitudes.size());
         env->SetFloatArrayRegion(maxAmplitudes, 0, amplitudes.size(),
                                  reinterpret_cast<jfloat*>(amplitudes.data()));
     }
-
     jobject frequencyMapping = env->NewObject(sFrequencyMappingClass, sFrequencyMappingCtor,
                                               minFrequency, resonantFrequency, frequencyResolution,
                                               suggestedSafeRange, maxAmplitudes);
+    env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setFrequencyMapping,
+                          frequencyMapping);
 
-    return env->NewObject(sVibratorInfoClass, sVibratorInfoCtor, wrapper->getVibratorId(),
-                          capabilities, supportedEffects, supportedBraking, supportedPrimitives,
-                          primitiveDurations, qFactor, frequencyMapping);
+    return info.checkAndLogFailure("vibratorGetInfo") ? JNI_FALSE : JNI_TRUE;
 }
 
 static const JNINativeMethod method_table[] = {
@@ -433,7 +463,7 @@
         {"setExternalControl", "(JZ)V", (void*)vibratorSetExternalControl},
         {"alwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable},
         {"alwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable},
-        {"getInfo", "(JF)Landroid/os/VibratorInfo;", (void*)vibratorGetInfo},
+        {"getInfo", "(JFLandroid/os/VibratorInfo$Builder;)Z", (void*)vibratorGetInfo},
 };
 
 int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env) {
@@ -459,11 +489,38 @@
     sFrequencyMappingClass = static_cast<jclass>(env->NewGlobalRef(frequencyMappingClass));
     sFrequencyMappingCtor = GetMethodIDOrDie(env, sFrequencyMappingClass, "<init>", "(FFFF[F)V");
 
-    jclass vibratorInfoClass = FindClassOrDie(env, "android/os/VibratorInfo");
-    sVibratorInfoClass = (jclass)env->NewGlobalRef(vibratorInfoClass);
-    sVibratorInfoCtor =
-            GetMethodIDOrDie(env, sVibratorInfoClass, "<init>",
-                             "(IJ[I[I[I[IFLandroid/os/VibratorInfo$FrequencyMapping;)V");
+    jclass vibratorInfoBuilderClass = FindClassOrDie(env, "android/os/VibratorInfo$Builder");
+    sVibratorInfoBuilderClassInfo.setCapabilities =
+            GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setCapabilities",
+                             "(J)Landroid/os/VibratorInfo$Builder;");
+    sVibratorInfoBuilderClassInfo.setSupportedEffects =
+            GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setSupportedEffects",
+                             "([I)Landroid/os/VibratorInfo$Builder;");
+    sVibratorInfoBuilderClassInfo.setSupportedBraking =
+            GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setSupportedBraking",
+                             "([I)Landroid/os/VibratorInfo$Builder;");
+    sVibratorInfoBuilderClassInfo.setPwlePrimitiveDurationMax =
+            GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setPwlePrimitiveDurationMax",
+                             "(I)Landroid/os/VibratorInfo$Builder;");
+    sVibratorInfoBuilderClassInfo.setPwleSizeMax =
+            GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setPwleSizeMax",
+                             "(I)Landroid/os/VibratorInfo$Builder;");
+    sVibratorInfoBuilderClassInfo.setSupportedPrimitive =
+            GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setSupportedPrimitive",
+                             "(II)Landroid/os/VibratorInfo$Builder;");
+    sVibratorInfoBuilderClassInfo.setPrimitiveDelayMax =
+            GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setPrimitiveDelayMax",
+                             "(I)Landroid/os/VibratorInfo$Builder;");
+    sVibratorInfoBuilderClassInfo.setCompositionSizeMax =
+            GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setCompositionSizeMax",
+                             "(I)Landroid/os/VibratorInfo$Builder;");
+    sVibratorInfoBuilderClassInfo.setQFactor =
+            GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setQFactor",
+                             "(F)Landroid/os/VibratorInfo$Builder;");
+    sVibratorInfoBuilderClassInfo.setFrequencyMapping =
+            GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setFrequencyMapping",
+                             "(Landroid/os/VibratorInfo$FrequencyMapping;)"
+                             "Landroid/os/VibratorInfo$Builder;");
 
     return jniRegisterNativeMethods(env,
                                     "com/android/server/vibrator/VibratorController$NativeWrapper",
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 3e2e37c..82aaa61 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -77,6 +77,10 @@
                 <xs:annotation name="final"/>
             </xs:element>
             <xs:element name="timing" type="hbmTiming" minOccurs="1" maxOccurs="1"/>
+            <xs:element type="refreshRateRange" name="refreshRate" minOccurs="0" maxOccurs="1">
+                <xs:annotation name="nullable"/>
+                <xs:annotation name="final"/>
+            </xs:element>
         </xs:all>
         <xs:attribute name="enabled" type="xs:boolean" use="optional"/>
     </xs:complexType>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index d40854a..6e2e362 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -44,10 +44,12 @@
     ctor public HighBrightnessMode();
     method public boolean getEnabled();
     method @NonNull public final java.math.BigDecimal getMinimumLux_all();
+    method @Nullable public final com.android.server.display.config.RefreshRateRange getRefreshRate_all();
     method public com.android.server.display.config.HbmTiming getTiming_all();
     method @NonNull public final java.math.BigDecimal getTransitionPoint_all();
     method public void setEnabled(boolean);
     method public final void setMinimumLux_all(@NonNull java.math.BigDecimal);
+    method public final void setRefreshRate_all(@Nullable com.android.server.display.config.RefreshRateRange);
     method public void setTiming_all(com.android.server.display.config.HbmTiming);
     method public final void setTransitionPoint_all(@NonNull java.math.BigDecimal);
   }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 25d3769..78ad59f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -8030,8 +8030,6 @@
             inForeground = true;
             receiverComponent = resolveDelegateReceiver(
                 DELEGATION_SECURITY_LOGGING, action, userId);
-            // STOPSHIP(b/185004808): remove excessive log.
-            Slogf.d(LOG_TAG, "Delegate for security logs broadcast: " + receiverComponent);
         }
         if (receiverComponent == null) {
             receiverComponent = getOwnerComponent(userId);
@@ -17567,10 +17565,15 @@
 
     @Override
     public boolean isUsbDataSignalingEnabled(String packageName) {
+        final CallerIdentity caller = getCallerIdentity(packageName);
         synchronized (getLockObject()) {
-            final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(
-                    getCallerIdentity(packageName));
-            return admin.mUsbDataSignalingEnabled;
+            // If the caller is an admin, return the policy set by itself. Otherwise
+            // return the device-wide policy.
+            if (isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)) {
+                return getProfileOwnerOrDeviceOwnerLocked(caller).mUsbDataSignalingEnabled;
+            } else {
+                return isUsbDataSignalingEnabledInternalLocked();
+            }
         }
     }
 
@@ -17580,12 +17583,16 @@
         Preconditions.checkCallAuthorization(isSystemUid(caller));
 
         synchronized (getLockObject()) {
-            final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
-                    UserHandle.USER_SYSTEM);
-            return admin == null || admin.mUsbDataSignalingEnabled;
+            return isUsbDataSignalingEnabledInternalLocked();
         }
     }
 
+    private boolean isUsbDataSignalingEnabledInternalLocked() {
+        final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
+                UserHandle.USER_SYSTEM);
+        return admin == null || admin.mUsbDataSignalingEnabled;
+    }
+
     @Override
     public boolean canUsbDataSignalingBeDisabled() {
         return mInjector.binderWithCleanCallingIdentity(() ->
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
index 0741c81b..c29de90 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
@@ -64,7 +64,7 @@
         mLastForceNanos = System.nanoTime();
     }
 
-    private static final boolean DEBUG = true;  // STOPSHIP if true.
+    private static final boolean DEBUG = false;  // STOPSHIP if true.
     private static final String TAG = "SecurityLogMonitor";
     /**
      * Each log entry can hold up to 4K bytes (but as of {@link android.os.Build.VERSION_CODES#N}
@@ -427,7 +427,7 @@
         while (!Thread.currentThread().isInterrupted()) {
             try {
                 final boolean force = mForceSemaphore.tryAcquire(POLLING_INTERVAL_MS, MILLISECONDS);
-                if (DEBUG) Slog.d(TAG, "Retrieving next batch, force=" + force);
+
                 getNextBatch(newLogs);
 
                 mLock.lockInterruptibly();
@@ -469,11 +469,6 @@
                 return;
             }
             final int logSize = mPendingLogs.size();
-            if (DEBUG) {
-                Slog.d(TAG, String.format(
-                        "notifyDeviceOwnerOrProfileOwnerIfNeeded, size: %d now: %d next: %d",
-                        logSize, SystemClock.elapsedRealtime(), mNextAllowedRetrievalTimeMillis));
-            }
             if (logSize >= BUFFER_ENTRIES_NOTIFICATION_LEVEL || (force && logSize > 0)) {
                 // Allow DO to retrieve logs if too many pending logs or if forced.
                 if (!mAllowedToRetrieve) {
@@ -512,7 +507,6 @@
         synchronized (mForceSemaphore) {
             final long toWaitNanos = mLastForceNanos + FORCE_FETCH_THROTTLE_NS - nowNanos;
             if (toWaitNanos > 0) {
-                if (DEBUG) Slog.d(TAG, "Forcing security logs throttled");
                 return NANOSECONDS.toMillis(toWaitNanos) + 1; // Round up.
             }
             mLastForceNanos = nowNanos;
@@ -521,7 +515,6 @@
             if (mForceSemaphore.availablePermits() == 0) {
                 mForceSemaphore.release();
             }
-            if (DEBUG) Slog.d(TAG, "Forcing security logs semaphore released");
             return 0;
         }
     }
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 8352d26..a822257 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -37,15 +37,7 @@
     name: "services.net-module-wifi",
     srcs: [
         ":framework-services-net-module-wifi-shared-srcs",
-        ":net-module-utils-srcs",
         ":net-utils-services-common-srcs",
-        "java/android/net/ip/IpClientCallbacks.java",
-        "java/android/net/ip/IpClientManager.java",
-        "java/android/net/ip/IpClientUtil.java",
-        "java/android/net/util/KeepalivePacketDataUtil.java",
-        "java/android/net/util/NetworkConstants.java",
-        "java/android/net/IpMemoryStore.java",
-        "java/android/net/NetworkMonitorManager.java",
     ],
     sdk_version: "module_current",
     min_sdk_version: "30",
@@ -88,11 +80,7 @@
 filegroup {
     name: "services-connectivity-shared-srcs",
     srcs: [
-        // TODO: move to networkstack-client
-        "java/android/net/IpMemoryStore.java",
-        "java/android/net/NetworkMonitorManager.java",
         // TODO: move to libs/net
-        "java/android/net/util/KeepalivePacketDataUtil.java",
         "java/android/net/util/NetworkConstants.java",
     ],
 }
diff --git a/services/net/java/android/net/IpMemoryStore.java b/services/net/java/android/net/IpMemoryStore.java
deleted file mode 100644
index 8df2e0d..0000000
--- a/services/net/java/android/net/IpMemoryStore.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.net.networkstack.ModuleNetworkStackClient;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.Consumer;
-
-/**
- * Manager class used to communicate with the ip memory store service in the network stack,
- * which is running in a separate module.
- * @hide
-*/
-public class IpMemoryStore extends IpMemoryStoreClient {
-    private static final String TAG = IpMemoryStore.class.getSimpleName();
-    @NonNull private final CompletableFuture<IIpMemoryStore> mService;
-    @NonNull private final AtomicReference<CompletableFuture<IIpMemoryStore>> mTailNode;
-
-    public IpMemoryStore(@NonNull final Context context) {
-        super(context);
-        mService = new CompletableFuture<>();
-        mTailNode = new AtomicReference<CompletableFuture<IIpMemoryStore>>(mService);
-        getModuleNetworkStackClient(context).fetchIpMemoryStore(
-                new IIpMemoryStoreCallbacks.Stub() {
-                    @Override
-                    public void onIpMemoryStoreFetched(@NonNull final IIpMemoryStore memoryStore) {
-                        mService.complete(memoryStore);
-                    }
-
-                    @Override
-                    public int getInterfaceVersion() {
-                        return this.VERSION;
-                    }
-
-                    @Override
-                    public String getInterfaceHash() {
-                        return this.HASH;
-                    }
-                });
-    }
-
-    /*
-     *  If the IpMemoryStore is ready, this function will run the request synchronously.
-     *  Otherwise, it will enqueue the requests for execution immediately after the
-     *  service becomes ready. The requests are guaranteed to be executed in the order
-     *  they are sumbitted.
-     */
-    @Override
-    protected void runWhenServiceReady(Consumer<IIpMemoryStore> cb) throws ExecutionException {
-        mTailNode.getAndUpdate(future -> future.handle((store, exception) -> {
-            if (exception != null) {
-                // this should never happens since we also catch the exception below
-                Log.wtf(TAG, "Error fetching IpMemoryStore", exception);
-                return store;
-            }
-
-            try {
-                cb.accept(store);
-            } catch (Exception e) {
-                Log.wtf(TAG, "Exception occured: " + e.getMessage());
-            }
-            return store;
-        }));
-    }
-
-    @VisibleForTesting
-    protected ModuleNetworkStackClient getModuleNetworkStackClient(Context context) {
-        return ModuleNetworkStackClient.getInstance(context);
-    }
-
-    /** Gets an instance of the memory store */
-    @NonNull
-    public static IpMemoryStore getMemoryStore(final Context context) {
-        return new IpMemoryStore(context);
-    }
-}
diff --git a/services/net/java/android/net/NetworkMonitorManager.java b/services/net/java/android/net/NetworkMonitorManager.java
deleted file mode 100644
index 0f66981..0000000
--- a/services/net/java/android/net/NetworkMonitorManager.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import android.annotation.Hide;
-import android.annotation.NonNull;
-import android.os.Binder;
-import android.os.RemoteException;
-import android.util.Log;
-
-/**
- * A convenience wrapper for INetworkMonitor.
- *
- * Wraps INetworkMonitor calls, making them a bit more friendly to use. Currently handles:
- * - Clearing calling identity
- * - Ignoring RemoteExceptions
- * - Converting to stable parcelables
- *
- * By design, all methods on INetworkMonitor are asynchronous oneway IPCs and are thus void. All the
- * wrapper methods in this class return a boolean that callers can use to determine whether
- * RemoteException was thrown.
- */
-@Hide
-public class NetworkMonitorManager {
-
-    @NonNull private final INetworkMonitor mNetworkMonitor;
-    @NonNull private final String mTag;
-
-    public NetworkMonitorManager(@NonNull INetworkMonitor networkMonitorManager,
-            @NonNull String tag) {
-        mNetworkMonitor = networkMonitorManager;
-        mTag = tag;
-    }
-
-    public NetworkMonitorManager(@NonNull INetworkMonitor networkMonitorManager) {
-        this(networkMonitorManager, NetworkMonitorManager.class.getSimpleName());
-    }
-
-    private void log(String s, Throwable e) {
-        Log.e(mTag, s, e);
-    }
-
-    // CHECKSTYLE:OFF Generated code
-
-    public boolean start() {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mNetworkMonitor.start();
-            return true;
-        } catch (RemoteException e) {
-            log("Error in start", e);
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    public boolean launchCaptivePortalApp() {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mNetworkMonitor.launchCaptivePortalApp();
-            return true;
-        } catch (RemoteException e) {
-            log("Error in launchCaptivePortalApp", e);
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    public boolean notifyCaptivePortalAppFinished(int response) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mNetworkMonitor.notifyCaptivePortalAppFinished(response);
-            return true;
-        } catch (RemoteException e) {
-            log("Error in notifyCaptivePortalAppFinished", e);
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    public boolean setAcceptPartialConnectivity() {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mNetworkMonitor.setAcceptPartialConnectivity();
-            return true;
-        } catch (RemoteException e) {
-            log("Error in setAcceptPartialConnectivity", e);
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    public boolean forceReevaluation(int uid) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mNetworkMonitor.forceReevaluation(uid);
-            return true;
-        } catch (RemoteException e) {
-            log("Error in forceReevaluation", e);
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    public boolean notifyPrivateDnsChanged(PrivateDnsConfigParcel config) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mNetworkMonitor.notifyPrivateDnsChanged(config);
-            return true;
-        } catch (RemoteException e) {
-            log("Error in notifyPrivateDnsChanged", e);
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    public boolean notifyDnsResponse(int returnCode) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mNetworkMonitor.notifyDnsResponse(returnCode);
-            return true;
-        } catch (RemoteException e) {
-            log("Error in notifyDnsResponse", e);
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    public boolean notifyNetworkConnected(LinkProperties lp, NetworkCapabilities nc) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mNetworkMonitor.notifyNetworkConnected(lp, nc);
-            return true;
-        } catch (RemoteException e) {
-            log("Error in notifyNetworkConnected", e);
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    public boolean notifyNetworkDisconnected() {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mNetworkMonitor.notifyNetworkDisconnected();
-            return true;
-        } catch (RemoteException e) {
-            log("Error in notifyNetworkDisconnected", e);
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    public boolean notifyLinkPropertiesChanged(LinkProperties lp) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mNetworkMonitor.notifyLinkPropertiesChanged(lp);
-            return true;
-        } catch (RemoteException e) {
-            log("Error in notifyLinkPropertiesChanged", e);
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    public boolean notifyNetworkCapabilitiesChanged(NetworkCapabilities nc) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mNetworkMonitor.notifyNetworkCapabilitiesChanged(nc);
-            return true;
-        } catch (RemoteException e) {
-            log("Error in notifyNetworkCapabilitiesChanged", e);
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    // CHECKSTYLE:ON Generated code
-}
diff --git a/services/net/java/android/net/ip/IpClientCallbacks.java b/services/net/java/android/net/ip/IpClientCallbacks.java
deleted file mode 100644
index b17fcaa..0000000
--- a/services/net/java/android/net/ip/IpClientCallbacks.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ip;
-
-import android.net.DhcpResultsParcelable;
-import android.net.Layer2PacketParcelable;
-import android.net.LinkProperties;
-
-import java.util.List;
-
-/**
- * Callbacks for handling IpClient events.
- *
- * This is a convenience class to allow clients not to override all methods of IIpClientCallbacks,
- * and avoid unparceling arguments.
- * These methods are called asynchronously on a Binder thread, as IpClient lives in a different
- * process.
- * @hide
- */
-public class IpClientCallbacks {
-
-    /**
-     * Callback called upon IpClient creation.
-     *
-     * @param ipClient The Binder token to communicate with IpClient.
-     */
-    public void onIpClientCreated(IIpClient ipClient) {}
-
-    /**
-     * Callback called prior to DHCP discovery/renewal.
-     *
-     * <p>In order to receive onPreDhcpAction(), call #withPreDhcpAction() when constructing a
-     * ProvisioningConfiguration.
-     *
-     * <p>Implementations of onPreDhcpAction() must call IpClient#completedPreDhcpAction() to
-     * indicate that DHCP is clear to proceed.
-      */
-    public void onPreDhcpAction() {}
-
-    /**
-     * Callback called after DHCP discovery/renewal.
-     */
-    public void onPostDhcpAction() {}
-
-    /**
-     * Callback called when new DHCP results are available.
-     *
-     * <p>This is purely advisory and not an indication of provisioning success or failure.  This is
-     * only here for callers that want to expose DHCPv4 results to other APIs
-     * (e.g., WifiInfo#setInetAddress).
-     *
-     * <p>DHCPv4 or static IPv4 configuration failure or success can be determined by whether or not
-     * the passed-in DhcpResults object is null.
-     */
-    public void onNewDhcpResults(DhcpResultsParcelable dhcpResults) {
-        // In general callbacks would not use a parcelable directly (DhcpResultsParcelable), and
-        // would use a wrapper instead, because of the lack of safety of stable parcelables. But
-        // there are already two classes in the tree for DHCP information: DhcpInfo and DhcpResults,
-        // and neither of them exposes an appropriate API (they are bags of mutable fields and can't
-        // be changed because they are public API and @UnsupportedAppUsage, being no better than the
-        // stable parcelable). Adding a third class would cost more than the gain considering that
-        // the only client of this callback is WiFi, which will end up converting the results to
-        // DhcpInfo anyway.
-    }
-
-    /**
-     * Indicates that provisioning was successful.
-     */
-    public void onProvisioningSuccess(LinkProperties newLp) {}
-
-    /**
-     * Indicates that provisioning failed.
-     */
-    public void onProvisioningFailure(LinkProperties newLp) {}
-
-    /**
-     * Invoked on LinkProperties changes.
-     */
-    public void onLinkPropertiesChange(LinkProperties newLp) {}
-
-    /**Called when the internal IpReachabilityMonitor (if enabled) has
-     * detected the loss of a critical number of required neighbors.
-     */
-    public void onReachabilityLost(String logMsg) {}
-
-    /**
-     * Called when the IpClient state machine terminates.
-     */
-    public void onQuit() {}
-
-    /**
-     * Called to indicate that a new APF program must be installed to filter incoming packets.
-     */
-    public void installPacketFilter(byte[] filter) {}
-
-    /**
-     * Called to indicate that the APF Program & data buffer must be read asynchronously from the
-     * wifi driver.
-     *
-     * <p>Due to Wifi HAL limitations, the current implementation only supports dumping the entire
-     * buffer. In response to this request, the driver returns the data buffer asynchronously
-     * by sending an IpClient#EVENT_READ_PACKET_FILTER_COMPLETE message.
-     */
-    public void startReadPacketFilter() {}
-
-    /**
-     * If multicast filtering cannot be accomplished with APF, this function will be called to
-     * actuate multicast filtering using another means.
-     */
-    public void setFallbackMulticastFilter(boolean enabled) {}
-
-    /**
-     * Enabled/disable Neighbor Discover offload functionality. This is called, for example,
-     * whenever 464xlat is being started or stopped.
-     */
-    public void setNeighborDiscoveryOffload(boolean enable) {}
-
-    /**
-     * Invoked on starting preconnection process.
-     */
-    public void onPreconnectionStart(List<Layer2PacketParcelable> packets) {}
-}
diff --git a/services/net/java/android/net/ip/IpClientManager.java b/services/net/java/android/net/ip/IpClientManager.java
deleted file mode 100644
index b45405f..0000000
--- a/services/net/java/android/net/ip/IpClientManager.java
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ip;
-
-import android.annotation.Hide;
-import android.annotation.NonNull;
-import android.net.NattKeepalivePacketData;
-import android.net.ProxyInfo;
-import android.net.TcpKeepalivePacketData;
-import android.net.TcpKeepalivePacketDataParcelable;
-import android.net.shared.Layer2Information;
-import android.net.shared.ProvisioningConfiguration;
-import android.net.util.KeepalivePacketDataUtil;
-import android.os.Binder;
-import android.os.RemoteException;
-import android.util.Log;
-
-/**
- * A convenience wrapper for IpClient.
- *
- * Wraps IIpClient calls, making them a bit more friendly to use. Currently handles:
- * - Clearing calling identity
- * - Ignoring RemoteExceptions
- * - Converting to stable parcelables
- *
- * By design, all methods on IIpClient are asynchronous oneway IPCs and are thus void. All the
- * wrapper methods in this class return a boolean that callers can use to determine whether
- * RemoteException was thrown.
- */
-@Hide
-public class IpClientManager {
-    @NonNull private final IIpClient mIpClient;
-    @NonNull private final String mTag;
-
-    public IpClientManager(@NonNull IIpClient ipClient, @NonNull String tag) {
-        mIpClient = ipClient;
-        mTag = tag;
-    }
-
-    public IpClientManager(@NonNull IIpClient ipClient) {
-        this(ipClient, IpClientManager.class.getSimpleName());
-    }
-
-    private void log(String s, Throwable e) {
-        Log.e(mTag, s, e);
-    }
-
-    /**
-     * For clients using {@link ProvisioningConfiguration.Builder#withPreDhcpAction()}, must be
-     * called after {@link IIpClientCallbacks#onPreDhcpAction} to indicate that DHCP is clear to
-     * proceed.
-     */
-    public boolean completedPreDhcpAction() {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mIpClient.completedPreDhcpAction();
-            return true;
-        } catch (RemoteException e) {
-            log("Error completing PreDhcpAction", e);
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    /**
-     * Confirm the provisioning configuration.
-     */
-    public boolean confirmConfiguration() {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mIpClient.confirmConfiguration();
-            return true;
-        } catch (RemoteException e) {
-            log("Error confirming IpClient configuration", e);
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    /**
-     * Indicate that packet filter read is complete.
-     */
-    public boolean readPacketFilterComplete(byte[] data) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mIpClient.readPacketFilterComplete(data);
-            return true;
-        } catch (RemoteException e) {
-            log("Error notifying IpClient of packet filter read", e);
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    /**
-     * Shut down this IpClient instance altogether.
-     */
-    public boolean shutdown() {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mIpClient.shutdown();
-            return true;
-        } catch (RemoteException e) {
-            log("Error shutting down IpClient", e);
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    /**
-     * Start provisioning with the provided parameters.
-     */
-    public boolean startProvisioning(ProvisioningConfiguration prov) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mIpClient.startProvisioning(prov.toStableParcelable());
-            return true;
-        } catch (RemoteException e) {
-            log("Error starting IpClient provisioning", e);
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    /**
-     * Stop this IpClient.
-     *
-     * <p>This does not shut down the StateMachine itself, which is handled by {@link #shutdown()}.
-     */
-    public boolean stop() {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mIpClient.stop();
-            return true;
-        } catch (RemoteException e) {
-            log("Error stopping IpClient", e);
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    /**
-     * Set the TCP buffer sizes to use.
-     *
-     * This may be called, repeatedly, at any time before or after a call to
-     * #startProvisioning(). The setting is cleared upon calling #stop().
-     */
-    public boolean setTcpBufferSizes(String tcpBufferSizes) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mIpClient.setTcpBufferSizes(tcpBufferSizes);
-            return true;
-        } catch (RemoteException e) {
-            log("Error setting IpClient TCP buffer sizes", e);
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    /**
-     * Set the HTTP Proxy configuration to use.
-     *
-     * This may be called, repeatedly, at any time before or after a call to
-     * #startProvisioning(). The setting is cleared upon calling #stop().
-     */
-    public boolean setHttpProxy(ProxyInfo proxyInfo) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mIpClient.setHttpProxy(proxyInfo);
-            return true;
-        } catch (RemoteException e) {
-            log("Error setting IpClient proxy", e);
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    /**
-     * Enable or disable the multicast filter.  Attempts to use APF to accomplish the filtering,
-     * if not, Callback.setFallbackMulticastFilter() is called.
-     */
-    public boolean setMulticastFilter(boolean enabled) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mIpClient.setMulticastFilter(enabled);
-            return true;
-        } catch (RemoteException e) {
-            log("Error setting multicast filter", e);
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    /**
-     * Add a TCP keepalive packet filter before setting up keepalive offload.
-     */
-    public boolean addKeepalivePacketFilter(int slot, TcpKeepalivePacketData pkt) {
-        return addKeepalivePacketFilter(slot, KeepalivePacketDataUtil.toStableParcelable(pkt));
-    }
-
-    /**
-     * Add a TCP keepalive packet filter before setting up keepalive offload.
-     * @deprecated This method is for use on pre-S platforms where TcpKeepalivePacketData is not
-     *             system API. On newer platforms use
-     *             addKeepalivePacketFilter(int, TcpKeepalivePacketData) instead.
-     */
-    @Deprecated
-    public boolean addKeepalivePacketFilter(int slot, TcpKeepalivePacketDataParcelable pkt) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mIpClient.addKeepalivePacketFilter(slot, pkt);
-            return true;
-        } catch (RemoteException e) {
-            log("Error adding Keepalive Packet Filter ", e);
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    /**
-     * Add a NAT-T keepalive packet filter before setting up keepalive offload.
-     */
-    public boolean addKeepalivePacketFilter(int slot, NattKeepalivePacketData pkt) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mIpClient.addNattKeepalivePacketFilter(
-                    slot, KeepalivePacketDataUtil.toStableParcelable(pkt));
-            return true;
-        } catch (RemoteException e) {
-            log("Error adding NAT-T Keepalive Packet Filter ", e);
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    /**
-     * Remove a keepalive packet filter after stopping keepalive offload.
-     */
-    public boolean removeKeepalivePacketFilter(int slot) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mIpClient.removeKeepalivePacketFilter(slot);
-            return true;
-        } catch (RemoteException e) {
-            log("Error removing Keepalive Packet Filter ", e);
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    /**
-     * Set the L2 key and group hint for storing info into the memory store.
-     */
-    public boolean setL2KeyAndGroupHint(String l2Key, String groupHint) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mIpClient.setL2KeyAndGroupHint(l2Key, groupHint);
-            return true;
-        } catch (RemoteException e) {
-            log("Failed setL2KeyAndGroupHint", e);
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    /**
-     * Notify IpClient that preconnection is complete and that the link is ready for use.
-     * The success parameter indicates whether the packets passed in by 'onPreconnectionStart'
-     * were successfully sent to the network or not.
-     */
-    public boolean notifyPreconnectionComplete(boolean success) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mIpClient.notifyPreconnectionComplete(success);
-            return true;
-        } catch (RemoteException e) {
-            log("Error notifying IpClient Preconnection completed", e);
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    /**
-     * Update the bssid, L2 key and group hint layer2 information.
-     */
-    public boolean updateLayer2Information(Layer2Information info) {
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mIpClient.updateLayer2Information(info.toStableParcelable());
-            return true;
-        } catch (RemoteException e) {
-            log("Error updating layer2 information", e);
-            return false;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-}
diff --git a/services/net/java/android/net/ip/IpClientUtil.java b/services/net/java/android/net/ip/IpClientUtil.java
deleted file mode 100644
index 426614e..0000000
--- a/services/net/java/android/net/ip/IpClientUtil.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ip;
-
-import android.content.Context;
-import android.net.DhcpResultsParcelable;
-import android.net.Layer2PacketParcelable;
-import android.net.LinkProperties;
-import android.net.networkstack.ModuleNetworkStackClient;
-import android.os.ConditionVariable;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.List;
-
-
-/**
- * Utilities and wrappers to simplify communication with IpClient, which lives in the NetworkStack
- * process.
- *
- * @hide
- */
-public class IpClientUtil {
-    // TODO: remove with its callers
-    public static final String DUMP_ARG = "ipclient";
-
-    /**
-     * Subclass of {@link IpClientCallbacks} allowing clients to block until provisioning is
-     * complete with {@link WaitForProvisioningCallbacks#waitForProvisioning()}.
-     */
-    public static class WaitForProvisioningCallbacks extends IpClientCallbacks {
-        private final ConditionVariable mCV = new ConditionVariable();
-        private LinkProperties mCallbackLinkProperties;
-
-        /**
-         * Block until either {@link #onProvisioningSuccess(LinkProperties)} or
-         * {@link #onProvisioningFailure(LinkProperties)} is called.
-         */
-        public LinkProperties waitForProvisioning() {
-            mCV.block();
-            return mCallbackLinkProperties;
-        }
-
-        @Override
-        public void onProvisioningSuccess(LinkProperties newLp) {
-            mCallbackLinkProperties = newLp;
-            mCV.open();
-        }
-
-        @Override
-        public void onProvisioningFailure(LinkProperties newLp) {
-            mCallbackLinkProperties = null;
-            mCV.open();
-        }
-    }
-
-    /**
-     * Create a new IpClient.
-     *
-     * <p>This is a convenience method to allow clients to use {@link IpClientCallbacks} instead of
-     * {@link IIpClientCallbacks}.
-     * @see {@link ModuleNetworkStackClient#makeIpClient(String, IIpClientCallbacks)}
-     */
-    public static void makeIpClient(Context context, String ifName, IpClientCallbacks callback) {
-        ModuleNetworkStackClient.getInstance(context)
-                .makeIpClient(ifName, new IpClientCallbacksProxy(callback));
-    }
-
-    /**
-     * Wrapper to relay calls from {@link IIpClientCallbacks} to {@link IpClientCallbacks}.
-     */
-    private static class IpClientCallbacksProxy extends IIpClientCallbacks.Stub {
-        protected final IpClientCallbacks mCb;
-
-        /**
-         * Create a new IpClientCallbacksProxy.
-         */
-        public IpClientCallbacksProxy(IpClientCallbacks cb) {
-            mCb = cb;
-        }
-
-        @Override
-        public void onIpClientCreated(IIpClient ipClient) {
-            mCb.onIpClientCreated(ipClient);
-        }
-
-        @Override
-        public void onPreDhcpAction() {
-            mCb.onPreDhcpAction();
-        }
-
-        @Override
-        public void onPostDhcpAction() {
-            mCb.onPostDhcpAction();
-        }
-
-        // This is purely advisory and not an indication of provisioning
-        // success or failure.  This is only here for callers that want to
-        // expose DHCPv4 results to other APIs (e.g., WifiInfo#setInetAddress).
-        // DHCPv4 or static IPv4 configuration failure or success can be
-        // determined by whether or not the passed-in DhcpResults object is
-        // null or not.
-        @Override
-        public void onNewDhcpResults(DhcpResultsParcelable dhcpResults) {
-            mCb.onNewDhcpResults(dhcpResults);
-        }
-
-        @Override
-        public void onProvisioningSuccess(LinkProperties newLp) {
-            mCb.onProvisioningSuccess(newLp);
-        }
-        @Override
-        public void onProvisioningFailure(LinkProperties newLp) {
-            mCb.onProvisioningFailure(newLp);
-        }
-
-        // Invoked on LinkProperties changes.
-        @Override
-        public void onLinkPropertiesChange(LinkProperties newLp) {
-            mCb.onLinkPropertiesChange(newLp);
-        }
-
-        // Called when the internal IpReachabilityMonitor (if enabled) has
-        // detected the loss of a critical number of required neighbors.
-        @Override
-        public void onReachabilityLost(String logMsg) {
-            mCb.onReachabilityLost(logMsg);
-        }
-
-        // Called when the IpClient state machine terminates.
-        @Override
-        public void onQuit() {
-            mCb.onQuit();
-        }
-
-        // Install an APF program to filter incoming packets.
-        @Override
-        public void installPacketFilter(byte[] filter) {
-            mCb.installPacketFilter(filter);
-        }
-
-        // Asynchronously read back the APF program & data buffer from the wifi driver.
-        // Due to Wifi HAL limitations, the current implementation only supports dumping the entire
-        // buffer. In response to this request, the driver returns the data buffer asynchronously
-        // by sending an IpClient#EVENT_READ_PACKET_FILTER_COMPLETE message.
-        @Override
-        public void startReadPacketFilter() {
-            mCb.startReadPacketFilter();
-        }
-
-        // If multicast filtering cannot be accomplished with APF, this function will be called to
-        // actuate multicast filtering using another means.
-        @Override
-        public void setFallbackMulticastFilter(boolean enabled) {
-            mCb.setFallbackMulticastFilter(enabled);
-        }
-
-        // Enabled/disable Neighbor Discover offload functionality. This is
-        // called, for example, whenever 464xlat is being started or stopped.
-        @Override
-        public void setNeighborDiscoveryOffload(boolean enable) {
-            mCb.setNeighborDiscoveryOffload(enable);
-        }
-
-        // Invoked on starting preconnection process.
-        @Override
-        public void onPreconnectionStart(List<Layer2PacketParcelable> packets) {
-            mCb.onPreconnectionStart(packets);
-        }
-
-        @Override
-        public int getInterfaceVersion() {
-            return this.VERSION;
-        }
-
-        @Override
-        public String getInterfaceHash() {
-            return this.HASH;
-        }
-    }
-
-    /**
-     * Dump logs for the specified IpClient.
-     * TODO: remove callers and delete
-     */
-    public static void dumpIpClient(
-            IIpClient connector, FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println("IpClient logs have moved to dumpsys network_stack");
-    }
-}
diff --git a/services/net/java/android/net/util/DhcpResultsCompatUtil.java b/services/net/java/android/net/util/DhcpResultsCompatUtil.java
deleted file mode 100644
index fce0834..0000000
--- a/services/net/java/android/net/util/DhcpResultsCompatUtil.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.util;
-
-import static android.net.shared.IpConfigurationParcelableUtil.unparcelAddress;
-
-import android.annotation.Nullable;
-import android.net.DhcpResults;
-import android.net.DhcpResultsParcelable;
-
-import java.net.Inet4Address;
-
-/**
- * Compatibility utility for code that still uses DhcpResults.
- *
- * TODO: remove this class when all usages of DhcpResults (including Wifi in AOSP) are removed.
- */
-public class DhcpResultsCompatUtil {
-
-    /**
-     * Convert a DhcpResultsParcelable to DhcpResults.
-     *
-     * contract {
-     *     returns(null) implies p == null
-     *     returnsNotNull() implies p != null
-     * }
-     */
-    @Nullable
-    public static DhcpResults fromStableParcelable(@Nullable DhcpResultsParcelable p) {
-        if (p == null) return null;
-        final DhcpResults results = new DhcpResults(p.baseConfiguration);
-        results.leaseDuration = p.leaseDuration;
-        results.mtu = p.mtu;
-        results.serverAddress = (Inet4Address) unparcelAddress(p.serverAddress);
-        results.vendorInfo = p.vendorInfo;
-        results.serverHostName = p.serverHostName;
-        results.captivePortalApiUrl = p.captivePortalApiUrl;
-        return results;
-    }
-}
diff --git a/services/net/java/android/net/util/KeepalivePacketDataUtil.java b/services/net/java/android/net/util/KeepalivePacketDataUtil.java
deleted file mode 100644
index 5666985..0000000
--- a/services/net/java/android/net/util/KeepalivePacketDataUtil.java
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.util;
-
-import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.InvalidPacketException;
-import android.net.KeepalivePacketData;
-import android.net.NattKeepalivePacketData;
-import android.net.NattKeepalivePacketDataParcelable;
-import android.net.TcpKeepalivePacketData;
-import android.net.TcpKeepalivePacketDataParcelable;
-import android.os.Build;
-import android.system.OsConstants;
-import android.util.Log;
-
-import com.android.net.module.util.IpUtils;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-
-/**
- * Utility class to convert to/from keepalive data parcelables.
- *
- * TODO: move to networkstack-client library when it is moved to frameworks/libs/net.
- * This class cannot go into other shared libraries as it depends on NetworkStack AIDLs.
- * @hide
- */
-public final class KeepalivePacketDataUtil {
-    private static final int IPV4_HEADER_LENGTH = 20;
-    private static final int IPV6_HEADER_LENGTH = 40;
-    private static final int TCP_HEADER_LENGTH = 20;
-
-    private static final String TAG = KeepalivePacketDataUtil.class.getSimpleName();
-
-    /**
-     * Convert a NattKeepalivePacketData to a NattKeepalivePacketDataParcelable.
-     */
-    @NonNull
-    public static NattKeepalivePacketDataParcelable toStableParcelable(
-            @NonNull NattKeepalivePacketData pkt) {
-        final NattKeepalivePacketDataParcelable parcel = new NattKeepalivePacketDataParcelable();
-        final InetAddress srcAddress = pkt.getSrcAddress();
-        final InetAddress dstAddress = pkt.getDstAddress();
-        parcel.srcAddress = srcAddress.getAddress();
-        parcel.srcPort = pkt.getSrcPort();
-        parcel.dstAddress = dstAddress.getAddress();
-        parcel.dstPort = pkt.getDstPort();
-        return parcel;
-    }
-
-    /**
-     * Convert a TcpKeepalivePacketData to a TcpKeepalivePacketDataParcelable.
-     */
-    @NonNull
-    public static TcpKeepalivePacketDataParcelable toStableParcelable(
-            @NonNull TcpKeepalivePacketData pkt) {
-        final TcpKeepalivePacketDataParcelable parcel = new TcpKeepalivePacketDataParcelable();
-        final InetAddress srcAddress = pkt.getSrcAddress();
-        final InetAddress dstAddress = pkt.getDstAddress();
-        parcel.srcAddress = srcAddress.getAddress();
-        parcel.srcPort = pkt.getSrcPort();
-        parcel.dstAddress = dstAddress.getAddress();
-        parcel.dstPort = pkt.getDstPort();
-        parcel.seq = pkt.getTcpSeq();
-        parcel.ack = pkt.getTcpAck();
-        parcel.rcvWnd = pkt.getTcpWindow();
-        parcel.rcvWndScale = pkt.getTcpWindowScale();
-        parcel.tos = pkt.getIpTos();
-        parcel.ttl = pkt.getIpTtl();
-        return parcel;
-    }
-
-    /**
-     * Factory method to create tcp keepalive packet structure.
-     * @hide
-     */
-    public static TcpKeepalivePacketData fromStableParcelable(
-            TcpKeepalivePacketDataParcelable tcpDetails) throws InvalidPacketException {
-        final byte[] packet;
-        try {
-            if ((tcpDetails.srcAddress != null) && (tcpDetails.dstAddress != null)
-                    && (tcpDetails.srcAddress.length == 4 /* V4 IP length */)
-                    && (tcpDetails.dstAddress.length == 4 /* V4 IP length */)) {
-                packet = buildV4Packet(tcpDetails);
-            } else {
-                // TODO: support ipv6
-                throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
-            }
-            return new TcpKeepalivePacketData(
-                    InetAddress.getByAddress(tcpDetails.srcAddress),
-                    tcpDetails.srcPort,
-                    InetAddress.getByAddress(tcpDetails.dstAddress),
-                    tcpDetails.dstPort,
-                    packet,
-                    tcpDetails.seq, tcpDetails.ack, tcpDetails.rcvWnd, tcpDetails.rcvWndScale,
-                    tcpDetails.tos, tcpDetails.ttl);
-        } catch (UnknownHostException e) {
-            throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
-        }
-
-    }
-
-    /**
-     * Build ipv4 tcp keepalive packet, not including the link-layer header.
-     */
-    // TODO : if this code is ever moved to the network stack, factorize constants with the ones
-    // over there.
-    private static byte[] buildV4Packet(TcpKeepalivePacketDataParcelable tcpDetails) {
-        final int length = IPV4_HEADER_LENGTH + TCP_HEADER_LENGTH;
-        ByteBuffer buf = ByteBuffer.allocate(length);
-        buf.order(ByteOrder.BIG_ENDIAN);
-        buf.put((byte) 0x45);                       // IP version and IHL
-        buf.put((byte) tcpDetails.tos);             // TOS
-        buf.putShort((short) length);
-        buf.putInt(0x00004000);                     // ID, flags=DF, offset
-        buf.put((byte) tcpDetails.ttl);             // TTL
-        buf.put((byte) OsConstants.IPPROTO_TCP);
-        final int ipChecksumOffset = buf.position();
-        buf.putShort((short) 0);                    // IP checksum
-        buf.put(tcpDetails.srcAddress);
-        buf.put(tcpDetails.dstAddress);
-        buf.putShort((short) tcpDetails.srcPort);
-        buf.putShort((short) tcpDetails.dstPort);
-        buf.putInt(tcpDetails.seq);                 // Sequence Number
-        buf.putInt(tcpDetails.ack);                 // ACK
-        buf.putShort((short) 0x5010);               // TCP length=5, flags=ACK
-        buf.putShort((short) (tcpDetails.rcvWnd >> tcpDetails.rcvWndScale));   // Window size
-        final int tcpChecksumOffset = buf.position();
-        buf.putShort((short) 0);                    // TCP checksum
-        // URG is not set therefore the urgent pointer is zero.
-        buf.putShort((short) 0);                    // Urgent pointer
-
-        buf.putShort(ipChecksumOffset, com.android.net.module.util.IpUtils.ipChecksum(buf, 0));
-        buf.putShort(tcpChecksumOffset, IpUtils.tcpChecksum(
-                buf, 0, IPV4_HEADER_LENGTH, TCP_HEADER_LENGTH));
-
-        return buf.array();
-    }
-
-    // TODO: add buildV6Packet.
-
-    /**
-     * Get a {@link TcpKeepalivePacketDataParcelable} from {@link KeepalivePacketData}, if the
-     * generic class actually contains TCP keepalive data.
-     *
-     * @deprecated This method is used on R platforms where android.net.TcpKeepalivePacketData was
-     * not yet system API. Newer platforms should use android.net.TcpKeepalivePacketData directly.
-     *
-     * @param data A {@link KeepalivePacketData} that may contain TCP keepalive data.
-     * @return A parcelable containing TCP keepalive data, or null if the input data does not
-     *         contain TCP keepalive data.
-     */
-    @Deprecated
-    @SuppressWarnings("AndroidFrameworkCompatChange") // API version check used to Log.wtf
-    @Nullable
-    public static TcpKeepalivePacketDataParcelable parseTcpKeepalivePacketData(
-            @Nullable KeepalivePacketData data) {
-        if (data == null) return null;
-
-        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
-            Log.wtf(TAG, "parseTcpKeepalivePacketData should not be used after R, use "
-                    + "TcpKeepalivePacketData instead.");
-        }
-
-        // Reconstruct TcpKeepalivePacketData from the packet contained in KeepalivePacketData
-        final ByteBuffer buffer = ByteBuffer.wrap(data.getPacket());
-        buffer.order(ByteOrder.BIG_ENDIAN);
-
-        // Most of the fields are accessible from the KeepalivePacketData superclass: instead of
-        // using Struct to parse everything, just extract the extra fields necessary for
-        // TcpKeepalivePacketData.
-        final int tcpSeq;
-        final int tcpAck;
-        final int wndSize;
-        final int ipTos;
-        final int ttl;
-        try {
-            // This only support IPv4, because TcpKeepalivePacketData only supports IPv4 for R and
-            // below, and this method should not be used on newer platforms.
-            tcpSeq = buffer.getInt(IPV4_HEADER_LENGTH + 4);
-            tcpAck = buffer.getInt(IPV4_HEADER_LENGTH + 8);
-            wndSize = buffer.getShort(IPV4_HEADER_LENGTH + 14);
-            ipTos = buffer.get(1);
-            ttl = buffer.get(8);
-        } catch (IndexOutOfBoundsException e) {
-            return null;
-        }
-
-        final TcpKeepalivePacketDataParcelable p = new TcpKeepalivePacketDataParcelable();
-        p.srcAddress = data.getSrcAddress().getAddress();
-        p.srcPort = data.getSrcPort();
-        p.dstAddress = data.getDstAddress().getAddress();
-        p.dstPort = data.getDstPort();
-        p.seq = tcpSeq;
-        p.ack = tcpAck;
-        // TcpKeepalivePacketData could actually use non-zero wndScale, but this does not affect
-        // actual functionality as generated packets will be the same (no wndScale option added)
-        p.rcvWnd = wndSize;
-        p.rcvWndScale = 0;
-        p.tos = ipTos;
-        p.ttl = ttl;
-        return p;
-    }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index e99113d..acf50b45 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -323,6 +323,8 @@
         when(mPowerManager.newWakeLock(anyInt(), anyString())).thenReturn(mWakeLock);
         doNothing().when(mWakeLock).acquire();
         doNothing().when(mAlarmManager).set(anyInt(), anyLong(), anyString(), any(), any());
+        doNothing().when(mAlarmManager)
+                .setWindow(anyInt(), anyLong(), anyLong(), anyString(), any(), any());
         doReturn(mock(Sensor.class)).when(mSensorManager)
                 .getDefaultSensor(eq(Sensor.TYPE_SIGNIFICANT_MOTION), eq(true));
         doReturn(true).when(mSensorManager).registerListener(any(), any(), anyInt());
@@ -1043,24 +1045,28 @@
         mDeviceIdleController.stepLightIdleStateLocked("testing");
         verifyLightStateConditions(LIGHT_STATE_IDLE);
         inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
-                longThat(l -> l == mConstants.LIGHT_IDLE_TIMEOUT));
+                longThat(l -> l == mConstants.LIGHT_IDLE_TIMEOUT),
+                longThat(l -> l == mConstants.LIGHT_IDLE_TIMEOUT_INITIAL_FLEX));
 
         // Should just alternate between IDLE and IDLE_MAINTENANCE now.
 
         mDeviceIdleController.stepLightIdleStateLocked("testing");
         verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
         inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
-                longThat(l -> l >= mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET));
+                longThat(l -> l >= mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET),
+                longThat(l -> l == mConstants.FLEX_TIME_SHORT));
 
         mDeviceIdleController.stepLightIdleStateLocked("testing");
         verifyLightStateConditions(LIGHT_STATE_IDLE);
         inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
-                longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT));
+                longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT),
+                longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT_INITIAL_FLEX));
 
         mDeviceIdleController.stepLightIdleStateLocked("testing");
         verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
         inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
-                longThat(l -> l >= mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET));
+                longThat(l -> l >= mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET),
+                longThat(l -> l == mConstants.FLEX_TIME_SHORT));
 
         // Test that motion doesn't reset the idle timeout.
         mDeviceIdleController.handleMotionDetectedLocked(50, "test");
@@ -1068,7 +1074,8 @@
         mDeviceIdleController.stepLightIdleStateLocked("testing");
         verifyLightStateConditions(LIGHT_STATE_IDLE);
         inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
-                longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT));
+                longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT),
+                longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT_INITIAL_FLEX));
     }
 
     ///////////////// EXIT conditions ///////////////////
@@ -1824,9 +1831,9 @@
                 .forClass(AlarmManager.OnAlarmListener.class);
         final ArgumentCaptor<AlarmManager.OnAlarmListener> motionRegistrationAlarmListener =
                 ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class);
-        doNothing().when(mAlarmManager).set(anyInt(), anyLong(), eq("DeviceIdleController.motion"),
-                motionAlarmListener.capture(), any());
-        doNothing().when(mAlarmManager).set(anyInt(), anyLong(),
+        doNothing().when(mAlarmManager).setWindow(anyInt(), anyLong(), anyLong(),
+                eq("DeviceIdleController.motion"), motionAlarmListener.capture(), any());
+        doNothing().when(mAlarmManager).setWindow(anyInt(), anyLong(), anyLong(),
                 eq("DeviceIdleController.motion_registration"),
                 motionRegistrationAlarmListener.capture(), any());
 
@@ -1900,9 +1907,9 @@
         mInjector.nowElapsed += mConstants.QUICK_DOZE_DELAY_TIMEOUT;
         final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListener = ArgumentCaptor
                 .forClass(AlarmManager.OnAlarmListener.class);
-        doNothing().when(mAlarmManager)
-                .set(anyInt(), anyLong(), eq("DeviceIdleController.motion"), any(), any());
-        doNothing().when(mAlarmManager).set(anyInt(), anyLong(),
+        doNothing().when(mAlarmManager).setWindow(
+                anyInt(), anyLong(), anyLong(), eq("DeviceIdleController.motion"), any(), any());
+        doNothing().when(mAlarmManager).setWindow(anyInt(), anyLong(), anyLong(),
                 eq("DeviceIdleController.motion_registration"),
                 alarmListener.capture(), any());
         ArgumentCaptor<TriggerEventListener> listenerCaptor =
@@ -1944,9 +1951,9 @@
         mInjector.nowElapsed += mConstants.QUICK_DOZE_DELAY_TIMEOUT;
         final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListener = ArgumentCaptor
                 .forClass(AlarmManager.OnAlarmListener.class);
-        doNothing().when(mAlarmManager)
-                .set(anyInt(), anyLong(), eq("DeviceIdleController.motion"), any(), any());
-        doNothing().when(mAlarmManager).set(anyInt(), anyLong(),
+        doNothing().when(mAlarmManager).setWindow(
+                anyInt(), anyLong(), anyLong(), eq("DeviceIdleController.motion"), any(), any());
+        doNothing().when(mAlarmManager).setWindow(anyInt(), anyLong(), anyLong(),
                 eq("DeviceIdleController.motion_registration"),
                 alarmListener.capture(), any());
         ArgumentCaptor<SensorEventListener> listenerCaptor =
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 280204d..eab1afb 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -1914,11 +1914,11 @@
     public void hasScheduleExactAlarmBinderCallChangeDisabled() throws RemoteException {
         mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false);
 
-        mockExactAlarmPermissionGrant(true, false, MODE_DEFAULT);
-        assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
+        mockExactAlarmPermissionGrant(false, true, MODE_DEFAULT);
+        assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
 
-        mockExactAlarmPermissionGrant(true, true, MODE_ALLOWED);
-        assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
+        mockExactAlarmPermissionGrant(true, false, MODE_ERRORED);
+        assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
     }
 
     private void mockChangeEnabled(long changeId, boolean enabled) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java b/services/tests/mockingservicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java
index da0b83e..28fcaee 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java
@@ -21,7 +21,6 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.os.SystemClock;
-import android.os.UserHandle;
 import android.provider.DeviceConfig;
 
 import androidx.test.core.app.ApplicationProvider;
@@ -63,7 +62,6 @@
     public void testCreateExtraStatsLocked_samplingIntervalNotSet_returnsDefault() {
         PlatformLogger logger = new PlatformLogger(
                 ApplicationProvider.getApplicationContext(),
-                UserHandle.of(UserHandle.USER_NULL),
                 mAppSearchConfig);
 
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
@@ -96,8 +94,7 @@
         int putDocumentSamplingInterval = 1;
         int batchCallSamplingInterval = 2;
         PlatformLogger logger = new PlatformLogger(
-                ApplicationProvider.getApplicationContext(),
-                UserHandle.of(UserHandle.USER_NULL), mAppSearchConfig);
+                ApplicationProvider.getApplicationContext(), mAppSearchConfig);
 
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
                 AppSearchConfig.KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
@@ -143,7 +140,6 @@
         final String testPackageName = "packageName";
         PlatformLogger logger = new PlatformLogger(
                 ApplicationProvider.getApplicationContext(),
-                UserHandle.of(UserHandle.USER_NULL),
                 mAppSearchConfig);
 
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
@@ -162,7 +158,6 @@
         final String testPackageName = "packageName";
         PlatformLogger logger = new PlatformLogger(
                 ApplicationProvider.getApplicationContext(),
-                UserHandle.of(UserHandle.USER_NULL),
                 mAppSearchConfig);
 
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
@@ -186,7 +181,6 @@
         final String testPackageName = "packageName";
         PlatformLogger logger = new PlatformLogger(
                 ApplicationProvider.getApplicationContext(),
-                UserHandle.of(UserHandle.USER_NULL),
                 mAppSearchConfig);
 
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
@@ -214,7 +208,6 @@
         final String testPackageName = "packageName";
         PlatformLogger logger = new PlatformLogger(
                 ApplicationProvider.getApplicationContext(),
-                UserHandle.of(UserHandle.USER_NULL),
                 mAppSearchConfig);
 
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssGeofenceProxyTest.java b/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssGeofenceProxyTest.java
index b480f24..5e219a2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssGeofenceProxyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssGeofenceProxyTest.java
@@ -18,6 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.content.Context;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
@@ -49,6 +50,7 @@
     private static final int NOTIFICATION_RESPONSIVENESS = 0;
     private static final int UNKNOWN_TIMER = 0;
 
+    private @Mock Context mContext;
     private @Mock GnssConfiguration mMockConfiguration;
     private @Mock GnssNative.GeofenceCallbacks mGeofenceCallbacks;
 
@@ -63,7 +65,7 @@
         GnssNative.setGnssHalForTest(mFakeHal);
 
         GnssNative gnssNative = Objects.requireNonNull(
-                GnssNative.create(new TestInjector(), mMockConfiguration));
+                GnssNative.create(new TestInjector(mContext), mMockConfiguration));
         gnssNative.setGeofenceCallbacks(mGeofenceCallbacks);
         mTestProvider = new GnssGeofenceProxy(gnssNative);
         gnssNative.register();
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeAppOpsHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeAppOpsHelper.java
index 3d03781..d728451 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeAppOpsHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeAppOpsHelper.java
@@ -29,9 +29,10 @@
 public class FakeAppOpsHelper extends AppOpsHelper {
 
     private static class AppOp {
-        private boolean mAllowed = true;
-        private boolean mStarted = false;
-        private int mNoteCount = 0;
+        AppOp() {}
+        boolean mAllowed = true;
+        boolean mStarted = false;
+        int mNoteCount = 0;
     }
 
     private final HashMap<String, SparseArray<AppOp>> mAppOps;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeSettingsHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeSettingsHelper.java
index f1099f0..cd70020 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeSettingsHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeSettingsHelper.java
@@ -29,8 +29,8 @@
 import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
- * Version of AppOpsHelper for testing. Settings are initialized to reasonable defaults (location is
- * enabled by default).
+ * Version of SettingsHelper for testing. Settings are initialized to reasonable defaults (location
+ * is enabled by default).
  */
 public class FakeSettingsHelper extends SettingsHelper {
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
index ae70dad..bd24cfd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
@@ -16,9 +16,14 @@
 
 package com.android.server.location.injector;
 
+import android.content.Context;
+
+import com.android.server.location.settings.FakeLocationSettings;
+
 public class TestInjector implements Injector {
 
     private final FakeUserInfoHelper mUserInfoHelper;
+    private final FakeLocationSettings mLocationSettings;
     private final FakeAlarmHelper mAlarmHelper;
     private final FakeAppOpsHelper mAppOpsHelper;
     private final FakeLocationPermissionsHelper mLocationPermissionsHelper;
@@ -32,8 +37,9 @@
     private final FakeEmergencyHelper mEmergencyHelper;
     private final LocationUsageLogger mLocationUsageLogger;
 
-    public TestInjector() {
+    public TestInjector(Context context) {
         mUserInfoHelper = new FakeUserInfoHelper();
+        mLocationSettings = new FakeLocationSettings(context);
         mAlarmHelper = new FakeAlarmHelper();
         mAppOpsHelper = new FakeAppOpsHelper();
         mLocationPermissionsHelper = new FakeLocationPermissionsHelper(mAppOpsHelper);
@@ -54,6 +60,11 @@
     }
 
     @Override
+    public FakeLocationSettings getLocationSettings() {
+        return mLocationSettings;
+    }
+
+    @Override
     public FakeAlarmHelper getAlarmHelper() {
         return mAlarmHelper;
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index 6bc3b60..f703e2e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -19,6 +19,8 @@
 import static android.app.AppOpsManager.OP_FINE_LOCATION;
 import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
 import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
+import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
+import static android.location.LocationManager.GPS_PROVIDER;
 import static android.location.LocationRequest.PASSIVE_INTERVAL;
 import static android.location.provider.ProviderProperties.POWER_USAGE_HIGH;
 import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
@@ -56,6 +58,8 @@
 import static org.testng.Assert.assertThrows;
 
 import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
 import android.location.ILocationCallback;
 import android.location.ILocationListener;
 import android.location.LastLocationRequest;
@@ -82,6 +86,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.R;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.location.injector.FakeUserInfoHelper;
@@ -139,6 +144,10 @@
     @Mock
     private Context mContext;
     @Mock
+    private Resources mResources;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
     private PowerManager mPowerManager;
     @Mock
     private PowerManager.WakeLock mWakeLock;
@@ -161,20 +170,28 @@
         LocalServices.addService(LocationManagerInternal.class, mInternal);
 
         doReturn("android").when(mContext).getPackageName();
+        doReturn(mResources).when(mContext).getResources();
+        doReturn(mPackageManager).when(mContext).getPackageManager();
         doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class);
         doReturn(mWakeLock).when(mPowerManager).newWakeLock(anyInt(), anyString());
 
-        mInjector = new TestInjector();
+        mInjector = new TestInjector(mContext);
         mInjector.getUserInfoHelper().startUser(OTHER_USER);
 
         mPassive = new PassiveLocationProviderManager(mContext, mInjector);
         mPassive.startManager(null);
         mPassive.setRealProvider(new PassiveLocationProvider(mContext));
 
+        createManager(NAME);
+    }
+
+    private void createManager(String name) {
+        mStateChangedListener = mock(LocationProviderManager.StateChangedListener.class);
+
         mProvider = new TestProvider(PROPERTIES, PROVIDER_IDENTITY);
         mProvider.setProviderAllowed(true);
 
-        mManager = new LocationProviderManager(mContext, mInjector, NAME, mPassive);
+        mManager = new LocationProviderManager(mContext, mInjector, name, mPassive);
         mManager.startManager(mStateChangedListener);
         mManager.setRealProvider(mProvider);
     }
@@ -1017,6 +1034,95 @@
     }
 
     @Test
+    public void testProviderRequest_AdasGnssBypass() {
+        doReturn(true).when(mPackageManager).hasSystemFeature(FEATURE_AUTOMOTIVE);
+        doReturn(true).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+
+        createManager(GPS_PROVIDER);
+
+        ILocationListener listener1 = createMockLocationListener();
+        LocationRequest request1 = new LocationRequest.Builder(5)
+                .setWorkSource(WORK_SOURCE)
+                .build();
+        mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
+
+        assertThat(mProvider.getRequest().isActive()).isTrue();
+        assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
+        assertThat(mProvider.getRequest().isAdasGnssBypass()).isFalse();
+
+        ILocationListener listener2 = createMockLocationListener();
+        LocationRequest request2 = new LocationRequest.Builder(1)
+                .setAdasGnssBypass(true)
+                .setWorkSource(WORK_SOURCE)
+                .build();
+        mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
+
+        assertThat(mProvider.getRequest().isActive()).isTrue();
+        assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(1);
+        assertThat(mProvider.getRequest().isAdasGnssBypass()).isTrue();
+    }
+
+    @Test
+    public void testProviderRequest_AdasGnssBypass_ProviderDisabled() {
+        doReturn(true).when(mPackageManager).hasSystemFeature(FEATURE_AUTOMOTIVE);
+        doReturn(true).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+
+        createManager(GPS_PROVIDER);
+
+        ILocationListener listener1 = createMockLocationListener();
+        LocationRequest request1 = new LocationRequest.Builder(1)
+                .setWorkSource(WORK_SOURCE)
+                .build();
+        mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
+
+        ILocationListener listener2 = createMockLocationListener();
+        LocationRequest request2 = new LocationRequest.Builder(5)
+                .setAdasGnssBypass(true)
+                .setWorkSource(WORK_SOURCE)
+                .build();
+        mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
+
+        mInjector.getSettingsHelper().setLocationEnabled(false, IDENTITY.getUserId());
+
+        assertThat(mProvider.getRequest().isActive()).isTrue();
+        assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
+        assertThat(mProvider.getRequest().isAdasGnssBypass()).isTrue();
+    }
+
+    @Test
+    public void testProviderRequest_AdasGnssBypass_ProviderDisabled_AdasDisabled() {
+        mInjector.getSettingsHelper().setIgnoreSettingsAllowlist(
+                new PackageTagsList.Builder().add(
+                        IDENTITY.getPackageName()).build());
+        doReturn(true).when(mPackageManager).hasSystemFeature(FEATURE_AUTOMOTIVE);
+        doReturn(true).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+
+        createManager(GPS_PROVIDER);
+
+        ILocationListener listener1 = createMockLocationListener();
+        LocationRequest request1 = new LocationRequest.Builder(5)
+                .setLocationSettingsIgnored(true)
+                .setWorkSource(WORK_SOURCE)
+                .build();
+        mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
+
+        ILocationListener listener2 = createMockLocationListener();
+        LocationRequest request2 = new LocationRequest.Builder(1)
+                .setAdasGnssBypass(true)
+                .setWorkSource(WORK_SOURCE)
+                .build();
+        mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
+
+        mInjector.getLocationSettings().updateUserSettings(IDENTITY.getUserId(),
+                settings -> settings.withAdasGnssLocationEnabled(false));
+        mInjector.getSettingsHelper().setLocationEnabled(false, IDENTITY.getUserId());
+
+        assertThat(mProvider.getRequest().isActive()).isTrue();
+        assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
+        assertThat(mProvider.getRequest().isAdasGnssBypass()).isFalse();
+    }
+
+    @Test
     public void testProviderRequest_BatterySaver_ScreenOnOff() {
         mInjector.getLocationPowerSaveModeHelper().setLocationPowerSaveMode(
                 LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF);
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
index 04e0151..63996f0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.MockitoAnnotations.initMocks;
 
+import android.content.Context;
 import android.location.Location;
 import android.location.LocationResult;
 import android.location.provider.ProviderRequest;
@@ -58,6 +59,7 @@
     private TestInjector mInjector;
     private FakeProvider mDelegateProvider;
 
+    private @Mock Context mContext;
     private @Mock AbstractLocationProvider.Listener mListener;
     private @Mock FakeProvider.FakeProviderInterface mDelegate;
 
@@ -72,7 +74,7 @@
 
         mRandom = new Random(seed);
 
-        mInjector = new TestInjector();
+        mInjector = new TestInjector(mContext);
         mDelegateProvider = new FakeProvider(mDelegate);
 
         mProvider = new StationaryThrottlingLocationProvider("test_provider", mInjector,
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/settings/FakeLocationSettings.java b/services/tests/mockingservicestests/src/com/android/server/location/settings/FakeLocationSettings.java
new file mode 100644
index 0000000..4d46aba
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/settings/FakeLocationSettings.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.settings;
+
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import java.io.File;
+
+public class FakeLocationSettings extends LocationSettings {
+
+    public FakeLocationSettings(Context context) {
+        super(context);
+    }
+
+    @Override
+    protected File getUserSettingsDir(int userId) {
+        return ApplicationProvider.getApplicationContext().getCacheDir();
+    }
+
+    @Override
+    protected LocationUserSettingsStore createUserSettingsStore(int userId, File file) {
+        return new FakeLocationUserSettingsStore(userId, file);
+    }
+
+    private class FakeLocationUserSettingsStore extends LocationUserSettingsStore {
+
+        FakeLocationUserSettingsStore(int userId, File file) {
+            super(userId, file);
+        }
+
+        @Override
+        protected void onChange(LocationUserSettings oldSettings,
+                LocationUserSettings newSettings) {
+            fireListeners(mUserId, oldSettings, newSettings);
+        }
+    }
+}
+
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/settings/LocationSettingsTest.java b/services/tests/mockingservicestests/src/com/android/server/location/settings/LocationSettingsTest.java
new file mode 100644
index 0000000..4b6c79b
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/settings/LocationSettingsTest.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.settings;
+
+import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.R;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import java.io.File;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class LocationSettingsTest {
+
+    private @Mock Context mContext;
+    private @Mock Resources mResources;
+    private @Mock PackageManager mPackageManager;
+
+    private LocationSettings mLocationSettings;
+
+    @Before
+    public void setUp() {
+        initMocks(this);
+
+        doReturn(mResources).when(mContext).getResources();
+        doReturn(mPackageManager).when(mContext).getPackageManager();
+        doReturn(true).when(mPackageManager).hasSystemFeature(FEATURE_AUTOMOTIVE);
+
+        resetLocationSettings();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mLocationSettings.deleteFiles();
+    }
+
+    private void resetLocationSettings() {
+        mLocationSettings = new LocationSettings(mContext) {
+            @Override
+            protected File getUserSettingsDir(int userId) {
+                return ApplicationProvider.getApplicationContext().getCacheDir();
+            }
+        };
+    }
+
+    @Test
+    public void testLoadDefaults() {
+        doReturn(true).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+        assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isTrue();
+
+        doReturn(false).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+        assertThat(mLocationSettings.getUserSettings(2).isAdasGnssLocationEnabled()).isFalse();
+    }
+
+    @Test
+    public void testUpdate() {
+        doReturn(false).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+        mLocationSettings.updateUserSettings(1,
+                settings -> settings.withAdasGnssLocationEnabled(true));
+        assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isTrue();
+
+        mLocationSettings.updateUserSettings(1,
+                settings -> settings.withAdasGnssLocationEnabled(false));
+        assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isFalse();
+    }
+
+    @Test
+    public void testSerialization() throws Exception {
+        doReturn(false).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+        mLocationSettings.updateUserSettings(1,
+                settings -> settings.withAdasGnssLocationEnabled(true));
+        assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isTrue();
+
+        mLocationSettings.flushFiles();
+        resetLocationSettings();
+        assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isTrue();
+    }
+
+    @Test
+    public void testListeners() {
+        doReturn(false).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+        LocationSettings.LocationUserSettingsListener listener = mock(
+                LocationSettings.LocationUserSettingsListener.class);
+
+        mLocationSettings.registerLocationUserSettingsListener(listener);
+
+        ArgumentCaptor<LocationUserSettings> oldCaptor = ArgumentCaptor.forClass(
+                LocationUserSettings.class);
+        ArgumentCaptor<LocationUserSettings> newCaptor = ArgumentCaptor.forClass(
+                LocationUserSettings.class);
+        mLocationSettings.updateUserSettings(1,
+                settings -> settings.withAdasGnssLocationEnabled(true));
+        verify(listener, timeout(500).times(1)).onLocationUserSettingsChanged(eq(1),
+                oldCaptor.capture(), newCaptor.capture());
+        assertThat(oldCaptor.getValue().isAdasGnssLocationEnabled()).isFalse();
+        assertThat(newCaptor.getValue().isAdasGnssLocationEnabled()).isTrue();
+
+        oldCaptor = ArgumentCaptor.forClass(LocationUserSettings.class);
+        newCaptor = ArgumentCaptor.forClass(LocationUserSettings.class);
+        mLocationSettings.updateUserSettings(1,
+                settings -> settings.withAdasGnssLocationEnabled(false));
+        verify(listener, timeout(500).times(2)).onLocationUserSettingsChanged(eq(1),
+                oldCaptor.capture(), newCaptor.capture());
+        assertThat(oldCaptor.getValue().isAdasGnssLocationEnabled()).isTrue();
+        assertThat(newCaptor.getValue().isAdasGnssLocationEnabled()).isFalse();
+
+        mLocationSettings.unregisterLocationUserSettingsListener(listener);
+        mLocationSettings.updateUserSettings(1,
+                settings -> settings.withAdasGnssLocationEnabled(true));
+        verify(listener, after(500).times(2)).onLocationUserSettingsChanged(anyInt(), any(), any());
+    }
+
+    @Test
+    public void testNonAutomotive() {
+        doReturn(false).when(mPackageManager).hasSystemFeature(FEATURE_AUTOMOTIVE);
+        doReturn(true).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+
+        LocationSettings.LocationUserSettingsListener listener = mock(
+                LocationSettings.LocationUserSettingsListener.class);
+        mLocationSettings.registerLocationUserSettingsListener(listener);
+
+        assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isFalse();
+        mLocationSettings.updateUserSettings(1,
+                settings -> settings.withAdasGnssLocationEnabled(true));
+        assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isFalse();
+        verify(listener, after(500).never()).onLocationUserSettingsChanged(anyInt(), any(), any());
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
index 5a42c4b..53483f6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -31,6 +31,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeThat;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -57,6 +58,9 @@
 import android.testing.TestableContext;
 import android.util.Log;
 import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
 import android.view.Display;
 
 import androidx.test.filters.FlakyTest;
@@ -82,9 +86,12 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.quality.Strictness;
+import org.xmlpull.v1.XmlPullParserException;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 
 /**
  * Tests for the {@link WallpaperManagerService} class.
@@ -355,6 +362,31 @@
         verifyDisplayData();
     }
 
+    @Test
+    public void testXmlSerializationRoundtrip() {
+        WallpaperData systemWallpaperData = mService.getCurrentWallpaperData(FLAG_SYSTEM, 0);
+        try {
+            TypedXmlSerializer serializer = Xml.newBinarySerializer();
+            serializer.setOutput(new ByteArrayOutputStream(), StandardCharsets.UTF_8.name());
+            serializer.startDocument(StandardCharsets.UTF_8.name(), true);
+            mService.writeWallpaperAttributes(serializer, "wp", systemWallpaperData);
+        } catch (IOException e) {
+            fail("exception occurred while writing system wallpaper attributes");
+        }
+
+        WallpaperData shouldMatchSystem = new WallpaperData(systemWallpaperData.userId,
+                systemWallpaperData.wallpaperFile.getParentFile(),
+                systemWallpaperData.wallpaperFile.getAbsolutePath(),
+                systemWallpaperData.cropFile.getAbsolutePath());
+        try {
+            TypedXmlPullParser parser = Xml.newBinaryPullParser();
+            mService.parseWallpaperAttributes(parser, shouldMatchSystem, true);
+        } catch (XmlPullParserException e) {
+            fail("exception occurred while parsing wallpaper");
+        }
+        assertEquals(systemWallpaperData.primaryColors, shouldMatchSystem.primaryColors);
+    }
+
     // Verify that after continue switch user from userId 0 to lastUserId, the wallpaper data for
     // non-current user must not bind to wallpaper service.
     private void verifyNoConnectionBeforeLastUser(int lastUserId) {
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
index aadaba4..3c10789 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
@@ -42,7 +42,7 @@
 
 import com.android.compatibility.common.util.SystemUtil;
 import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
-import com.android.server.appsearch.visibilitystore.VisibilityStore;
+import com.android.server.appsearch.visibilitystore.VisibilityStoreImpl;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -67,7 +67,7 @@
     private final Map<UserHandle, PackageManager> mMockPackageManagers = new ArrayMap<>();
     private Context mContext;
     private AppSearchImpl mAppSearchImpl;
-    private VisibilityStore mVisibilityStore;
+    private VisibilityStoreImpl mVisibilityStore;
     private int mGlobalQuerierUid;
 
     @Before
@@ -93,7 +93,7 @@
         // Give ourselves global query permissions
         mAppSearchImpl = AppSearchImpl.create(
                 mTemporaryFolder.newFolder(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
-        mVisibilityStore = VisibilityStore.create(mAppSearchImpl, mContext);
+        mVisibilityStore = VisibilityStoreImpl.create(mAppSearchImpl, mContext);
         mGlobalQuerierUid =
                 mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0);
     }
@@ -127,8 +127,8 @@
                 "database",
                 Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
                 mVisibilityStore,
-                /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
-                /*schemasPackageAccessible=*/ ImmutableMap.of(
+                /*schemasNotDisplayedBySystem=*/ Collections.singletonList("schema1"),
+                /*schemasVisibleToPackages=*/ ImmutableMap.of(
                         "schema1",
                         ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
                 /*forceOverride=*/ false,
@@ -159,8 +159,8 @@
                         new AppSearchSchema.Builder("schema1").build(),
                         new AppSearchSchema.Builder("schema2").build()),
                 mVisibilityStore,
-                /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
-                /*schemasPackageAccessible=*/ ImmutableMap.of(
+                /*schemasNotDisplayedBySystem=*/ Collections.singletonList("schema1"),
+                /*schemasVisibleToPackages=*/ ImmutableMap.of(
                         "schema1",
                         ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
                 /*forceOverride=*/ false,
@@ -233,8 +233,8 @@
                 "database",
                 Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
                 mVisibilityStore,
-                /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
-                /*schemasPackageAccessible=*/ ImmutableMap.of(
+                /*schemasNotDisplayedBySystem=*/ Collections.singletonList("schema1"),
+                /*schemasVisibleToPackages=*/ ImmutableMap.of(
                         "schema1",
                         ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
                 /*forceOverride=*/ false,
@@ -263,8 +263,8 @@
                 "database",
                 /*schemas=*/ Collections.emptyList(),
                 mVisibilityStore,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ true,
                 /*schemaVersion=*/ 0);
 
@@ -292,8 +292,8 @@
                 "database",
                 Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
                 mVisibilityStore,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*schemaVersion=*/ 0);
 
@@ -327,8 +327,8 @@
                 "database",
                 Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
                 mVisibilityStore,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*schemaVersion=*/ 0);
 
@@ -355,8 +355,8 @@
                 "database",
                 Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
                 mVisibilityStore,
-                /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("Schema"),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.singletonList("Schema"),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*schemaVersion=*/ 0);
 
@@ -370,7 +370,7 @@
     }
 
     @Test
-    public void testSetSchema_defaultNotPackageAccessible() throws Exception {
+    public void testSetSchema_defaultNotVisibleToPackages() throws Exception {
         String packageName = "com.package";
 
         // Make sure package doesn't global query privileges
@@ -384,8 +384,8 @@
                 "database",
                 Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
                 mVisibilityStore,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*schemaVersion=*/ 0);
         assertThat(mVisibilityStore
@@ -399,7 +399,7 @@
     }
 
     @Test
-    public void testSetSchema_packageAccessible() throws Exception {
+    public void testSetSchema_visibleToPackages() throws Exception {
         // Values for a "foo" client
         String packageNameFoo = "packageFoo";
         byte[] sha256CertFoo = new byte[] {10};
@@ -423,8 +423,8 @@
                 "database",
                 Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
                 mVisibilityStore,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ ImmutableMap.of(
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ ImmutableMap.of(
                         "Schema",
                         ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
                 /*forceOverride=*/ false,
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
index f032402..02bb168 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
@@ -22,7 +22,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.testng.Assert.expectThrows;
+import static org.junit.Assert.assertThrows;
 
 import android.app.appsearch.AppSearchResult;
 import android.app.appsearch.AppSearchSchema;
@@ -34,6 +34,7 @@
 import android.app.appsearch.StorageInfo;
 import android.app.appsearch.exceptions.AppSearchException;
 import android.content.Context;
+import android.os.Process;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 
@@ -55,7 +56,6 @@
 import com.android.server.appsearch.proto.StatusProto;
 import com.android.server.appsearch.proto.StringIndexingConfig;
 import com.android.server.appsearch.proto.TermMatchType;
-import com.android.server.appsearch.visibilitystore.VisibilityStore;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -391,7 +391,7 @@
 
         DocumentProto.Builder actualDocument = documentProto.toBuilder();
         AppSearchException e =
-                expectThrows(
+                assertThrows(
                         AppSearchException.class, () -> removePrefixesFromDocument(actualDocument));
         assertThat(e).hasMessageThat().contains("Found unexpected multiple prefix names");
     }
@@ -416,7 +416,7 @@
 
         DocumentProto.Builder actualDocument = documentProto.toBuilder();
         AppSearchException e =
-                expectThrows(
+                assertThrows(
                         AppSearchException.class, () -> removePrefixesFromDocument(actualDocument));
         assertThat(e).hasMessageThat().contains("Found unexpected multiple prefix names");
     }
@@ -431,8 +431,8 @@
                 "database",
                 schemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
 
@@ -480,8 +480,8 @@
                 "database1",
                 schemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
 
@@ -499,7 +499,7 @@
                         new SearchSpec.Builder().addFilterSchemas("Type1").build(),
                         context.getPackageName(),
                         /*visibilityStore=*/ null,
-                        VisibilityStore.NO_OP_UID,
+                        Process.INVALID_UID,
                         /*callerHasSystemAccess=*/ false,
                         /*logger=*/ null);
         assertThat(results.getResults()).hasSize(1);
@@ -513,7 +513,7 @@
                         .setSchema(context.getPackageName() + "$database1/Type1")
                         .build();
         AppSearchException e =
-                expectThrows(
+                assertThrows(
                         AppSearchException.class,
                         () -> PrefixUtil.getPrefix(invalidDoc.getNamespace()));
         assertThat(e)
@@ -538,10 +538,8 @@
         assertThat(initStats.hasDeSync()).isFalse();
         assertThat(initStats.getDocumentStoreRecoveryCause())
                 .isEqualTo(InitializeStats.RECOVERY_CAUSE_NONE);
-        // TODO(b/187879464): There should not be a recovery here, but icing lib reports one if the
-        //  doc had no tokens. Once the mentioned bug is fixed, uncomment this.
-        // assertThat(initStats.getIndexRestorationCause())
-        //         .isEqualTo(InitializeStats.RECOVERY_CAUSE_NONE);
+        assertThat(initStats.getIndexRestorationCause())
+                .isEqualTo(InitializeStats.RECOVERY_CAUSE_NONE);
         assertThat(initStats.getSchemaStoreRecoveryCause())
                 .isEqualTo(InitializeStats.RECOVERY_CAUSE_NONE);
         assertThat(initStats.getDocumentStoreDataStatus())
@@ -558,7 +556,7 @@
                         new SearchSpec.Builder().addFilterSchemas("Type1").build(),
                         context.getPackageName(),
                         /*visibilityStore=*/ null,
-                        VisibilityStore.NO_OP_UID,
+                        Process.INVALID_UID,
                         /*callerHasSystemAccess=*/ false,
                         /*logger=*/ null);
         assertThat(results.getResults()).isEmpty();
@@ -569,8 +567,8 @@
                 "database1",
                 Collections.singletonList(new AppSearchSchema.Builder("Type1").build()),
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
 
@@ -585,7 +583,7 @@
                         new SearchSpec.Builder().addFilterSchemas("Type1").build(),
                         context.getPackageName(),
                         /*visibilityStore=*/ null,
-                        VisibilityStore.NO_OP_UID,
+                        Process.INVALID_UID,
                         /*callerHasSystemAccess=*/ false,
                         /*logger=*/ null);
         assertThat(results.getResults()).hasSize(1);
@@ -604,8 +602,8 @@
                 "database",
                 schemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
 
@@ -638,8 +636,8 @@
                 "database1",
                 schemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
         mAppSearchImpl.setSchema(
@@ -647,8 +645,8 @@
                 "database2",
                 schemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
 
@@ -692,8 +690,8 @@
                 "database",
                 schemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
 
@@ -734,8 +732,8 @@
                 "database1",
                 schema1,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
 
@@ -747,8 +745,8 @@
                 "database2",
                 schema2,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
 
@@ -790,8 +788,8 @@
                 "database1",
                 schema1,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
 
@@ -803,8 +801,8 @@
                 "database2",
                 schema2,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
 
@@ -851,7 +849,7 @@
                         searchSpec,
                         /*callerPackageName=*/ "",
                         /*visibilityStore=*/ null,
-                        VisibilityStore.NO_OP_UID,
+                        Process.INVALID_UID,
                         /*callerHasSystemAccess=*/ false,
                         /*logger=*/ null);
         assertThat(searchResultPage.getResults()).isEmpty();
@@ -893,8 +891,8 @@
                 "database1",
                 schemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
 
@@ -938,8 +936,8 @@
                 "database1",
                 oldSchemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
 
@@ -954,8 +952,8 @@
                         "database1",
                         newSchemas,
                         /*visibilityStore=*/ null,
-                        /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                        /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                        /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                        /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                         /*forceOverride=*/ true,
                         /*version=*/ 0);
         assertThat(setSchemaResponse.getDeletedTypes()).containsExactly("Text");
@@ -977,8 +975,8 @@
                 "database1",
                 schemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
 
@@ -1010,8 +1008,8 @@
                         "database1",
                         finalSchemas,
                         /*visibilityStore=*/ null,
-                        /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                        /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                        /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                        /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                         /*forceOverride=*/ false,
                         /*version=*/ 0);
 
@@ -1024,8 +1022,8 @@
                 "database1",
                 finalSchemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ true,
                 /*version=*/ 0);
 
@@ -1062,8 +1060,8 @@
                 "database1",
                 schemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
         mAppSearchImpl.setSchema(
@@ -1071,8 +1069,8 @@
                 "database2",
                 schemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
 
@@ -1111,8 +1109,8 @@
                 "database1",
                 schemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ true,
                 /*version=*/ 0);
 
@@ -1156,8 +1154,8 @@
                 "database",
                 schema,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
 
@@ -1218,8 +1216,8 @@
                 "database",
                 schema,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
         mAppSearchImpl.setSchema(
@@ -1227,8 +1225,8 @@
                 "database",
                 schema,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
 
@@ -1273,8 +1271,8 @@
                 "database1",
                 Collections.singletonList(new AppSearchSchema.Builder("schema").build()),
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
         assertThat(mAppSearchImpl.getPackageToDatabases())
@@ -1287,8 +1285,8 @@
                 "database2",
                 Collections.singletonList(new AppSearchSchema.Builder("schema").build()),
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
         assertThat(mAppSearchImpl.getPackageToDatabases())
@@ -1301,8 +1299,8 @@
                 "database1",
                 Collections.singletonList(new AppSearchSchema.Builder("schema").build()),
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
         assertThat(mAppSearchImpl.getPackageToDatabases())
@@ -1360,8 +1358,8 @@
                 "database",
                 schemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
 
@@ -1511,8 +1509,8 @@
                 "database",
                 schemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
 
@@ -1534,8 +1532,8 @@
                 "database",
                 schemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
 
@@ -1550,8 +1548,8 @@
                 "database",
                 schemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
 
@@ -1599,8 +1597,8 @@
                 "database",
                 schemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
 
@@ -1622,8 +1620,8 @@
                 "database1",
                 schemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
 
@@ -1644,8 +1642,8 @@
                 "database1",
                 schemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
         mAppSearchImpl.setSchema(
@@ -1653,8 +1651,8 @@
                 "database2",
                 schemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
 
@@ -1700,15 +1698,15 @@
                 "database",
                 schemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
 
         appSearchImpl.close();
 
         // Check all our public APIs
-        expectThrows(
+        assertThrows(
                 IllegalStateException.class,
                 () ->
                         appSearchImpl.setSchema(
@@ -1716,15 +1714,15 @@
                                 "database",
                                 schemas,
                                 /*visibilityStore=*/ null,
-                                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                                 /*forceOverride=*/ false,
                                 /*version=*/ 0));
 
-        expectThrows(
+        assertThrows(
                 IllegalStateException.class, () -> appSearchImpl.getSchema("package", "database"));
 
-        expectThrows(
+        assertThrows(
                 IllegalStateException.class,
                 () ->
                         appSearchImpl.putDocument(
@@ -1733,13 +1731,13 @@
                                 new GenericDocument.Builder<>("namespace", "id", "type").build(),
                                 /*logger=*/ null));
 
-        expectThrows(
+        assertThrows(
                 IllegalStateException.class,
                 () ->
                         appSearchImpl.getDocument(
                                 "package", "database", "namespace", "id", Collections.emptyMap()));
 
-        expectThrows(
+        assertThrows(
                 IllegalStateException.class,
                 () ->
                         appSearchImpl.query(
@@ -1749,7 +1747,7 @@
                                 new SearchSpec.Builder().build(),
                                 /*logger=*/ null));
 
-        expectThrows(
+        assertThrows(
                 IllegalStateException.class,
                 () ->
                         appSearchImpl.globalQuery(
@@ -1757,19 +1755,19 @@
                                 new SearchSpec.Builder().build(),
                                 "package",
                                 /*visibilityStore=*/ null,
-                                VisibilityStore.NO_OP_UID,
+                                Process.INVALID_UID,
                                 /*callerHasSystemAccess=*/ false,
                                 /*logger=*/ null));
 
-        expectThrows(
+        assertThrows(
                 IllegalStateException.class,
                 () -> appSearchImpl.getNextPage(/*nextPageToken=*/ 1L));
 
-        expectThrows(
+        assertThrows(
                 IllegalStateException.class,
                 () -> appSearchImpl.invalidateNextPageToken(/*nextPageToken=*/ 1L));
 
-        expectThrows(
+        assertThrows(
                 IllegalStateException.class,
                 () ->
                         appSearchImpl.reportUsage(
@@ -1780,7 +1778,7 @@
                                 /*usageTimestampMillis=*/ 1000L,
                                 /*systemUsage=*/ false));
 
-        expectThrows(
+        assertThrows(
                 IllegalStateException.class,
                 () ->
                         appSearchImpl.remove(
@@ -1790,7 +1788,7 @@
                                 "id",
                                 /*removeStatsBuilder=*/ null));
 
-        expectThrows(
+        assertThrows(
                 IllegalStateException.class,
                 () ->
                         appSearchImpl.removeByQuery(
@@ -1800,15 +1798,15 @@
                                 new SearchSpec.Builder().build(),
                                 /*removeStatsBuilder=*/ null));
 
-        expectThrows(
+        assertThrows(
                 IllegalStateException.class,
                 () -> appSearchImpl.getStorageInfoForPackage("package"));
 
-        expectThrows(
+        assertThrows(
                 IllegalStateException.class,
                 () -> appSearchImpl.getStorageInfoForDatabase("package", "database"));
 
-        expectThrows(
+        assertThrows(
                 IllegalStateException.class,
                 () -> appSearchImpl.persistToDisk(PersistType.Code.FULL));
     }
@@ -1827,8 +1825,8 @@
                 "database",
                 schemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
 
@@ -1866,8 +1864,8 @@
                 "database",
                 schemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
 
@@ -1892,7 +1890,7 @@
         // Delete the first document
         appSearchImpl.remove("package", "database", "namespace1", "id1", /*statsBuilder=*/ null);
         appSearchImpl.persistToDisk(PersistType.Code.LITE);
-        expectThrows(
+        assertThrows(
                 AppSearchException.class,
                 () ->
                         appSearchImpl.getDocument(
@@ -1909,7 +1907,7 @@
         // Only the second document should be retrievable from another instance.
         AppSearchImpl appSearchImpl2 =
                 AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
-        expectThrows(
+        assertThrows(
                 AppSearchException.class,
                 () ->
                         appSearchImpl2.getDocument(
@@ -1938,8 +1936,8 @@
                 "database",
                 schemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
 
@@ -1972,7 +1970,7 @@
                         .build(),
                 /*statsBuilder=*/ null);
         appSearchImpl.persistToDisk(PersistType.Code.LITE);
-        expectThrows(
+        assertThrows(
                 AppSearchException.class,
                 () ->
                         appSearchImpl.getDocument(
@@ -1989,7 +1987,7 @@
         // Only the second document should be retrievable from another instance.
         AppSearchImpl appSearchImpl2 =
                 AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
-        expectThrows(
+        assertThrows(
                 AppSearchException.class,
                 () ->
                         appSearchImpl2.getDocument(
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java
index f20e8c6..9b75561 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java
@@ -25,6 +25,7 @@
 import android.app.appsearch.GenericDocument;
 import android.app.appsearch.SearchResultPage;
 import android.app.appsearch.SearchSpec;
+import android.app.appsearch.exceptions.AppSearchException;
 
 import com.android.server.appsearch.external.localstorage.stats.CallStats;
 import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
@@ -32,17 +33,24 @@
 import com.android.server.appsearch.external.localstorage.stats.RemoveStats;
 import com.android.server.appsearch.external.localstorage.stats.SearchStats;
 import com.android.server.appsearch.proto.DeleteStatsProto;
+import com.android.server.appsearch.proto.DocumentProto;
 import com.android.server.appsearch.proto.InitializeStatsProto;
 import com.android.server.appsearch.proto.PutDocumentStatsProto;
+import com.android.server.appsearch.proto.PutResultProto;
 import com.android.server.appsearch.proto.QueryStatsProto;
 import com.android.server.appsearch.proto.ScoringSpecProto;
+import com.android.server.appsearch.proto.StatusProto;
 import com.android.server.appsearch.proto.TermMatchType;
 
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 
+import java.io.File;
 import java.util.Collections;
 import java.util.List;
 
@@ -279,7 +287,7 @@
     // Testing actual logging
     //
     @Test
-    public void testLoggingStats_initialize() throws Exception {
+    public void testLoggingStats_initializeWithoutDocuments_success() throws Exception {
         // Create an unused AppSearchImpl to generated an InitializeStats.
         InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
         AppSearchImpl.create(mTemporaryFolder.newFolder(), initStatsBuilder, ALWAYS_OPTIMIZE);
@@ -295,25 +303,139 @@
                 .isEqualTo(InitializeStatsProto.DocumentStoreDataStatus.NO_DATA_LOSS_VALUE);
         assertThat(iStats.getDocumentCount()).isEqualTo(0);
         assertThat(iStats.getSchemaTypeCount()).isEqualTo(0);
+        assertThat(iStats.hasReset()).isEqualTo(false);
+        assertThat(iStats.getResetStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
     }
 
     @Test
-    public void testLoggingStats_putDocument() throws Exception {
+    public void testLoggingStats_initializeWithDocuments_success() throws Exception {
+        final String testPackageName = "testPackage";
+        final String testDatabase = "testDatabase";
+        final File folder = mTemporaryFolder.newFolder();
+
+        AppSearchImpl appSearchImpl =
+                AppSearchImpl.create(folder, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
+        List<AppSearchSchema> schemas =
+                ImmutableList.of(
+                        new AppSearchSchema.Builder("Type1").build(),
+                        new AppSearchSchema.Builder("Type2").build());
+        appSearchImpl.setSchema(
+                testPackageName,
+                testDatabase,
+                schemas,
+                /*visibilityStore=*/ null,
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+                /*forceOverride=*/ false,
+                /*version=*/ 0);
+        GenericDocument doc1 = new GenericDocument.Builder<>("namespace", "id1", "Type1").build();
+        GenericDocument doc2 = new GenericDocument.Builder<>("namespace", "id2", "Type1").build();
+        appSearchImpl.putDocument(testPackageName, testDatabase, doc1, mLogger);
+        appSearchImpl.putDocument(testPackageName, testDatabase, doc2, mLogger);
+        appSearchImpl.close();
+
+        // Create another appsearchImpl on the same folder
+        InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
+        AppSearchImpl.create(folder, initStatsBuilder, ALWAYS_OPTIMIZE);
+        InitializeStats iStats = initStatsBuilder.build();
+
+        assertThat(iStats).isNotNull();
+        assertThat(iStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
+        // Total latency captured in LocalStorage
+        assertThat(iStats.getTotalLatencyMillis()).isEqualTo(0);
+        assertThat(iStats.hasDeSync()).isFalse();
+        assertThat(iStats.getNativeLatencyMillis()).isGreaterThan(0);
+        assertThat(iStats.getDocumentStoreDataStatus())
+                .isEqualTo(InitializeStatsProto.DocumentStoreDataStatus.NO_DATA_LOSS_VALUE);
+        assertThat(iStats.getDocumentCount()).isEqualTo(2);
+        assertThat(iStats.getSchemaTypeCount()).isEqualTo(2);
+        assertThat(iStats.hasReset()).isEqualTo(false);
+        assertThat(iStats.getResetStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
+    }
+
+    @Test
+    public void testLoggingStats_initialize_failure() throws Exception {
+        final String testPackageName = "testPackage";
+        final String testDatabase = "testDatabase";
+        final File folder = mTemporaryFolder.newFolder();
+
+        AppSearchImpl appSearchImpl =
+                AppSearchImpl.create(folder, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
+
+        List<AppSearchSchema> schemas =
+                ImmutableList.of(
+                        new AppSearchSchema.Builder("Type1").build(),
+                        new AppSearchSchema.Builder("Type2").build());
+        appSearchImpl.setSchema(
+                testPackageName,
+                testDatabase,
+                schemas,
+                /*visibilityStore=*/ null,
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+                /*forceOverride=*/ false,
+                /*version=*/ 0);
+
+        // Insert a valid doc
+        GenericDocument doc1 = new GenericDocument.Builder<>("namespace", "id1", "Type1").build();
+        appSearchImpl.putDocument(testPackageName, testDatabase, doc1, mLogger);
+
+        // Insert the invalid doc with an invalid namespace right into icing
+        DocumentProto invalidDoc =
+                DocumentProto.newBuilder()
+                        .setNamespace("invalidNamespace")
+                        .setUri("id2")
+                        .setSchema(String.format("%s$%s/Type1", testPackageName, testDatabase))
+                        .build();
+        PutResultProto putResultProto = appSearchImpl.mIcingSearchEngineLocked.put(invalidDoc);
+        assertThat(putResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.OK);
+        appSearchImpl.close();
+
+        // Create another appsearchImpl on the same folder
+        InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
+        AppSearchImpl.create(folder, initStatsBuilder, ALWAYS_OPTIMIZE);
+        InitializeStats iStats = initStatsBuilder.build();
+
+        // Some of other fields are already covered by AppSearchImplTest#testReset()
+        assertThat(iStats).isNotNull();
+        assertThat(iStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_INTERNAL_ERROR);
+        assertThat(iStats.hasReset()).isTrue();
+    }
+
+    @Test
+    public void testLoggingStats_putDocument_success() throws Exception {
         // Insert schema
         final String testPackageName = "testPackage";
         final String testDatabase = "testDatabase";
-        List<AppSearchSchema> schemas =
-                Collections.singletonList(new AppSearchSchema.Builder("type").build());
+        AppSearchSchema testSchema =
+                new AppSearchSchema.Builder("type")
+                        .addProperty(
+                                new AppSearchSchema.StringPropertyConfig.Builder("subject")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                AppSearchSchema.StringPropertyConfig
+                                                        .INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(
+                                                AppSearchSchema.StringPropertyConfig
+                                                        .TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .build();
+        List<AppSearchSchema> schemas = Collections.singletonList(testSchema);
         mAppSearchImpl.setSchema(
                 testPackageName,
                 testDatabase,
                 schemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
-        GenericDocument document = new GenericDocument.Builder<>("namespace", "id", "type").build();
+
+        GenericDocument document =
+                new GenericDocument.Builder<>("namespace", "id", "type")
+                        .setPropertyString("subject", "testPut example1")
+                        .build();
 
         mAppSearchImpl.putDocument(testPackageName, testDatabase, document, mLogger);
 
@@ -322,43 +444,119 @@
         assertThat(pStats.getPackageName()).isEqualTo(testPackageName);
         assertThat(pStats.getDatabase()).isEqualTo(testDatabase);
         assertThat(pStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
-        // The rest of native stats have been tested in testCopyNativeStats
+        // The latency related native stats have been tested in testCopyNativeStats
         assertThat(pStats.getNativeDocumentSizeBytes()).isGreaterThan(0);
+        assertThat(pStats.getNativeNumTokensIndexed()).isGreaterThan(0);
     }
 
     @Test
-    public void testLoggingStats_search() throws Exception {
+    public void testLoggingStats_putDocument_failure() throws Exception {
         // Insert schema
         final String testPackageName = "testPackage";
         final String testDatabase = "testDatabase";
-        List<AppSearchSchema> schemas =
-                Collections.singletonList(new AppSearchSchema.Builder("type").build());
+        AppSearchSchema testSchema =
+                new AppSearchSchema.Builder("type")
+                        .addProperty(
+                                new AppSearchSchema.StringPropertyConfig.Builder("subject")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                AppSearchSchema.StringPropertyConfig
+                                                        .INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(
+                                                AppSearchSchema.StringPropertyConfig
+                                                        .TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .build();
+        List<AppSearchSchema> schemas = Collections.singletonList(testSchema);
         mAppSearchImpl.setSchema(
                 testPackageName,
                 testDatabase,
                 schemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
 
-        GenericDocument document = new GenericDocument.Builder<>("namespace", "id", "type").build();
-        mAppSearchImpl.putDocument(testPackageName, testDatabase, document, mLogger);
+        GenericDocument document =
+                new GenericDocument.Builder<>("namespace", "id", "type")
+                        .setPropertyString("nonExist", "testPut example1")
+                        .build();
+
+        // We mainly want to check the status code in stats. So we don't need to inspect the
+        // exception here.
+        Assert.assertThrows(
+                AppSearchException.class,
+                () -> mAppSearchImpl.putDocument(testPackageName, testDatabase, document, mLogger));
+
+        PutDocumentStats pStats = mLogger.mPutDocumentStats;
+        assertThat(pStats).isNotNull();
+        assertThat(pStats.getPackageName()).isEqualTo(testPackageName);
+        assertThat(pStats.getDatabase()).isEqualTo(testDatabase);
+        assertThat(pStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_NOT_FOUND);
+    }
+
+    @Test
+    public void testLoggingStats_search_success() throws Exception {
+        // Insert schema
+        final String testPackageName = "testPackage";
+        final String testDatabase = "testDatabase";
+        AppSearchSchema testSchema =
+                new AppSearchSchema.Builder("type")
+                        .addProperty(
+                                new AppSearchSchema.StringPropertyConfig.Builder("subject")
+                                        .setCardinality(
+                                                AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                                        .setIndexingType(
+                                                AppSearchSchema.StringPropertyConfig
+                                                        .INDEXING_TYPE_PREFIXES)
+                                        .setTokenizerType(
+                                                AppSearchSchema.StringPropertyConfig
+                                                        .TOKENIZER_TYPE_PLAIN)
+                                        .build())
+                        .build();
+        List<AppSearchSchema> schemas = Collections.singletonList(testSchema);
+        mAppSearchImpl.setSchema(
+                testPackageName,
+                testDatabase,
+                schemas,
+                /*visibilityStore=*/ null,
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+                /*forceOverride=*/ false,
+                /*version=*/ 0);
+        GenericDocument document1 =
+                new GenericDocument.Builder<>("namespace", "id1", "type")
+                        .setPropertyString("subject", "testPut example1")
+                        .build();
+        GenericDocument document2 =
+                new GenericDocument.Builder<>("namespace", "id2", "type")
+                        .setPropertyString("subject", "testPut example2")
+                        .build();
+        GenericDocument document3 =
+                new GenericDocument.Builder<>("namespace", "id3", "type")
+                        .setPropertyString("subject", "testPut 3")
+                        .build();
+        mAppSearchImpl.putDocument(testPackageName, testDatabase, document1, mLogger);
+        mAppSearchImpl.putDocument(testPackageName, testDatabase, document2, mLogger);
+        mAppSearchImpl.putDocument(testPackageName, testDatabase, document3, mLogger);
 
         // No query filters specified. package2 should only get its own documents back.
         SearchSpec searchSpec =
-                new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
+                new SearchSpec.Builder()
+                        .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
+                        .setRankingStrategy(SearchSpec.RANKING_STRATEGY_CREATION_TIMESTAMP)
+                        .build();
+        String queryStr = "testPut e";
         SearchResultPage searchResultPage =
                 mAppSearchImpl.query(
-                        testPackageName,
-                        testDatabase,
-                        /*QueryExpression=*/ "",
-                        searchSpec,
-                        /*logger=*/ mLogger);
+                        testPackageName, testDatabase, queryStr, searchSpec, /*logger=*/ mLogger);
 
-        assertThat(searchResultPage.getResults()).hasSize(1);
-        assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document);
+        assertThat(searchResultPage.getResults()).hasSize(2);
+        // The ranking strategy is LIFO
+        assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
+        assertThat(searchResultPage.getResults().get(1).getGenericDocument()).isEqualTo(document1);
 
         SearchStats sStats = mLogger.mSearchStats;
 
@@ -368,17 +566,59 @@
         assertThat(sStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
         assertThat(sStats.getTotalLatencyMillis()).isGreaterThan(0);
         assertThat(sStats.getVisibilityScope()).isEqualTo(SearchStats.VISIBILITY_SCOPE_LOCAL);
-        assertThat(sStats.getTermCount()).isEqualTo(0);
-        // assertThat(sStats.getNativeQueryLength()).isEqualTo(0);
+        assertThat(sStats.getTermCount()).isEqualTo(2);
+        assertThat(sStats.getQueryLength()).isEqualTo(queryStr.length());
         assertThat(sStats.getFilteredNamespaceCount()).isEqualTo(1);
         assertThat(sStats.getFilteredSchemaTypeCount()).isEqualTo(1);
-        assertThat(sStats.getCurrentPageReturnedResultCount()).isEqualTo(1);
+        assertThat(sStats.getCurrentPageReturnedResultCount()).isEqualTo(2);
         assertThat(sStats.isFirstPage()).isTrue();
-        assertThat(sStats.getScoredDocumentCount()).isEqualTo(1);
+        assertThat(sStats.getRankingStrategy())
+                .isEqualTo(SearchSpec.RANKING_STRATEGY_CREATION_TIMESTAMP);
+        assertThat(sStats.getScoredDocumentCount()).isEqualTo(2);
+        assertThat(sStats.getResultWithSnippetsCount()).isEqualTo(0);
     }
 
     @Test
-    public void testLoggingStats_remove() throws Exception {
+    public void testLoggingStats_search_failure() throws Exception {
+        final String testPackageName = "testPackage";
+        final String testDatabase = "testDatabase";
+        List<AppSearchSchema> schemas =
+                ImmutableList.of(
+                        new AppSearchSchema.Builder("Type1").build(),
+                        new AppSearchSchema.Builder("Type2").build());
+        mAppSearchImpl.setSchema(
+                testPackageName,
+                testDatabase,
+                schemas,
+                /*visibilityStore=*/ null,
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+                /*forceOverride=*/ false,
+                /*version=*/ 0);
+
+        SearchSpec searchSpec =
+                new SearchSpec.Builder()
+                        .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
+                        .setRankingStrategy(SearchSpec.RANKING_STRATEGY_CREATION_TIMESTAMP)
+                        .addFilterPackageNames("anotherPackage")
+                        .build();
+
+        mAppSearchImpl.query(
+                testPackageName,
+                testPackageName,
+                /* queryExpression= */ "",
+                searchSpec,
+                /*logger=*/ mLogger);
+
+        SearchStats sStats = mLogger.mSearchStats;
+        assertThat(sStats).isNotNull();
+        assertThat(sStats.getPackageName()).isEqualTo(testPackageName);
+        assertThat(sStats.getDatabase()).isEqualTo(testPackageName);
+        assertThat(sStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR);
+    }
+
+    @Test
+    public void testLoggingStats_remove_success() throws Exception {
         // Insert schema
         final String testPackageName = "testPackage";
         final String testDatabase = "testDatabase";
@@ -391,8 +631,8 @@
                 testDatabase,
                 schemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
         GenericDocument document =
@@ -406,12 +646,59 @@
         assertThat(rStats.getPackageName()).isEqualTo(testPackageName);
         assertThat(rStats.getDatabase()).isEqualTo(testDatabase);
         // delete by namespace + id
+        assertThat(rStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
         assertThat(rStats.getDeleteType()).isEqualTo(DeleteStatsProto.DeleteType.Code.SINGLE_VALUE);
         assertThat(rStats.getDeletedDocumentCount()).isEqualTo(1);
     }
 
     @Test
-    public void testLoggingStats_removeByQuery() throws Exception {
+    public void testLoggingStats_remove_failure() throws Exception {
+        // Insert schema
+        final String testPackageName = "testPackage";
+        final String testDatabase = "testDatabase";
+        final String testNamespace = "testNameSpace";
+        final String testId = "id";
+        List<AppSearchSchema> schemas =
+                Collections.singletonList(new AppSearchSchema.Builder("type").build());
+        mAppSearchImpl.setSchema(
+                testPackageName,
+                testDatabase,
+                schemas,
+                /*visibilityStore=*/ null,
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+                /*forceOverride=*/ false,
+                /*version=*/ 0);
+
+        GenericDocument document =
+                new GenericDocument.Builder<>(testNamespace, testId, "type").build();
+        mAppSearchImpl.putDocument(testPackageName, testDatabase, document, /*logger=*/ null);
+
+        RemoveStats.Builder rStatsBuilder = new RemoveStats.Builder(testPackageName, testDatabase);
+
+        // We mainly want to check the status code in stats. So we don't need to inspect the
+        // exception here.
+        Assert.assertThrows(
+                AppSearchException.class,
+                () ->
+                        mAppSearchImpl.remove(
+                                testPackageName,
+                                testDatabase,
+                                testNamespace,
+                                "invalidId",
+                                rStatsBuilder));
+
+        RemoveStats rStats = rStatsBuilder.build();
+        assertThat(rStats.getPackageName()).isEqualTo(testPackageName);
+        assertThat(rStats.getDatabase()).isEqualTo(testDatabase);
+        assertThat(rStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_NOT_FOUND);
+        // delete by namespace + id
+        assertThat(rStats.getDeleteType()).isEqualTo(DeleteStatsProto.DeleteType.Code.SINGLE_VALUE);
+        assertThat(rStats.getDeletedDocumentCount()).isEqualTo(0);
+    }
+
+    @Test
+    public void testLoggingStats_removeByQuery_success() throws Exception {
         // Insert schema
         final String testPackageName = "testPackage";
         final String testDatabase = "testDatabase";
@@ -423,8 +710,8 @@
                 testDatabase,
                 schemas,
                 /*visibilityStore=*/ null,
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap(),
                 /*forceOverride=*/ false,
                 /*version=*/ 0);
         GenericDocument document1 =
@@ -444,6 +731,7 @@
 
         assertThat(rStats.getPackageName()).isEqualTo(testPackageName);
         assertThat(rStats.getDatabase()).isEqualTo(testDatabase);
+        assertThat(rStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
         // delete by query
         assertThat(rStats.getDeleteType()).isEqualTo(DeleteStatsProto.DeleteType.Code.QUERY_VALUE);
         assertThat(rStats.getDeletedDocumentCount()).isEqualTo(2);
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 d3feb12..64a670d 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
@@ -46,7 +46,6 @@
                     PREFIX,
                     Collections.singletonMap(PREFIX + SCHEMA_TYPE, SCHEMA_TYPE_CONFIG_PROTO));
 
-    // TODO(tytytyww): Add tests for Double and Long Snippets.
     @Test
     public void testSingleStringSnippet() {
         final String propertyKeyString = "content";
@@ -112,7 +111,6 @@
         assertThat(match.getSnippet()).isEqualTo(window);
     }
 
-    // TODO(tytytyww): Add tests for Double and Long Snippets.
     @Test
     public void testNoSnippets() {
         final String propertyKeyString = "content";
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java
index 6d90686..57d9941 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java
@@ -254,14 +254,25 @@
 
     @Test
     public void testAppSearchStats_SetSchemaStats() {
+        SchemaMigrationStats schemaMigrationStats =
+                new SchemaMigrationStats.Builder()
+                        .setGetSchemaLatencyMillis(1)
+                        .setQueryAndTransformLatencyMillis(2)
+                        .setFirstSetSchemaLatencyMillis(3)
+                        .setSecondSetSchemaLatencyMillis(4)
+                        .setSaveDocumentLatencyMillis(5)
+                        .setMigratedDocumentCount(6)
+                        .setSavedDocumentCount(7)
+                        .build();
         int nativeLatencyMillis = 1;
         int newTypeCount = 2;
         int compatibleTypeChangeCount = 3;
         int indexIncompatibleTypeChangeCount = 4;
         int backwardsIncompatibleTypeChangeCount = 5;
-        final SetSchemaStats sStats =
+        SetSchemaStats sStats =
                 new SetSchemaStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE)
                         .setStatusCode(TEST_STATUS_CODE)
+                        .setSchemaMigrationStats(schemaMigrationStats)
                         .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS)
                         .setNativeLatencyMillis(nativeLatencyMillis)
                         .setNewTypeCount(newTypeCount)
@@ -274,6 +285,7 @@
         assertThat(sStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
         assertThat(sStats.getDatabase()).isEqualTo(TEST_DATA_BASE);
         assertThat(sStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE);
+        assertThat(sStats.getSchemaMigrationStats()).isEqualTo(schemaMigrationStats);
         assertThat(sStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS);
         assertThat(sStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
         assertThat(sStats.getNewTypeCount()).isEqualTo(newTypeCount);
@@ -285,6 +297,35 @@
     }
 
     @Test
+    public void testAppSearchStats_SchemaMigrationStats() {
+        int getSchemaLatency = 1;
+        int queryAndTransformLatency = 2;
+        int firstSetSchemaLatency = 3;
+        int secondSetSchemaLatency = 4;
+        int saveDocumentLatency = 5;
+        int migratedDocumentCount = 6;
+        int savedDocumentCount = 7;
+        SchemaMigrationStats sStats =
+                new SchemaMigrationStats.Builder()
+                        .setGetSchemaLatencyMillis(getSchemaLatency)
+                        .setQueryAndTransformLatencyMillis(queryAndTransformLatency)
+                        .setFirstSetSchemaLatencyMillis(firstSetSchemaLatency)
+                        .setSecondSetSchemaLatencyMillis(secondSetSchemaLatency)
+                        .setSaveDocumentLatencyMillis(saveDocumentLatency)
+                        .setMigratedDocumentCount(migratedDocumentCount)
+                        .setSavedDocumentCount(savedDocumentCount)
+                        .build();
+
+        assertThat(sStats.getGetSchemaLatencyMillis()).isEqualTo(getSchemaLatency);
+        assertThat(sStats.getQueryAndTransformLatencyMillis()).isEqualTo(queryAndTransformLatency);
+        assertThat(sStats.getFirstSetSchemaLatencyMillis()).isEqualTo(firstSetSchemaLatency);
+        assertThat(sStats.getSecondSetSchemaLatencyMillis()).isEqualTo(secondSetSchemaLatency);
+        assertThat(sStats.getSaveDocumentLatencyMillis()).isEqualTo(saveDocumentLatency);
+        assertThat(sStats.getMigratedDocumentCount()).isEqualTo(migratedDocumentCount);
+        assertThat(sStats.getSavedDocumentCount()).isEqualTo(savedDocumentCount);
+    }
+
+    @Test
     public void testAppSearchStats_RemoveStats() {
         int nativeLatencyMillis = 1;
         @RemoveStats.DeleteType int deleteType = 2;
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 7c275e1..ec96d6a 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
@@ -65,13 +65,8 @@
         Context context = ApplicationProvider.getApplicationContext();
         mContext = new ContextWrapper(context) {
             @Override
-            public Context createContextAsUser(UserHandle user, int flags) {
-                return new ContextWrapper(super.createContextAsUser(user, flags)) {
-                    @Override
-                    public PackageManager getPackageManager() {
-                        return getMockPackageManager(user);
-                    }
-                };
+            public PackageManager getPackageManager() {
+                return getMockPackageManager(mContext.getUser());
             }
         };
     }
@@ -153,7 +148,6 @@
         final int testUid = 1234;
         PlatformLogger logger = new PlatformLogger(
                 mContext,
-                mContext.getUser(),
                 AppSearchConfig.create(DIRECT_EXECUTOR));
         PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
         when(mockPackageManager.getPackageUid(testPackageName, /*flags=*/0)).thenReturn(testUid);
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java b/services/tests/servicestests/src/com/android/server/appsearch/visibilitystore/VisibilityStoreImplTest.java
similarity index 91%
rename from services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java
rename to services/tests/servicestests/src/com/android/server/appsearch/visibilitystore/VisibilityStoreImplTest.java
index b67ebe4..07a728b 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/visibilitystore/VisibilityStoreImplTest.java
@@ -14,10 +14,7 @@
  * limitations under the License.
  */
 
-// TODO(b/169883602): This is purposely a different package from the path so that it can access
-// AppSearchImpl methods without having to make methods public. This should be moved into a proper
-// package once AppSearchImpl-VisibilityStore's dependencies are refactored.
-package com.android.server.appsearch.external.localstorage;
+package com.android.server.appsearch.visibilitystore;
 
 import static android.Manifest.permission.READ_GLOBAL_APP_SEARCH_DATA;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
@@ -39,8 +36,10 @@
 
 import androidx.test.core.app.ApplicationProvider;
 
+import com.android.server.appsearch.external.localstorage.AppSearchImpl;
+import com.android.server.appsearch.external.localstorage.OptimizeStrategy;
 import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
-import com.android.server.appsearch.visibilitystore.VisibilityStore;
+import com.android.server.appsearch.external.localstorage.visibilitystore.VisibilityStore;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -55,7 +54,7 @@
 import java.util.Collections;
 import java.util.Map;
 
-public class VisibilityStoreTest {
+public class VisibilityStoreImplTest {
     /**
      * Always trigger optimize in this class. OptimizeStrategy will be tested in its own test class.
      */
@@ -64,7 +63,7 @@
     @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
     private final Map<UserHandle, PackageManager> mMockPackageManagers = new ArrayMap<>();
     private Context mContext;
-    private VisibilityStore mVisibilityStore;
+    private VisibilityStoreImpl mVisibilityStore;
     private int mUid;
 
     @Before
@@ -90,7 +89,7 @@
         // Give ourselves global query permissions
         AppSearchImpl appSearchImpl = AppSearchImpl.create(
                 mTemporaryFolder.newFolder(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
-        mVisibilityStore = VisibilityStore.create(appSearchImpl, mContext);
+        mVisibilityStore = VisibilityStoreImpl.create(appSearchImpl, mContext);
         mUid = mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0);
     }
 
@@ -132,7 +131,7 @@
     }
 
     @Test
-    public void testSetVisibility_platformSurfaceable() throws Exception {
+    public void testSetVisibility_displayedBySystem() throws Exception {
         // Make sure we have global query privileges
         PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
         when(mockPackageManager
@@ -143,9 +142,9 @@
         mVisibilityStore.setVisibility(
                 "package",
                 "database",
-                /*schemasNotPlatformSurfaceable=*/ ImmutableSet.of(
+                /*schemasNotDisplayedBySystem=*/ ImmutableSet.of(
                         "prefix/schema1", "prefix/schema2"),
-                /*schemasPackageAccessible=*/ Collections.emptyMap());
+                /*schemasVisibleToPackages=*/ Collections.emptyMap());
         assertThat(
                         mVisibilityStore.isSchemaSearchableByCaller(
                                 "package",
@@ -168,9 +167,9 @@
         mVisibilityStore.setVisibility(
                 "package",
                 "database",
-                /*schemasNotPlatformSurfaceable=*/ ImmutableSet.of(
+                /*schemasNotDisplayedBySystem=*/ ImmutableSet.of(
                         "prefix/schema1", "prefix/schema3"),
-                /*schemasPackageAccessible=*/ Collections.emptyMap());
+                /*schemasVisibleToPackages=*/ Collections.emptyMap());
         assertThat(
                         mVisibilityStore.isSchemaSearchableByCaller(
                                 "package",
@@ -200,8 +199,8 @@
         mVisibilityStore.setVisibility(
                 "package",
                 "database",
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
-                /*schemasPackageAccessible=*/ Collections.emptyMap());
+                /*schemasNotDisplayedBySystem=*/ Collections.emptySet(),
+                /*schemasVisibleToPackages=*/ Collections.emptyMap());
         assertThat(
                         mVisibilityStore.isSchemaSearchableByCaller(
                                 "package",
@@ -229,7 +228,7 @@
     }
 
     @Test
-    public void testSetVisibility_packageAccessible() throws Exception {
+    public void testSetVisibility_visibleToPackages() throws Exception {
         // Values for a "foo" client
         String packageNameFoo = "packageFoo";
         byte[] sha256CertFoo = new byte[] {10};
@@ -272,8 +271,8 @@
         mVisibilityStore.setVisibility(
                 "package",
                 "database",
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
-                /*schemasPackageAccessible=*/ ImmutableMap.of(
+                /*schemasNotDisplayedBySystem=*/ Collections.emptySet(),
+                /*schemasVisibleToPackages=*/ ImmutableMap.of(
                         "prefix/schemaFoo",
                         ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo)),
                         "prefix/schemaBar",
@@ -339,8 +338,8 @@
         mVisibilityStore.setVisibility(
                 "package",
                 "database",
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
-                /*schemasPackageAccessible=*/ ImmutableMap.of(
+                /*schemasNotDisplayedBySystem=*/ Collections.emptySet(),
+                /*schemasVisibleToPackages=*/ ImmutableMap.of(
                         "prefix/schemaFoo",
                         ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))));
 
@@ -392,8 +391,8 @@
         mVisibilityStore.setVisibility(
                 "package",
                 "database",
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
-                /*schemasPackageAccessible=*/ ImmutableMap.of(
+                /*schemasNotDisplayedBySystem=*/ Collections.emptySet(),
+                /*schemasVisibleToPackages=*/ ImmutableMap.of(
                         "prefix/schemaFoo",
                         ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))));
 
@@ -425,8 +424,8 @@
         mVisibilityStore.setVisibility(
                 /*packageName=*/ "",
                 /*databaseName=*/ "",
-                /*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
-                /*schemasPackageAccessible=*/ ImmutableMap.of(
+                /*schemasNotDisplayedBySystem=*/ Collections.emptySet(),
+                /*schemasVisibleToPackages=*/ ImmutableMap.of(
                         "schema",
                         ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))));
 
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index c0df2e3..cae6c86 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -48,8 +48,11 @@
 import android.hardware.Sensor;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
+import android.hardware.display.BrightnessInfo;
 import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
 import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
 import android.hardware.display.DisplayManagerInternal.RefreshRateRange;
 import android.hardware.fingerprint.IUdfpsHbmListener;
 import android.os.Handler;
@@ -120,7 +123,7 @@
         mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
         final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContext);
         when(mContext.getContentResolver()).thenReturn(resolver);
-        mInjector = new FakesInjector();
+        mInjector = spy(new FakesInjector());
         mHandler = new Handler(Looper.getMainLooper());
 
         LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
@@ -1256,13 +1259,163 @@
         assertNull(vote);
     }
 
+    @Test
+    public void testHbmVoting_forHdr() {
+        DisplayModeDirector director =
+                createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0);
+        director.start(createMockSensorManager());
+
+        ArgumentCaptor<DisplayListener> captor =
+                  ArgumentCaptor.forClass(DisplayListener.class);
+        verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class),
+                  eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
+                  | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
+        DisplayListener listener = captor.getValue();
+
+        // Specify Limitation
+        when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID)).thenReturn(
+                List.of(new RefreshRateLimitation(
+                        DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE,
+                        60.f, 60.f)));
+
+        // Verify that there is no HBM vote initially
+        Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+        assertNull(vote);
+
+        // Turn on HBM
+        when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+                new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR));
+        listener.onDisplayChanged(DISPLAY_ID);
+        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+        assertVoteForRefreshRate(vote, 60.f);
+
+        // Turn off HBM
+        when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+                new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF));
+        listener.onDisplayChanged(DISPLAY_ID);
+        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+        assertNull(vote);
+    }
+
+    @Test
+    public void testHbmVoting_forSunlight() {
+        DisplayModeDirector director =
+                createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0);
+        director.start(createMockSensorManager());
+
+        ArgumentCaptor<DisplayListener> captor =
+                  ArgumentCaptor.forClass(DisplayListener.class);
+        verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class),
+                  eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
+                  | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
+        DisplayListener listener = captor.getValue();
+
+        // Specify Limitation
+        when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID)).thenReturn(
+                List.of(new RefreshRateLimitation(
+                        DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE,
+                        60.f, 60.f)));
+
+        // Verify that there is no HBM vote initially
+        Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+        assertNull(vote);
+
+        // Turn on HBM
+        when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+                new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT));
+        listener.onDisplayChanged(DISPLAY_ID);
+        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+        assertVoteForRefreshRate(vote, 60.f);
+
+        // Turn off HBM
+        when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+                new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF));
+        listener.onDisplayChanged(DISPLAY_ID);
+        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+        assertNull(vote);
+    }
+
+    @Test
+    public void testHbmVoting_forSunlight_NoLimitation() {
+        DisplayModeDirector director =
+                createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0);
+        director.start(createMockSensorManager());
+
+        ArgumentCaptor<DisplayListener> captor =
+                  ArgumentCaptor.forClass(DisplayListener.class);
+        verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class),
+                  eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
+                  | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
+        DisplayListener listener = captor.getValue();
+
+        // Specify Limitation for different display
+        when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID + 1)).thenReturn(
+                List.of(new RefreshRateLimitation(
+                        DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE,
+                        60.f, 60.f)));
+
+        // Verify that there is no HBM vote initially
+        Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+        assertNull(vote);
+
+        // Turn on HBM
+        when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+                new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT));
+        listener.onDisplayChanged(DISPLAY_ID);
+        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+        assertNull(vote);
+
+        // Turn off HBM
+        when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+                new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF));
+        listener.onDisplayChanged(DISPLAY_ID);
+        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+        assertNull(vote);
+    }
+
+    @Test
+    public void testHbmVoting_RemovedDisplay() {
+        DisplayModeDirector director =
+                createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0);
+        director.start(createMockSensorManager());
+
+        ArgumentCaptor<DisplayListener> captor =
+                  ArgumentCaptor.forClass(DisplayListener.class);
+        verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class),
+                  eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
+                  | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
+        DisplayListener listener = captor.getValue();
+
+        // Specify Limitation for different display
+        when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID)).thenReturn(
+                List.of(new RefreshRateLimitation(
+                        DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE,
+                        60.f, 60.f)));
+
+        // Verify that there is no HBM vote initially
+        Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+        assertNull(vote);
+
+        // Turn on HBM
+        when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+                new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT));
+        listener.onDisplayChanged(DISPLAY_ID);
+        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+        assertVoteForRefreshRate(vote, 60.f);
+
+        // Turn off HBM
+        listener.onDisplayRemoved(DISPLAY_ID);
+        vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+        assertNull(vote);
+    }
+
     private void assertVoteForRefreshRate(Vote vote, float refreshRate) {
         assertThat(vote).isNotNull();
         final RefreshRateRange expectedRange = new RefreshRateRange(refreshRate, refreshRate);
         assertThat(vote.refreshRateRange).isEqualTo(expectedRange);
     }
 
-    private static class FakeDeviceConfig extends FakeDeviceConfigInterface {
+    public static class FakeDeviceConfig extends FakeDeviceConfigInterface {
         @Override
         public String getProperty(String namespace, String name) {
             Preconditions.checkArgument(DeviceConfig.NAMESPACE_DISPLAY_MANAGER.equals(namespace));
@@ -1403,7 +1556,7 @@
         mHandler.runWithScissors(() -> { }, 500 /*timeout*/);
     }
 
-    static class FakesInjector implements DisplayModeDirector.Injector {
+    public static class FakesInjector implements DisplayModeDirector.Injector {
         private final FakeDeviceConfig mDeviceConfig;
         private ContentObserver mBrightnessObserver;
         private ContentObserver mPeakRefreshRateObserver;
@@ -1444,6 +1597,14 @@
             mPeakRefreshRateObserver = observer;
         }
 
+        @Override
+        public void registerDisplayListener(DisplayListener listener, Handler handler, long flag) {}
+
+        @Override
+        public BrightnessInfo getBrightnessInfo(int displayId) {
+            return null;
+        }
+
         void notifyPeakRefreshRateChanged() {
             if (mPeakRefreshRateObserver != null) {
                 mPeakRefreshRateObserver.dispatchChange(false /*selfChange*/,
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 7df2dd6..c572dd6 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -649,7 +649,7 @@
 
     protected class MockAppSearchManager implements IAppSearchManager {
 
-        protected Map<String, List<PackageIdentifier>> mSchemasPackageAccessible =
+        protected Map<String, List<PackageIdentifier>> mSchemasVisibleToPackages =
                 new ArrayMap<>(1);
         private Map<String, Map<String, GenericDocument>> mDocumentMap = new ArrayMap<>(1);
 
@@ -659,19 +659,19 @@
 
         @Override
         public void setSchema(String packageName, String databaseName, List<Bundle> schemaBundles,
-                List<String> schemasNotPlatformSurfaceable,
-                Map<String, List<Bundle>> schemasPackageAccessibleBundles, boolean forceOverride,
+                List<String> schemasNotDisplayedBySystem,
+                Map<String, List<Bundle>> schemasVisibleToPackagesBundles, boolean forceOverride,
                 int version, UserHandle userHandle, long binderCallStartTimeMillis,
                 IAppSearchResultCallback callback) throws RemoteException {
             for (Map.Entry<String, List<Bundle>> entry :
-                    schemasPackageAccessibleBundles.entrySet()) {
+                    schemasVisibleToPackagesBundles.entrySet()) {
                 final String key = entry.getKey();
                 final List<PackageIdentifier> packageIdentifiers;
-                if (!mSchemasPackageAccessible.containsKey(key)) {
+                if (!mSchemasVisibleToPackages.containsKey(key)) {
                     packageIdentifiers = new ArrayList<>(entry.getValue().size());
-                    mSchemasPackageAccessible.put(key, packageIdentifiers);
+                    mSchemasVisibleToPackages.put(key, packageIdentifiers);
                 } else {
-                    packageIdentifiers = mSchemasPackageAccessible.get(key);
+                    packageIdentifiers = mSchemasVisibleToPackages.get(key);
                 }
                 for (int i = 0; i < entry.getValue().size(); i++) {
                     packageIdentifiers.add(new PackageIdentifier(entry.getValue().get(i)));
@@ -785,7 +785,7 @@
         }
 
         @Override
-        public void getNextPage(long nextPageToken, UserHandle userHandle,
+        public void getNextPage(String packageName, long nextPageToken, UserHandle userHandle,
                 IAppSearchResultCallback callback) throws RemoteException {
             final Bundle page = new Bundle();
             page.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, 1);
@@ -795,8 +795,8 @@
         }
 
         @Override
-        public void invalidateNextPageToken(long nextPageToken, UserHandle userHandle)
-                throws RemoteException {
+        public void invalidateNextPageToken(String packageName, long nextPageToken,
+                UserHandle userHandle) throws RemoteException {
         }
 
         @Override
@@ -875,13 +875,13 @@
         }
 
         @Override
-        public void persistToDisk(UserHandle userHandle, long binderCallStartTimeMillis)
-                throws RemoteException {
+        public void persistToDisk(String packageName, UserHandle userHandle,
+                long binderCallStartTimeMillis) throws RemoteException {
         }
 
         @Override
-        public void initialize(UserHandle userHandle, long binderCallStartTimeMillis,
-                IAppSearchResultCallback callback)
+        public void initialize(String packageName, UserHandle userHandle,
+                long binderCallStartTimeMillis, IAppSearchResultCallback callback)
                 throws RemoteException {
             ignore(callback);
         }
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
index 558fb30..976a588 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -18,7 +18,11 @@
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import static org.junit.Assert.fail;
+
 import static java.lang.reflect.Modifier.isFinal;
+import static java.lang.reflect.Modifier.isPrivate;
+import static java.lang.reflect.Modifier.isProtected;
 import static java.lang.reflect.Modifier.isPublic;
 import static java.lang.reflect.Modifier.isStatic;
 
@@ -44,9 +48,12 @@
 
 import java.io.File;
 import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.regex.Pattern;
 
@@ -393,6 +400,178 @@
         Assert.assertEquals(3600000003L, multiPackage[1].timeouts.maxPendingTimeUs);
     }
 
+    // Report an error from the Computer structure validation test.
+    private void flag(String name, String msg) {
+        fail(name + " " + msg);
+    }
+
+    // Return a string that identifies a Method.  This is not very efficient but it is not
+    // called very often.
+    private String displayName(Method m) {
+        String r = m.getName();
+        String p = Arrays.toString(m.getGenericParameterTypes())
+                   .replaceAll("([a-zA-Z0-9]+\\.)+", "")
+                   .replace("class ", "")
+                   .replaceAll("^\\[", "(")
+                   .replaceAll("\\]$", ")");
+        return r + p;
+    }
+
+    // Match a method to an array of Methods.  Matching is on method signature: name and
+    // parameter types.  If a method in the declared array matches, return it.  Otherwise
+    // return null.
+    private Method matchMethod(Method m, Method[] declared) {
+        String n = m.getName();
+        Type[] t = m.getGenericParameterTypes();
+        for (int i = 0; i < declared.length; i++) {
+            Method l = declared[i];
+            if (l != null && l.getName().equals(n)
+                    && Arrays.equals(l.getGenericParameterTypes(), t)) {
+                Method result = l;
+                // Set the method to null since it has been visited already.
+                declared[i] = null;
+                return result;
+            }
+        }
+        return null;
+    }
+
+    // Return the boolean locked value.  A null return means the annotation was not
+    // found.  This method will fail if the annotation is found but is not one of the
+    // known constants.
+    private Boolean getOverride(Method m) {
+        final String name = "Computer." + displayName(m);
+        final PackageManagerService.Computer.LiveImplementation annotation =
+                m.getAnnotation(PackageManagerService.Computer.LiveImplementation.class);
+        if (annotation == null) {
+            return null;
+        }
+        final int override = annotation.override();
+        if (override == PackageManagerService.Computer.LiveImplementation.MANDATORY) {
+            return true;
+        } else if (override == PackageManagerService.Computer.LiveImplementation.NOT_ALLOWED) {
+            return false;
+        } else {
+            flag(name, "invalid Live value: " + override);
+            return null;
+        }
+    }
+
+    @Test
+    public void testComputerStructure() {
+        // Verify that Copmuter methods are properly annotated and that ComputerLocked is
+        // properly populated per annotations.
+        // Call PackageManagerService.validateComputer();
+        Class base = PackageManagerService.Computer.class;
+
+        HashMap<Method, Boolean> methodType = new HashMap<>();
+
+        // Verify that all Computer methods are annotated and that the annotation
+        // parameter locked() is valid.
+        for (Method m : base.getDeclaredMethods()) {
+            final String name = "Computer." + displayName(m);
+            Boolean override = getOverride(m);
+            if (override == null) {
+                flag(name, "missing required Live annotation");
+            }
+            methodType.put(m, override);
+        }
+
+        Class coreClass = PackageManagerService.ComputerEngine.class;
+        final Method[] coreMethods = coreClass.getDeclaredMethods();
+
+        // Examine every method in the core.  If it inherits from a base method it must be
+        // "public final" if the base is NOT_ALLOWED or "public" if the base is MANDATORY.
+        // If the core method does not inherit from the base then it must be either
+        // private or protected.
+        for (Method m : base.getDeclaredMethods()) {
+            String name = "Computer." + displayName(m);
+            final boolean locked = methodType.get(m);
+            final Method core = matchMethod(m, coreMethods);
+            if (core == null) {
+                flag(name, "not overridden in ComputerEngine");
+                continue;
+            }
+            name = "ComputerEngine." + displayName(m);
+            final int modifiers = core.getModifiers();
+            if (!locked) {
+                if (!isPublic(modifiers)) {
+                    flag(name, "is not public");
+                }
+                if (!isFinal(modifiers)) {
+                    flag(name, "is not final");
+                }
+            }
+        }
+        // Any methods left in the coreMethods array must be private or protected.
+        // Protected methods must be overridden (and final) in the live list.
+        Method[] coreHelpers = new Method[coreMethods.length];
+        int coreIndex = 0;
+        for (Method m : coreMethods) {
+            if (m != null) {
+                final String name = "ComputerEngine." + displayName(m);
+                final int modifiers = m.getModifiers();
+                if (isPrivate(modifiers)) {
+                    // Okay
+                } else if (isProtected(modifiers)) {
+                    coreHelpers[coreIndex++] = m;
+                } else {
+                    flag(name, "is neither private nor protected");
+                }
+            }
+        }
+
+        Class liveClass = PackageManagerService.ComputerLocked.class;
+        final Method[] liveMethods = liveClass.getDeclaredMethods();
+
+        // Examine every method in the live list.  Every method must be final and must
+        // inherit either from base or core.  If the method inherits from a base method
+        // then the base must be MANDATORY.
+        for (Method m : base.getDeclaredMethods()) {
+            String name = "Computer." + displayName(m);
+            final boolean locked = methodType.get(m);
+            final Method live = matchMethod(m, liveMethods);
+            if (live == null) {
+                if (locked) {
+                    flag(name, "not overridden in ComputerLocked");
+                }
+                continue;
+            }
+            if (!locked) {
+                flag(name, "improperly overridden in ComputerLocked");
+                continue;
+            }
+
+            name = "ComputerLocked." + displayName(m);
+            final int modifiers = live.getModifiers();
+            if (!locked) {
+                if (!isPublic(modifiers)) {
+                    flag(name, "is not public");
+                }
+                if (!isFinal(modifiers)) {
+                    flag(name, "is not final");
+                }
+            }
+        }
+        for (Method m : coreHelpers) {
+            if (m == null) {
+                continue;
+            }
+            String name = "ComputerLocked." + displayName(m);
+            final Method live = matchMethod(m, liveMethods);
+            if (live == null) {
+                flag(name, "is not overridden in ComputerLocked");
+                continue;
+            }
+        }
+        for (Method m : liveMethods) {
+            if (m != null) {
+                String name = "ComputerLocked." + displayName(m);
+                flag(name, "illegal local method");
+            }
+        }
+    }
+
     private static PerPackageReadTimeouts[] getPerPackageReadTimeouts(String knownDigestersList) {
         final String defaultTimeouts = "3600000001:3600000002:3600000003";
         List<PerPackageReadTimeouts> result = PerPackageReadTimeouts.parseDigestersList(
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index 17d99e6..9044b27 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -222,6 +222,64 @@
     }
 
     /**
+     * Tests that readPermissions works correctly with {@link SystemConfig#ALLOW_VENDOR_APEX}
+     * permission flag for the tag: {@code allowed-vendor-apex}.
+     */
+    @Test
+    public void readPermissions_allowVendorApex_parsesVendorApexAllowList()
+            throws IOException {
+        final String contents =
+                "<config>\n"
+                        + "    <allowed-vendor-apex package=\"com.android.apex1\" />\n"
+                        + "</config>";
+        final File folder = createTempSubfolder("folder");
+        createTempFile(folder, "vendor-apex-allowlist.xml", contents);
+
+        mSysConfig.readPermissions(folder, /* Grant all permission flags */ ~0);
+
+        assertThat(mSysConfig.getAllowedVendorApexes()).containsExactly("com.android.apex1");
+    }
+
+    /**
+     * Tests that readPermissions works correctly with {@link SystemConfig#ALLOW_VENDOR_APEX}
+     * permission flag for the tag: {@code allowed-vendor-apex}.
+     */
+    @Test
+    public void readPermissions_allowVendorApex_parsesVendorApexAllowList_noPackage()
+            throws IOException {
+        final String contents =
+                "<config>\n"
+                        + "    <allowed-vendor-apex/>\n"
+                        + "</config>";
+        final File folder = createTempSubfolder("folder");
+        createTempFile(folder, "vendor-apex-allowlist.xml", contents);
+
+        mSysConfig.readPermissions(folder, /* Grant all permission flags */ ~0);
+
+        assertThat(mSysConfig.getAllowedVendorApexes()).isEmpty();
+    }
+
+
+    /**
+     * Tests that readPermissions works correctly without {@link SystemConfig#ALLOW_VENDOR_APEX}
+     * permission flag for the tag: {@code allowed-oem-apex}.
+     */
+    @Test
+    public void readPermissions_notAllowVendorApex_doesNotParseVendorApexAllowList()
+            throws IOException {
+        final String contents =
+                "<config>\n"
+                        + "    <allowed-vendor-apex package=\"com.android.apex1\" />\n"
+                        + "</config>";
+        final File folder = createTempSubfolder("folder");
+        createTempFile(folder, "vendor-apex-allowlist.xml", contents);
+
+        mSysConfig.readPermissions(folder, /* Grant all but ALLOW_VENDOR_APEX flag */ ~0x400);
+
+        assertThat(mSysConfig.getAllowedVendorApexes()).isEmpty();
+    }
+
+    /**
      * Creates folderName/fileName in the mTemporaryFolder and fills it with the contents.
      *
      * @param folderName subdirectory of mTemporaryFolder to put the file, creating if needed
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index a246917..4c157c0 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -1179,6 +1179,36 @@
         assertBucket(STANDBY_BUCKET_RESTRICTED);
     }
 
+    /**
+     * Test that an app that "timed out" into the RESTRICTED bucket can be raised out by system
+     * interaction.
+     */
+    @Test
+    public void testSystemInteractionOverridesRestrictedTimeout() throws Exception {
+        reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+
+        // Long enough that it could have timed out into RESTRICTED.
+        mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
+        mController.checkIdleStates(USER_ID);
+        assertBucket(STANDBY_BUCKET_RESTRICTED);
+
+        // Report system interaction.
+        mInjector.mElapsedRealtime += 1000;
+        reportEvent(mController, SYSTEM_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
+
+        // Ensure that it's raised out of RESTRICTED for the system interaction elevation duration.
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+        mInjector.mElapsedRealtime += 1000;
+        mController.checkIdleStates(USER_ID);
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+
+        // Elevation duration over. Should fall back down.
+        mInjector.mElapsedRealtime += 10 * MINUTE_MS;
+        mController.checkIdleStates(USER_ID);
+        assertBucket(STANDBY_BUCKET_RESTRICTED);
+    }
+
     @Test
     public void testRestrictedBucketDisabled() throws Exception {
         mInjector.mIsRestrictedBucketEnabled = false;
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
index 0585758..0449e44 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -56,6 +56,8 @@
     private int[] mSupportedEffects;
     private int[] mSupportedBraking;
     private int[] mSupportedPrimitives;
+    private int mCompositionSizeMax;
+    private int mPwleSizeMax;
     private float mMinFrequency = Float.NaN;
     private float mResonantFrequency = Float.NaN;
     private float mFrequencyResolution = Float.NaN;
@@ -151,12 +153,22 @@
         }
 
         @Override
-        public VibratorInfo getInfo(float suggestedFrequencyRange) {
-            VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping(
-                    mMinFrequency, mResonantFrequency, mFrequencyResolution,
-                    suggestedFrequencyRange, mMaxAmplitudes);
-            return new VibratorInfo(vibratorId, mCapabilities, mSupportedEffects, mSupportedBraking,
-                    mSupportedPrimitives, null, mQFactor, frequencyMapping);
+        public boolean getInfo(float suggestedFrequencyRange, VibratorInfo.Builder infoBuilder) {
+            infoBuilder.setCapabilities(mCapabilities);
+            infoBuilder.setSupportedBraking(mSupportedBraking);
+            infoBuilder.setPwleSizeMax(mPwleSizeMax);
+            infoBuilder.setSupportedEffects(mSupportedEffects);
+            if (mSupportedPrimitives != null) {
+                for (int primitive : mSupportedPrimitives) {
+                    infoBuilder.setSupportedPrimitive(primitive, EFFECT_DURATION);
+                }
+            }
+            infoBuilder.setCompositionSizeMax(mCompositionSizeMax);
+            infoBuilder.setQFactor(mQFactor);
+            infoBuilder.setFrequencyMapping(new VibratorInfo.FrequencyMapping(mMinFrequency,
+                    mResonantFrequency, mFrequencyResolution, suggestedFrequencyRange,
+                    mMaxAmplitudes));
+            return true;
         }
 
         private void applyLatency() {
@@ -236,6 +248,16 @@
         mSupportedPrimitives = primitives;
     }
 
+    /** Set the max number of primitives allowed in a composition by the fake vibrator hardware. */
+    public void setCompositionSizeMax(int compositionSizeMax) {
+        mCompositionSizeMax = compositionSizeMax;
+    }
+
+    /** Set the max number of PWLEs allowed in a composition by the fake vibrator hardware. */
+    public void setPwleSizeMax(int pwleSizeMax) {
+        mPwleSizeMax = pwleSizeMax;
+    }
+
     /** Set the resonant frequency of the fake vibrator hardware. */
     public void setResonantFrequency(float frequencyHz) {
         mResonantFrequency = frequencyHz;
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java b/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java
index f4eb2de..32988ef 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java
@@ -68,6 +68,38 @@
     }
 
     @Test
+    public void testRampSegments_withPwleDurationLimit_splitsLongRamps() {
+        List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
+                new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
+                        /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
+                new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 1,
+                        /* startFrequency= */ 0, /* endFrequency= */ -1, /* duration= */ 25),
+                new RampSegment(/* startAmplitude= */ 1, /* endAmplitude*/ 1,
+                        /* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 5)));
+        List<VibrationEffectSegment> expectedSegments = Arrays.asList(
+                new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
+                        /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
+                new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0.32f,
+                        /* startFrequency= */ 0, /* endFrequency= */ -0.32f, /* duration= */ 8),
+                new RampSegment(/* startAmplitude= */ 0.32f, /* endAmplitude= */ 0.64f,
+                        /* startFrequency= */ -0.32f, /* endFrequency= */ -0.64f,
+                        /* duration= */ 8),
+                new RampSegment(/* startAmplitude= */ 0.64f, /* endAmplitude= */ 1,
+                        /* startFrequency= */ -0.64f, /* endFrequency= */ -1, /* duration= */ 9),
+                new RampSegment(/* startAmplitude= */ 1, /* endAmplitude*/ 1,
+                        /* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 5));
+
+        VibratorInfo vibratorInfo = new VibratorInfo.Builder(0)
+                .setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)
+                .setPwlePrimitiveDurationMax(10)
+                .build();
+
+        // Update repeat index to skip the ramp splits.
+        assertEquals(4, mAdapter.apply(segments, 2, vibratorInfo));
+        assertEquals(expectedSegments, segments);
+    }
+
+    @Test
     public void testStepAndRampSegments_withoutPwleCapability_keepsListUnchanged() {
         mAdapter = new StepToRampAdapter(50);
 
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
index 85501245..7d24a2f 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -33,6 +33,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.Intent;
 import android.media.AudioManager;
 import android.os.Handler;
 import android.os.PowerManagerInternal;
@@ -69,6 +70,7 @@
 public class VibrationSettingsTest {
 
     private static final int UID = 1;
+    private static final int USER_OPERATION_TIMEOUT_MILLIS = 60_000; // 1 min
     private static final PowerSaveState NORMAL_POWER_STATE = new PowerSaveState.Builder().build();
     private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder()
             .setBatterySaverEnabled(true).build();
@@ -408,6 +410,25 @@
     }
 
     @Test
+    public void getCurrentIntensity_updateTriggeredAfterUserSwitched() {
+        mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_OFF);
+        setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
+                Vibrator.VIBRATION_INTENSITY_HIGH);
+        assertEquals(Vibrator.VIBRATION_INTENSITY_HIGH,
+                mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_RINGTONE));
+
+        // Switching user is not working with FakeSettingsProvider.
+        // Testing the broadcast flow manually.
+        Settings.System.putIntForUser(mContextSpy.getContentResolver(),
+                Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW,
+                UserHandle.USER_CURRENT);
+        mVibrationSettings.mUserReceiver.onReceive(mContextSpy,
+                new Intent(Intent.ACTION_USER_SWITCHED));
+        assertEquals(Vibrator.VIBRATION_INTENSITY_LOW,
+                mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_RINGTONE));
+    }
+
+    @Test
     public void getFallbackEffect_returnsEffectsFromSettings() {
         assertNotNull(mVibrationSettings.getFallbackEffect(VibrationEffect.EFFECT_TICK));
         assertNotNull(mVibrationSettings.getFallbackEffect(VibrationEffect.EFFECT_TEXTURE_TICK));
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
index f02e2f0..b8fdb55 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -356,7 +356,8 @@
 
     @Test
     public void vibrate_singleVibratorComposed_runsVibration() throws Exception {
-        mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+        FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
+        fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
 
         long vibrationId = 1;
         VibrationEffect effect = VibrationEffect.startComposition()
@@ -374,7 +375,7 @@
         assertEquals(Arrays.asList(
                 expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0),
                 expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f, 0)),
-                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
+                fakeVibrator.getEffectSegments());
     }
 
     @Test
@@ -395,6 +396,27 @@
     }
 
     @Test
+    public void vibrate_singleVibratorLargeComposition_splitsVibratorComposeCalls() {
+        FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
+        fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+        fakeVibrator.setCompositionSizeMax(2);
+
+        long vibrationId = 1;
+        VibrationEffect effect = VibrationEffect.startComposition()
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SPIN, 0.8f)
+                .compose();
+        VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
+        waitForCompletion(thread);
+
+        verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
+        // Vibrator compose called twice.
+        verify(mControllerCallbacks, times(2)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
+        assertEquals(3, fakeVibrator.getEffectSegments().size());
+    }
+
+    @Test
     public void vibrate_singleVibratorComposedEffects_runsDifferentVibrations() throws Exception {
         mVibratorProviders.get(VIBRATOR_ID).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
         mVibratorProviders.get(VIBRATOR_ID).setSupportedPrimitives(
@@ -432,12 +454,13 @@
 
     @Test
     public void vibrate_singleVibratorPwle_runsComposePwle() throws Exception {
-        mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
-        mVibratorProviders.get(VIBRATOR_ID).setSupportedBraking(Braking.CLAB);
-        mVibratorProviders.get(VIBRATOR_ID).setMinFrequency(100);
-        mVibratorProviders.get(VIBRATOR_ID).setResonantFrequency(150);
-        mVibratorProviders.get(VIBRATOR_ID).setFrequencyResolution(50);
-        mVibratorProviders.get(VIBRATOR_ID).setMaxAmplitudes(
+        FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
+        fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+        fakeVibrator.setSupportedBraking(Braking.CLAB);
+        fakeVibrator.setMinFrequency(100);
+        fakeVibrator.setResonantFrequency(150);
+        fakeVibrator.setFrequencyResolution(50);
+        fakeVibrator.setMaxAmplitudes(
                 0.5f /* 100Hz*/, 1 /* 150Hz */, 0.6f /* 200Hz */);
 
         long vibrationId = 1;
@@ -462,8 +485,34 @@
                 expectedRamp(/* amplitude= */ 0.6f, /* frequency= */ 200, /* duration= */ 30),
                 expectedRamp(/* StartAmplitude= */ 0.6f, /* endAmplitude= */ 0.5f,
                         /* startFrequency= */ 200, /* endFrequency= */ 100, /* duration= */ 40)),
-                mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
-        assertEquals(Arrays.asList(Braking.CLAB), mVibratorProviders.get(VIBRATOR_ID).getBraking());
+                fakeVibrator.getEffectSegments());
+        assertEquals(Arrays.asList(Braking.CLAB), fakeVibrator.getBraking());
+    }
+
+    @Test
+    public void vibrate_singleVibratorLargePwle_splitsVibratorComposeCalls() {
+        FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
+        fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+        fakeVibrator.setMinFrequency(100);
+        fakeVibrator.setResonantFrequency(150);
+        fakeVibrator.setFrequencyResolution(50);
+        fakeVibrator.setMaxAmplitudes(1, 1, 1);
+        fakeVibrator.setPwleSizeMax(2);
+
+        long vibrationId = 1;
+        VibrationEffect effect = VibrationEffect.startWaveform()
+                .addStep(1, 10)
+                .addRamp(0, 20)
+                .addStep(0.8f, 1, 30)
+                .addRamp(0.6f, -1, 40)
+                .build();
+        VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
+        waitForCompletion(thread);
+
+        verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
+        // Vibrator compose called twice.
+        verify(mControllerCallbacks, times(2)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
+        assertEquals(4, fakeVibrator.getEffectSegments().size());
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java
index 9e98e7d..a732bd1 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java
@@ -298,11 +298,13 @@
     private void mockVibratorCapabilities(int capabilities) {
         VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping(
                 Float.NaN, Float.NaN, Float.NaN, Float.NaN, null);
-        when(mNativeWrapperMock.getInfo(anyFloat())).thenReturn(
-                new VibratorInfo.Builder(VIBRATOR_ID)
-                        .setCapabilities(capabilities)
-                        .setFrequencyMapping(frequencyMapping)
-                        .build());
+        when(mNativeWrapperMock.getInfo(anyFloat(), any(VibratorInfo.Builder.class)))
+                .then(invocation -> {
+                    ((VibratorInfo.Builder) invocation.getArgument(1))
+                            .setCapabilities(capabilities)
+                            .setFrequencyMapping(frequencyMapping);
+                    return true;
+                });
     }
 
     private PrebakedSegment createPrebaked(int effectId, int effectStrength) {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 3862d75..71c05b5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -48,6 +48,7 @@
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
+import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.Notification.Builder;
 import android.app.NotificationChannel;
@@ -111,6 +112,8 @@
     NotificationUsageStats mUsageStats;
     @Mock
     IAccessibilityManager mAccessibilityService;
+    @Mock
+    KeyguardManager mKeyguardManager;
     NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake();
     private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake(
             1 << 30);
@@ -153,6 +156,7 @@
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
         when(mUsageStats.isAlertRateLimited(any())).thenReturn(false);
         when(mVibrator.hasFrequencyControl()).thenReturn(false);
+        when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(false);
 
         long serviceReturnValue = IntPair.of(
                 AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED,
@@ -174,6 +178,7 @@
         mService.setScreenOn(false);
         mService.setUsageStats(mUsageStats);
         mService.setAccessibilityManager(accessibilityManager);
+        mService.setKeyguardManager(mKeyguardManager);
         mService.mScreenOn = false;
         mService.mInCallStateOffHook = false;
         mService.mNotificationPulseEnabled = true;
@@ -496,6 +501,94 @@
     }
 
     @Test
+    public void testLockedPrivateA11yRedaction() throws Exception {
+        NotificationRecord r = getBeepyNotification();
+        r.setPackageVisibilityOverride(NotificationManager.VISIBILITY_NO_OVERRIDE);
+        r.getNotification().visibility = Notification.VISIBILITY_PRIVATE;
+        when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(true);
+        AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class);
+        when(accessibilityManager.isEnabled()).thenReturn(true);
+        mService.setAccessibilityManager(accessibilityManager);
+
+        mService.buzzBeepBlinkLocked(r);
+
+        ArgumentCaptor<AccessibilityEvent> eventCaptor =
+                ArgumentCaptor.forClass(AccessibilityEvent.class);
+
+        verify(accessibilityManager, times(1))
+                .sendAccessibilityEvent(eventCaptor.capture());
+
+        AccessibilityEvent event = eventCaptor.getValue();
+        assertEquals(r.getNotification().publicVersion, event.getParcelableData());
+    }
+
+    @Test
+    public void testLockedOverridePrivateA11yRedaction() throws Exception {
+        NotificationRecord r = getBeepyNotification();
+        r.setPackageVisibilityOverride(Notification.VISIBILITY_PRIVATE);
+        r.getNotification().visibility = Notification.VISIBILITY_PUBLIC;
+        when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(true);
+        AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class);
+        when(accessibilityManager.isEnabled()).thenReturn(true);
+        mService.setAccessibilityManager(accessibilityManager);
+
+        mService.buzzBeepBlinkLocked(r);
+
+        ArgumentCaptor<AccessibilityEvent> eventCaptor =
+                ArgumentCaptor.forClass(AccessibilityEvent.class);
+
+        verify(accessibilityManager, times(1))
+                .sendAccessibilityEvent(eventCaptor.capture());
+
+        AccessibilityEvent event = eventCaptor.getValue();
+        assertEquals(r.getNotification().publicVersion, event.getParcelableData());
+    }
+
+    @Test
+    public void testLockedPublicA11yNoRedaction() throws Exception {
+        NotificationRecord r = getBeepyNotification();
+        r.setPackageVisibilityOverride(NotificationManager.VISIBILITY_NO_OVERRIDE);
+        r.getNotification().visibility = Notification.VISIBILITY_PUBLIC;
+        when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(true);
+        AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class);
+        when(accessibilityManager.isEnabled()).thenReturn(true);
+        mService.setAccessibilityManager(accessibilityManager);
+
+        mService.buzzBeepBlinkLocked(r);
+
+        ArgumentCaptor<AccessibilityEvent> eventCaptor =
+                ArgumentCaptor.forClass(AccessibilityEvent.class);
+
+        verify(accessibilityManager, times(1))
+                .sendAccessibilityEvent(eventCaptor.capture());
+
+        AccessibilityEvent event = eventCaptor.getValue();
+        assertEquals(r.getNotification(), event.getParcelableData());
+    }
+
+    @Test
+    public void testUnlockedPrivateA11yNoRedaction() throws Exception {
+        NotificationRecord r = getBeepyNotification();
+        r.setPackageVisibilityOverride(NotificationManager.VISIBILITY_NO_OVERRIDE);
+        r.getNotification().visibility = Notification.VISIBILITY_PRIVATE;
+        when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(false);
+        AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class);
+        when(accessibilityManager.isEnabled()).thenReturn(true);
+        mService.setAccessibilityManager(accessibilityManager);
+
+        mService.buzzBeepBlinkLocked(r);
+
+        ArgumentCaptor<AccessibilityEvent> eventCaptor =
+                ArgumentCaptor.forClass(AccessibilityEvent.class);
+
+        verify(accessibilityManager, times(1))
+                .sendAccessibilityEvent(eventCaptor.capture());
+
+        AccessibilityEvent event = eventCaptor.getValue();
+        assertEquals(r.getNotification(), event.getParcelableData());
+    }
+
+    @Test
     public void testBeepInsistently() throws Exception {
         NotificationRecord r = getInsistentBeepyNotification();
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index a522b5c..1f543a1 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -94,6 +94,7 @@
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
@@ -2425,6 +2426,8 @@
         when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
                 eq(mTestNotificationChannel.getId()), anyBoolean()))
                 .thenReturn(mTestNotificationChannel);
+        when(mPreferencesHelper.deleteNotificationChannel(eq(PKG), anyInt(),
+                eq(mTestNotificationChannel.getId()))).thenReturn(true);
         reset(mListeners);
         mBinderService.deleteNotificationChannel(PKG, mTestNotificationChannel.getId());
         verify(mListeners, times(1)).notifyNotificationChannelChanged(eq(PKG),
@@ -2433,6 +2436,22 @@
     }
 
     @Test
+    public void testDeleteChannelOnlyDoExtraWorkIfExisted() throws Exception {
+        List<String> associations = new ArrayList<>();
+        associations.add("a");
+        when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
+                .thenReturn(associations);
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
+                eq(mTestNotificationChannel.getId()), anyBoolean()))
+                .thenReturn(null);
+        reset(mListeners);
+        mBinderService.deleteNotificationChannel(PKG, mTestNotificationChannel.getId());
+        verifyNoMoreInteractions(mListeners);
+        verifyNoMoreInteractions(mHistoryManager);
+    }
+
+    @Test
     public void testDeleteChannelGroupNotifyListener() throws Exception {
         List<String> associations = new ArrayList<>();
         associations.add("a");
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 3a51ff2..23da02c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -3333,6 +3333,17 @@
     }
 
     @Test
+    public void testDeleted_twice() throws Exception {
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+                mAppOpsManager, mStatsEventBuilderFactory);
+
+        mHelper.createNotificationChannel(
+                PKG_P, UID_P, createNotificationChannel("id", "id", 2), true, false);
+        assertTrue(mHelper.deleteNotificationChannel(PKG_P, UID_P, "id"));
+        assertFalse(mHelper.deleteNotificationChannel(PKG_P, UID_P, "id"));
+    }
+
+    @Test
     public void testDeleted_recentTime() throws Exception {
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
                 mAppOpsManager, mStatsEventBuilderFactory);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 9267285..9cf29d4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.KeyguardManager.ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -53,6 +54,7 @@
 import static org.mockito.ArgumentMatchers.contains;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.refEq;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -1012,12 +1014,26 @@
         // Create another activity on top and the user id is 1
         final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task)
                 .setUid(UserHandle.PER_USER_RANGE + 1).build();
+        doReturn(true).when(topActivity).okToShowLocked();
+        topActivity.intent.setAction(Intent.ACTION_MAIN);
 
         // Make sure the listeners will be notified for putting the task to locked state
         TaskChangeNotificationController controller = mAtm.getTaskChangeNotificationController();
         spyOn(controller);
         mWm.mRoot.lockAllProfileTasks(0);
         verify(controller).notifyTaskProfileLocked(eq(taskId), eq(0));
+
+        // Create the work lock activity on top of the task
+        final ActivityRecord workLockActivity = new ActivityBuilder(mAtm).setTask(task)
+                .setUid(UserHandle.PER_USER_RANGE + 1).build();
+        doReturn(true).when(workLockActivity).okToShowLocked();
+        workLockActivity.intent.setAction(ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER);
+        doReturn(workLockActivity.mActivityComponent).when(mAtm).getSysUiServiceComponentLocked();
+
+        // Make sure the listener won't be notified again.
+        clearInvocations(controller);
+        mWm.mRoot.lockAllProfileTasks(0);
+        verify(controller, never()).notifyTaskProfileLocked(anyInt(), anyInt());
     }
 
     /**
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 4e261de..4872ec5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -401,6 +401,7 @@
         assertFitted();
 
         final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
+        final Rect currentAppBounds = mActivity.getWindowConfiguration().getAppBounds();
         final Rect originalBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
 
         final int notchHeight = 100;
@@ -428,8 +429,8 @@
         // Because the display cannot rotate, the portrait activity will fit the short side of
         // display with keeping portrait bounds [200, 0 - 700, 1000] in center.
         assertEquals(newDisplayBounds.height(), currentBounds.height());
-        assertEquals(currentBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(),
-                currentBounds.width());
+        assertEquals(currentAppBounds.height() * newDisplayBounds.height()
+                / newDisplayBounds.width(), currentAppBounds.width());
         assertFitted();
         // The appBounds should be [200, 100 - 700, 1000].
         final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index e9e2013..97afc16 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -214,8 +214,8 @@
         spyOn(mDisplayContent);
         spyOn(mDisplayContent.mInputMethodWindow);
         when(task.getDisplayContent().shouldImeAttachedToApp()).thenReturn(true);
-        // Intentionally set the IME window is in drawn state.
-        doReturn(true).when(mDisplayContent.mInputMethodWindow).isDrawn();
+        // Intentionally set the IME window is in visible state.
+        doReturn(true).when(mDisplayContent.mInputMethodWindow).isVisible();
         // Verify no NPE happens when calling createTaskSnapshot.
         try {
             final TaskSnapshot.Builder builder = new TaskSnapshot.Builder();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index d93ebb3..0ebff1d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -60,6 +60,7 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.same;
+import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.never;
 
@@ -121,8 +122,8 @@
 
     @Test
     public void testRemoveContainer() {
-        final Task taskController1 = createTask(mDisplayContent);
-        final Task task = createTaskInRootTask(taskController1, 0 /* userId */);
+        final Task rootTask = createTask(mDisplayContent);
+        final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
         final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
 
         task.removeIfPossible();
@@ -130,12 +131,14 @@
         assertNull(task.getParent());
         assertEquals(0, task.getChildCount());
         assertNull(activity.getParent());
+        verify(mAtm.getLockTaskController(), atLeast(1)).clearLockedTask(task);
+        verify(mAtm.getLockTaskController(), atLeast(1)).clearLockedTask(rootTask);
     }
 
     @Test
     public void testRemoveContainer_deferRemoval() {
-        final Task taskController1 = createTask(mDisplayContent);
-        final Task task = createTaskInRootTask(taskController1, 0 /* userId */);
+        final Task rootTask = createTask(mDisplayContent);
+        final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
         final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
 
         doReturn(true).when(task).shouldDeferRemoval();
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 17303a4..beaca68 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -29,11 +29,7 @@
 import android.content.Intent;
 import android.hardware.soundtrigger.IRecognitionStatusCallback;
 import android.hardware.soundtrigger.SoundTrigger;
-import android.media.AudioAttributes;
 import android.media.AudioFormat;
-import android.media.AudioManager;
-import android.media.AudioRecord;
-import android.media.MediaRecorder;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -66,12 +62,16 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.PrintWriter;
+import java.time.Duration;
+import java.time.Instant;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Function;
 
 /**
  * A class that provides the communication with the HotwordDetectionService.
@@ -81,33 +81,38 @@
     // TODO (b/177502877): Set the Debug flag to false before shipping.
     private static final boolean DEBUG = true;
 
-    // Number of bytes per sample of audio (which is a short).
-    private static final int BYTES_PER_SAMPLE = 2;
     // TODO: These constants need to be refined.
     private static final long VALIDATION_TIMEOUT_MILLIS = 3000;
-    private static final long VOICE_INTERACTION_TIMEOUT_TO_OPEN_MIC_MILLIS = 2000;
-    private static final int MAX_STREAMING_SECONDS = 10;
-    private static final int MICROPHONE_BUFFER_LENGTH_SECONDS = 8;
-    private static final int HOTWORD_AUDIO_LENGTH_SECONDS = 3;
     private static final long MAX_UPDATE_TIMEOUT_MILLIS = 6000;
+    private static final Duration MAX_UPDATE_TIMEOUT_DURATION =
+            Duration.ofMillis(MAX_UPDATE_TIMEOUT_MILLIS);
 
     private final Executor mAudioCopyExecutor = Executors.newCachedThreadPool();
     // TODO: This may need to be a Handler(looper)
     private final ScheduledExecutorService mScheduledExecutorService =
             Executors.newSingleThreadScheduledExecutor();
-    private final AtomicBoolean mUpdateStateFinish = new AtomicBoolean(false);
+    private final AtomicBoolean mUpdateStateAfterStartFinished = new AtomicBoolean(false);
+    private final @NonNull ServiceConnectionFactory mServiceConnectionFactory;
 
     final Object mLock;
     final int mVoiceInteractionServiceUid;
     final ComponentName mDetectionComponentName;
     final int mUser;
     final Context mContext;
-    final @NonNull ServiceConnector<IHotwordDetectionService> mRemoteHotwordDetectionService;
-    boolean mBound;
     volatile HotwordDetectionServiceIdentity mIdentity;
+    private IHotwordRecognitionStatusCallback mCallback;
+    private IMicrophoneHotwordDetectionVoiceInteractionCallback mSoftwareCallback;
+    private Instant mLastRestartInstant;
+
+    private ScheduledFuture<?> mCancellationTaskFuture;
 
     @GuardedBy("mLock")
     private ParcelFileDescriptor mCurrentAudioSink;
+    @GuardedBy("mLock")
+    private boolean mValidatingDspTrigger = false;
+    @GuardedBy("mLock")
+    private boolean mPerformingSoftwareHotwordDetection;
+    private @NonNull ServiceConnection mRemoteHotwordDetectionService;
 
     HotwordDetectionConnection(Object lock, Context context, int voiceInteractionServiceUid,
             ComponentName serviceName, int userId, boolean bindInstantServiceAllowed,
@@ -121,50 +126,36 @@
         final Intent intent = new Intent(HotwordDetectionService.SERVICE_INTERFACE);
         intent.setComponent(mDetectionComponentName);
 
-        mRemoteHotwordDetectionService = new ServiceConnector.Impl<IHotwordDetectionService>(
-                mContext, intent, bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0, mUser,
-                IHotwordDetectionService.Stub::asInterface) {
-            @Override // from ServiceConnector.Impl
-            protected void onServiceConnectionStatusChanged(IHotwordDetectionService service,
-                    boolean connected) {
-                if (DEBUG) {
-                    Slog.d(TAG, "onServiceConnectionStatusChanged connected = " + connected);
-                }
-                synchronized (mLock) {
-                    mBound = connected;
-                }
-            }
+        mServiceConnectionFactory = new ServiceConnectionFactory(intent, bindInstantServiceAllowed);
 
-            @Override
-            protected long getAutoDisconnectTimeoutMs() {
-                return -1;
-            }
+        mRemoteHotwordDetectionService = mServiceConnectionFactory.create();
 
-            @Override
-            public void binderDied() {
-                super.binderDied();
-                Slog.w(TAG, "binderDied");
-                try {
-                    callback.onError(-1);
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "Failed to report onError status: " + e);
-                }
-            }
-        };
-        mRemoteHotwordDetectionService.connect();
         if (callback == null) {
             updateStateLocked(options, sharedMemory);
             return;
         }
-        updateAudioFlinger();
-        updateContentCaptureManager();
-        updateStateWithCallbackLocked(options, sharedMemory, callback);
+        mCallback = callback;
+
+        mLastRestartInstant = Instant.now();
+        updateStateAfterProcessStart(options, sharedMemory);
+
+        // TODO(volnov): we need to be smarter here, e.g. schedule it a bit more often, but wait
+        // until the current session is closed.
+        mCancellationTaskFuture = mScheduledExecutorService.scheduleAtFixedRate(() -> {
+            if (DEBUG) {
+                Slog.i(TAG, "Time to restart the process, TTL has passed");
+            }
+
+            synchronized (mLock) {
+                restartProcessLocked();
+            }
+        }, 30, 30, TimeUnit.MINUTES);
     }
 
-    private void updateStateWithCallbackLocked(PersistableBundle options,
-            SharedMemory sharedMemory, IHotwordRecognitionStatusCallback callback) {
+    private void updateStateAfterProcessStart(
+            PersistableBundle options, SharedMemory sharedMemory) {
         if (DEBUG) {
-            Slog.d(TAG, "updateStateWithCallbackLocked");
+            Slog.d(TAG, "updateStateAfterProcessStart");
         }
         mRemoteHotwordDetectionService.postAsync(service -> {
             AndroidFuture<Void> future = new AndroidFuture<>();
@@ -183,21 +174,21 @@
                     mIdentity =
                             new HotwordDetectionServiceIdentity(uid, mVoiceInteractionServiceUid);
                     future.complete(null);
+                    if (mUpdateStateAfterStartFinished.getAndSet(true)) {
+                        Slog.w(TAG, "call callback after timeout");
+                        return;
+                    }
+                    int status = bundle != null ? bundle.getInt(
+                            KEY_INITIALIZATION_STATUS,
+                            INITIALIZATION_STATUS_UNKNOWN)
+                            : INITIALIZATION_STATUS_UNKNOWN;
+                    // Add the protection to avoid unexpected status
+                    if (status > HotwordDetectionService.getMaxCustomInitializationStatus()
+                            && status != INITIALIZATION_STATUS_UNKNOWN) {
+                        status = INITIALIZATION_STATUS_UNKNOWN;
+                    }
                     try {
-                        if (mUpdateStateFinish.getAndSet(true)) {
-                            Slog.w(TAG, "call callback after timeout");
-                            return;
-                        }
-                        int status = bundle != null ? bundle.getInt(
-                                KEY_INITIALIZATION_STATUS,
-                                INITIALIZATION_STATUS_UNKNOWN)
-                                : INITIALIZATION_STATUS_UNKNOWN;
-                        // Add the protection to avoid unexpected status
-                        if (status > HotwordDetectionService.getMaxCustomInitializationStatus()
-                                && status != INITIALIZATION_STATUS_UNKNOWN) {
-                            status = INITIALIZATION_STATUS_UNKNOWN;
-                        }
-                        callback.onStatusReported(status);
+                        mCallback.onStatusReported(status);
                     } catch (RemoteException e) {
                         Slog.w(TAG, "Failed to report initialization status: " + e);
                     }
@@ -214,13 +205,13 @@
                 .whenComplete((res, err) -> {
                     if (err instanceof TimeoutException) {
                         Slog.w(TAG, "updateState timed out");
+                        if (mUpdateStateAfterStartFinished.getAndSet(true)) {
+                            return;
+                        }
                         try {
-                            if (mUpdateStateFinish.getAndSet(true)) {
-                                return;
-                            }
-                            callback.onStatusReported(INITIALIZATION_STATUS_UNKNOWN);
+                            mCallback.onStatusReported(INITIALIZATION_STATUS_UNKNOWN);
                         } catch (RemoteException e) {
-                            Slog.w(TAG, "Failed to report initialization status: " + e);
+                            Slog.w(TAG, "Failed to report initialization status UNKNOWN", e);
                         }
                     } else if (err != null) {
                         Slog.w(TAG, "Failed to update state: " + err);
@@ -230,27 +221,9 @@
                 });
     }
 
-    private void updateAudioFlinger() {
-        // TODO: Consider using a proxy that limits the exposed API surface.
-        IBinder audioFlinger = ServiceManager.getService("media.audio_flinger");
-        if (audioFlinger == null) {
-            throw new IllegalStateException("Service media.audio_flinger wasn't found.");
-        }
-        mRemoteHotwordDetectionService.post(service -> service.updateAudioFlinger(audioFlinger));
-    }
-
-    private void updateContentCaptureManager() {
-        IBinder b = ServiceManager
-                .getService(Context.CONTENT_CAPTURE_MANAGER_SERVICE);
-        IContentCaptureManager binderService = IContentCaptureManager.Stub.asInterface(b);
-        mRemoteHotwordDetectionService.post(
-                service -> service.updateContentCaptureManager(binderService,
-                        new ContentCaptureOptions(null)));
-    }
-
     private boolean isBound() {
         synchronized (mLock) {
-            return mBound;
+            return mRemoteHotwordDetectionService.isBound();
         }
     }
 
@@ -258,18 +231,25 @@
         if (DEBUG) {
             Slog.d(TAG, "cancelLocked");
         }
-        if (mBound) {
+        if (mRemoteHotwordDetectionService.isBound()) {
             mRemoteHotwordDetectionService.unbind();
-            mBound = false;
             LocalServices.getService(PermissionManagerServiceInternal.class)
                     .setHotwordDetectionServiceProvider(null);
             mIdentity = null;
         }
+        mCancellationTaskFuture.cancel(/* may interrupt */ true);
     }
 
     void updateStateLocked(PersistableBundle options, SharedMemory sharedMemory) {
-        mRemoteHotwordDetectionService.run(
-                service -> service.updateState(options, sharedMemory, null /* callback */));
+        // Prevent doing the init late, so restart is handled equally to a clean process start.
+        // TODO(b/191742511): this logic needs a test
+        if (!mUpdateStateAfterStartFinished.get()
+                && Instant.now().minus(MAX_UPDATE_TIMEOUT_DURATION).isBefore(mLastRestartInstant)) {
+            updateStateAfterProcessStart(options, sharedMemory);
+        } else {
+            mRemoteHotwordDetectionService.run(
+                    service -> service.updateState(options, sharedMemory, null /* callback */));
+        }
     }
 
     void startListeningFromMic(
@@ -278,7 +258,20 @@
         if (DEBUG) {
             Slog.d(TAG, "startListeningFromMic");
         }
+        mSoftwareCallback = callback;
 
+        synchronized (mLock) {
+            if (mPerformingSoftwareHotwordDetection) {
+                Slog.i(TAG, "Hotword validation is already in progress, ignoring.");
+                return;
+            }
+            mPerformingSoftwareHotwordDetection = true;
+
+            startListeningFromMicLocked();
+        }
+    }
+
+    private void startListeningFromMicLocked() {
         // TODO: consider making this a non-anonymous class.
         IDspHotwordDetectionCallback internalCallback = new IDspHotwordDetectionCallback.Stub() {
             @Override
@@ -286,15 +279,22 @@
                 if (DEBUG) {
                     Slog.d(TAG, "onDetected");
                 }
-                callback.onDetected(result, null, null);
+                synchronized (mLock) {
+                    if (mPerformingSoftwareHotwordDetection) {
+                        mSoftwareCallback.onDetected(result, null, null);
+                        mPerformingSoftwareHotwordDetection = false;
+                    } else {
+                        Slog.i(TAG, "Hotword detection has already completed");
+                    }
+                }
             }
 
             @Override
             public void onRejected(HotwordRejectedResult result) throws RemoteException {
                 if (DEBUG) {
-                    Slog.d(TAG, "onRejected");
+                    Slog.wtf(TAG, "onRejected");
                 }
-                // onRejected isn't allowed here
+                // onRejected isn't allowed here, and we are not expecting it.
             }
         };
 
@@ -315,6 +315,7 @@
         if (DEBUG) {
             Slog.d(TAG, "startListeningFromExternalSource");
         }
+
         handleExternalSourceHotwordDetection(
                 audioStream,
                 audioFormat,
@@ -326,18 +327,27 @@
         if (DEBUG) {
             Slog.d(TAG, "stopListening");
         }
-
-        mRemoteHotwordDetectionService.run(service -> service.stopDetection());
-
         synchronized (mLock) {
-            if (mCurrentAudioSink != null) {
-                Slog.i(TAG, "Closing audio stream to hotword detector: stopping requested");
-                bestEffortClose(mCurrentAudioSink);
-            }
-            mCurrentAudioSink = null;
+            stopListeningLocked();
         }
     }
 
+    private void stopListeningLocked() {
+        if (!mPerformingSoftwareHotwordDetection) {
+            Slog.i(TAG, "Hotword detection is not running");
+            return;
+        }
+        mPerformingSoftwareHotwordDetection = false;
+
+        mRemoteHotwordDetectionService.run(IHotwordDetectionService::stopDetection);
+
+        if (mCurrentAudioSink != null) {
+            Slog.i(TAG, "Closing audio stream to hotword detector: stopping requested");
+            bestEffortClose(mCurrentAudioSink);
+        }
+        mCurrentAudioSink = null;
+    }
+
     void triggerHardwareRecognitionEventForTestLocked(
             SoundTrigger.KeyphraseRecognitionEvent event,
             IHotwordRecognitionStatusCallback callback) {
@@ -358,7 +368,14 @@
                 if (DEBUG) {
                     Slog.d(TAG, "onDetected");
                 }
-                externalCallback.onKeyphraseDetected(recognitionEvent, result);
+                synchronized (mLock) {
+                    if (mValidatingDspTrigger) {
+                        mValidatingDspTrigger = false;
+                        externalCallback.onKeyphraseDetected(recognitionEvent, result);
+                    } else {
+                        Slog.i(TAG, "Ignored hotword detected since trigger has been handled");
+                    }
+                }
             }
 
             @Override
@@ -366,16 +383,26 @@
                 if (DEBUG) {
                     Slog.d(TAG, "onRejected");
                 }
-                externalCallback.onRejected(result);
+                synchronized (mLock) {
+                    if (mValidatingDspTrigger) {
+                        mValidatingDspTrigger = false;
+                        externalCallback.onRejected(result);
+                    } else {
+                        Slog.i(TAG, "Ignored hotword rejected since trigger has been handled");
+                    }
+                }
             }
         };
 
-        mRemoteHotwordDetectionService.run(
-                service -> service.detectFromDspSource(
-                        recognitionEvent,
-                        recognitionEvent.getCaptureFormat(),
-                        VALIDATION_TIMEOUT_MILLIS,
-                        internalCallback));
+        synchronized (mLock) {
+            mValidatingDspTrigger = true;
+            mRemoteHotwordDetectionService.run(
+                    service -> service.detectFromDspSource(
+                            recognitionEvent,
+                            recognitionEvent.getCaptureFormat(),
+                            VALIDATION_TIMEOUT_MILLIS,
+                            internalCallback));
+        }
     }
 
     private void detectFromDspSource(SoundTrigger.KeyphraseRecognitionEvent recognitionEvent,
@@ -391,7 +418,14 @@
                 if (DEBUG) {
                     Slog.d(TAG, "onDetected");
                 }
-                externalCallback.onKeyphraseDetected(recognitionEvent, result);
+                synchronized (mLock) {
+                    if (!mValidatingDspTrigger) {
+                        Slog.i(TAG, "Ignoring #onDetected due to a process restart");
+                        return;
+                    }
+                    mValidatingDspTrigger = false;
+                    externalCallback.onKeyphraseDetected(recognitionEvent, result);
+                }
             }
 
             @Override
@@ -399,16 +433,88 @@
                 if (DEBUG) {
                     Slog.d(TAG, "onRejected");
                 }
-                externalCallback.onRejected(result);
+                synchronized (mLock) {
+                    if (!mValidatingDspTrigger) {
+                        Slog.i(TAG, "Ignoring #onRejected due to a process restart");
+                        return;
+                    }
+                    mValidatingDspTrigger = false;
+                    externalCallback.onRejected(result);
+                }
             }
         };
 
-        mRemoteHotwordDetectionService.run(
-                service -> service.detectFromDspSource(
-                        recognitionEvent,
-                        recognitionEvent.getCaptureFormat(),
-                        VALIDATION_TIMEOUT_MILLIS,
-                        internalCallback));
+        synchronized (mLock) {
+            mValidatingDspTrigger = true;
+            mRemoteHotwordDetectionService.run(
+                    service -> service.detectFromDspSource(
+                            recognitionEvent,
+                            recognitionEvent.getCaptureFormat(),
+                            VALIDATION_TIMEOUT_MILLIS,
+                            internalCallback));
+        }
+    }
+
+    void forceRestart() {
+        if (DEBUG) {
+            Slog.i(TAG, "Requested to restart the service internally. Performing the restart");
+        }
+        synchronized (mLock) {
+            restartProcessLocked();
+        }
+    }
+
+    private void restartProcessLocked() {
+        if (DEBUG) {
+            Slog.i(TAG, "Restarting hotword detection process");
+        }
+
+        ServiceConnection oldConnection = mRemoteHotwordDetectionService;
+
+        // TODO(volnov): this can be done after connect() has been successful.
+        if (mValidatingDspTrigger) {
+            // We're restarting the process while it's processing a DSP trigger, so report a
+            // rejection. This also allows the Interactor to startReco again
+            try {
+                mCallback.onRejected(new HotwordRejectedResult.Builder().build());
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to call #rejected");
+            }
+            mValidatingDspTrigger = false;
+        }
+
+        mUpdateStateAfterStartFinished.set(false);
+        mLastRestartInstant = Instant.now();
+
+        // Recreate connection to reset the cache.
+        mRemoteHotwordDetectionService = mServiceConnectionFactory.create();
+
+        if (DEBUG) {
+            Slog.i(TAG, "Started the new process, issuing #onProcessRestarted");
+        }
+        try {
+            mCallback.onProcessRestarted();
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to communicate #onProcessRestarted", e);
+        }
+
+        // Restart listening from microphone if the hotword process has been restarted.
+        if (mPerformingSoftwareHotwordDetection) {
+            Slog.i(TAG, "Process restarted: calling startRecognition() again");
+            startListeningFromMicLocked();
+        }
+
+        if (mCurrentAudioSink != null) {
+            Slog.i(TAG, "Closing external audio stream to hotword detector: process restarted");
+            bestEffortClose(mCurrentAudioSink);
+            mCurrentAudioSink = null;
+        }
+
+        if (DEBUG) {
+            Slog.i(TAG, "#onProcessRestarted called, unbinding from the old process");
+        }
+        oldConnection.ignoreConnectionStatusEvents();
+        oldConnection.unbind();
     }
 
     static final class SoundTriggerCallback extends IRecognitionStatusCallback.Stub {
@@ -462,139 +568,13 @@
         }
     }
 
-    // TODO: figure out if we need to let the client configure some of the parameters.
-    private static AudioRecord createAudioRecord(
-            @NonNull SoundTrigger.KeyphraseRecognitionEvent recognitionEvent) {
-        int sampleRate = recognitionEvent.getCaptureFormat().getSampleRate();
-        return new AudioRecord(
-                new AudioAttributes.Builder()
-                        .setInternalCapturePreset(MediaRecorder.AudioSource.HOTWORD).build(),
-                recognitionEvent.getCaptureFormat(),
-                getBufferSizeInBytes(
-                        sampleRate,
-                        MAX_STREAMING_SECONDS,
-                        recognitionEvent.getCaptureFormat().getChannelCount()),
-                recognitionEvent.getCaptureSession());
-    }
-
-    @Nullable
-    private AudioRecord createMicAudioRecord(AudioFormat audioFormat) {
-        if (DEBUG) {
-            Slog.i(TAG, "#createAudioRecord");
-        }
-        try {
-            AudioRecord audioRecord = new AudioRecord(
-                    new AudioAttributes.Builder()
-                            .setInternalCapturePreset(MediaRecorder.AudioSource.HOTWORD).build(),
-                    audioFormat,
-                    getBufferSizeInBytes(
-                            audioFormat.getSampleRate(),
-                            MICROPHONE_BUFFER_LENGTH_SECONDS,
-                            audioFormat.getChannelCount()),
-                    AudioManager.AUDIO_SESSION_ID_GENERATE);
-
-            if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
-                Slog.w(TAG, "Failed to initialize AudioRecord");
-                audioRecord.release();
-                return null;
-            }
-
-            return audioRecord;
-        } catch (IllegalArgumentException e) {
-            Slog.e(TAG, "Failed to create AudioRecord", e);
-            return null;
-        }
-    }
-
-    @Nullable
-    private AudioRecord createFakeAudioRecord() {
-        if (DEBUG) {
-            Slog.i(TAG, "#createFakeAudioRecord");
-        }
-        try {
-            AudioRecord audioRecord = new AudioRecord.Builder()
-                    .setAudioFormat(new AudioFormat.Builder()
-                            .setSampleRate(32000)
-                            .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
-                            .setChannelMask(AudioFormat.CHANNEL_IN_MONO).build())
-                    .setAudioAttributes(new AudioAttributes.Builder()
-                            .setInternalCapturePreset(MediaRecorder.AudioSource.HOTWORD).build())
-                    .setBufferSizeInBytes(
-                            AudioRecord.getMinBufferSize(32000,
-                                    AudioFormat.CHANNEL_IN_MONO,
-                                    AudioFormat.ENCODING_PCM_16BIT) * 2)
-                    .build();
-
-            if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
-                Slog.w(TAG, "Failed to initialize AudioRecord");
-                audioRecord.release();
-                return null;
-            }
-            return audioRecord;
-        } catch (IllegalArgumentException e) {
-            Slog.e(TAG, "Failed to create AudioRecord", e);
-        }
-        return null;
-    }
-
-    /**
-     * Returns the number of bytes required to store {@code bufferLengthSeconds} of audio sampled at
-     * {@code sampleRate} Hz, using the format returned by DSP audio capture.
-     */
-    private static int getBufferSizeInBytes(
-            int sampleRate, int bufferLengthSeconds, int intChannelCount) {
-        return BYTES_PER_SAMPLE * sampleRate * bufferLengthSeconds * intChannelCount;
-    }
-
-    private static Pair<ParcelFileDescriptor, ParcelFileDescriptor> createPipe() {
-        ParcelFileDescriptor[] fileDescriptors;
-        try {
-            fileDescriptors = ParcelFileDescriptor.createPipe();
-        } catch (IOException e) {
-            Slog.e(TAG, "Failed to create audio stream pipe", e);
-            return null;
-        }
-
-        return Pair.create(fileDescriptors[0], fileDescriptors[1]);
-    }
-
     public void dump(String prefix, PrintWriter pw) {
-        pw.print(prefix); pw.print("mBound="); pw.println(mBound);
-    }
-
-    private interface AudioReader extends Closeable {
-        int read(byte[] dest, int offset, int length) throws IOException;
-
-        static AudioReader createFromInputStream(InputStream is) {
-            return new AudioReader() {
-                @Override
-                public int read(byte[] dest, int offset, int length) throws IOException {
-                    return is.read(dest, offset, length);
-                }
-
-                @Override
-                public void close() throws IOException {
-                    is.close();
-                }
-            };
-        }
-
-        static AudioReader createFromAudioRecord(AudioRecord record) {
-            record.startRecording();
-
-            return new AudioReader() {
-                @Override
-                public int read(byte[] dest, int offset, int length) throws IOException {
-                    return record.read(dest, offset, length);
-                }
-
-                @Override
-                public void close() throws IOException {
-                    record.stop();
-                    record.release();
-                }
-            };
-        }
+        pw.print(prefix);
+        pw.print("mBound=" + mRemoteHotwordDetectionService.isBound());
+        pw.print(", mValidatingDspTrigger=" + mValidatingDspTrigger);
+        pw.print(", mPerformingSoftwareHotwordDetection=" + mPerformingSoftwareHotwordDetection);
+        pw.print(", mRestartCount=" + mServiceConnectionFactory.mRestartCount);
+        pw.println(", mLastRestartInstant=" + mLastRestartInstant);
     }
 
     private void handleExternalSourceHotwordDetection(
@@ -605,8 +585,7 @@
         if (DEBUG) {
             Slog.d(TAG, "#handleExternalSourceHotwordDetection");
         }
-        AudioReader audioSource = AudioReader.createFromInputStream(
-                new ParcelFileDescriptor.AutoCloseInputStream(audioStream));
+        InputStream audioSource = new ParcelFileDescriptor.AutoCloseInputStream(audioStream);
 
         Pair<ParcelFileDescriptor, ParcelFileDescriptor> clientPipe = createPipe();
         if (clientPipe == null) {
@@ -621,7 +600,7 @@
         }
 
         mAudioCopyExecutor.execute(() -> {
-            try (AudioReader source = audioSource;
+            try (InputStream source = audioSource;
                  OutputStream fos =
                          new ParcelFileDescriptor.AutoCloseOutputStream(serviceAudioSink)) {
 
@@ -681,6 +660,150 @@
                         }));
     }
 
+    private class ServiceConnectionFactory {
+        private final Intent mIntent;
+        private final int mBindingFlags;
+
+        private int mRestartCount = 0;
+
+        ServiceConnectionFactory(@NonNull Intent intent, boolean bindInstantServiceAllowed) {
+            mIntent = intent;
+            mBindingFlags = bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0;
+        }
+
+        ServiceConnection create() {
+            ServiceConnection connection =
+                    new ServiceConnection(mContext, mIntent, mBindingFlags, mUser,
+                            IHotwordDetectionService.Stub::asInterface, ++mRestartCount);
+            connection.connect();
+
+            updateAudioFlinger(connection);
+            updateContentCaptureManager(connection);
+            return connection;
+        }
+    }
+
+    private class ServiceConnection extends ServiceConnector.Impl<IHotwordDetectionService> {
+        private final Object mLock = new Object();
+
+        private final Intent mIntent;
+        private final int mBindingFlags;
+        private final int mInstanceNumber;
+
+        private boolean mRespectServiceConnectionStatusChanged = true;
+        private boolean mIsBound = false;
+
+        ServiceConnection(@NonNull Context context,
+                @NonNull Intent intent, int bindingFlags, int userId,
+                @Nullable Function<IBinder, IHotwordDetectionService> binderAsInterface,
+                int instanceNumber) {
+            super(context, intent, bindingFlags, userId, binderAsInterface);
+            this.mIntent = intent;
+            this.mBindingFlags = bindingFlags;
+            this.mInstanceNumber = instanceNumber;
+        }
+
+        @Override // from ServiceConnector.Impl
+        protected void onServiceConnectionStatusChanged(IHotwordDetectionService service,
+                boolean connected) {
+            if (DEBUG) {
+                Slog.d(TAG, "onServiceConnectionStatusChanged connected = " + connected);
+            }
+            synchronized (mLock) {
+                if (!mRespectServiceConnectionStatusChanged) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Ignored onServiceConnectionStatusChanged event");
+                    }
+                    return;
+                }
+                mIsBound = connected;
+            }
+        }
+
+        @Override
+        protected long getAutoDisconnectTimeoutMs() {
+            return -1;
+        }
+
+        @Override
+        public void binderDied() {
+            super.binderDied();
+            synchronized (mLock) {
+                if (!mRespectServiceConnectionStatusChanged) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Ignored #binderDied event");
+                    }
+                    return;
+                }
+
+                Slog.w(TAG, "binderDied");
+                try {
+                    mCallback.onError(-1);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Failed to report onError status: " + e);
+                }
+            }
+        }
+
+        @Override
+        protected boolean bindService(
+                @NonNull android.content.ServiceConnection serviceConnection) {
+            try {
+                return mContext.bindIsolatedService(
+                        mIntent,
+                        Context.BIND_AUTO_CREATE | mBindingFlags,
+                        "hotword_detector_" + mInstanceNumber,
+                        mExecutor,
+                        serviceConnection);
+            } catch (IllegalArgumentException e) {
+                Slog.wtf(TAG, "Can't bind to the hotword detection service!", e);
+                return false;
+            }
+        }
+
+        boolean isBound() {
+            synchronized (mLock) {
+                return mIsBound;
+            }
+        }
+
+        void ignoreConnectionStatusEvents() {
+            synchronized (mLock) {
+                mRespectServiceConnectionStatusChanged = false;
+            }
+        }
+    }
+
+    private static Pair<ParcelFileDescriptor, ParcelFileDescriptor> createPipe() {
+        ParcelFileDescriptor[] fileDescriptors;
+        try {
+            fileDescriptors = ParcelFileDescriptor.createPipe();
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to create audio stream pipe", e);
+            return null;
+        }
+
+        return Pair.create(fileDescriptors[0], fileDescriptors[1]);
+    }
+
+    private static void updateAudioFlinger(ServiceConnection connection) {
+        // TODO: Consider using a proxy that limits the exposed API surface.
+        IBinder audioFlinger = ServiceManager.getService("media.audio_flinger");
+        if (audioFlinger == null) {
+            throw new IllegalStateException("Service media.audio_flinger wasn't found.");
+        }
+        connection.post(service -> service.updateAudioFlinger(audioFlinger));
+    }
+
+    private static void updateContentCaptureManager(ServiceConnection connection) {
+        IBinder b = ServiceManager
+                .getService(Context.CONTENT_CAPTURE_MANAGER_SERVICE);
+        IContentCaptureManager binderService = IContentCaptureManager.Stub.asInterface(b);
+        connection.post(
+                service -> service.updateContentCaptureManager(binderService,
+                        new ContentCaptureOptions(null)));
+    }
+
     private static void bestEffortClose(Closeable closeable) {
         try {
             closeable.close();
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index bc812c2..162acba 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -795,6 +795,10 @@
                     Settings.Secure.ASSISTANT, null, userHandle);
         }
 
+        void forceRestartHotwordDetector() {
+            mImpl.forceRestartHotwordDetector();
+        }
+
         @Override
         public void showSession(Bundle args, int flags) {
             synchronized (this) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index ca30bc5..89c5a72 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -562,6 +562,14 @@
                 && (serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) == 0;
     }
 
+    void forceRestartHotwordDetector() {
+        if (mHotwordDetectionConnection == null) {
+            Slog.w(TAG, "Failed to force-restart hotword detection: no hotword detection active");
+            return;
+        }
+        mHotwordDetectionConnection.forceRestart();
+    }
+
     public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!mValid) {
             pw.print("  NOT VALID: ");
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java
index 2e3ca01..cdd8f7b 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java
@@ -54,6 +54,8 @@
                 return requestHide(pw);
             case "disable":
                 return requestDisable(pw);
+            case "restart-detection":
+                return requestRestartDetection(pw);
             default:
                 return handleDefaultCommands(cmd);
         }
@@ -74,6 +76,8 @@
             pw.println("");
             pw.println("  disable [true|false]");
             pw.println("    Temporarily disable (when true) service");
+            pw.println("  restart-detection");
+            pw.println("    Force a restart of a hotword detection service");
             pw.println("");
         }
     }
@@ -143,6 +147,16 @@
         return 0;
     }
 
+    private int requestRestartDetection(PrintWriter pw) {
+        Slog.i(TAG, "requestRestartDetection()");
+        try {
+            mService.forceRestartHotwordDetector();
+        } catch (Exception e) {
+            return handleError(pw, "requestRestartDetection()", e);
+        }
+        return 0;
+    }
+
     private static int handleError(PrintWriter pw, String message, Exception e) {
         Slog.e(TAG,  "error calling " + message, e);
         pw.printf("Error calling %s: %s\n", message, e);
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 1953af4..e000265 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1018,6 +1018,16 @@
     // this magic number is a bug ID
     public static final long ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION = 157233955L;
 
+    /**
+     * Enable READ_PHONE_NUMBERS or READ_PRIVILEGED_PHONE_STATE protections on
+     * {@link TelecomManager#getPhoneAccount(PhoneAccountHandle)}.
+     * @hide
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
+    // bug ID
+    public static final long ENABLE_GET_PHONE_ACCOUNT_PERMISSION_PROTECTION = 183407956L;
+
     private static final String TAG = "TelecomManager";
 
 
@@ -1351,6 +1361,9 @@
      * Return the {@link PhoneAccount} for a specified {@link PhoneAccountHandle}. Object includes
      * resources which can be used in a user interface.
      *
+     * Requires Permission:
+     * {@link android.Manifest.permission#READ_PHONE_NUMBERS} for applications targeting API
+     * level 31+.
      * @param account The {@link PhoneAccountHandle}.
      * @return The {@link PhoneAccount} object.
      */
@@ -1358,7 +1371,7 @@
         ITelecomService service = getTelecomService();
         if (service != null) {
             try {
-                return service.getPhoneAccount(account);
+                return service.getPhoneAccount(account, mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.e(TAG, "Error calling ITelecomService#getPhoneAccount", e);
             }
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 18afde7..6f286d9 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -79,7 +79,7 @@
     /**
      * @see TelecomManager#getPhoneAccount
      */
-    PhoneAccount getPhoneAccount(in PhoneAccountHandle account);
+    PhoneAccount getPhoneAccount(in PhoneAccountHandle account, String callingPackage);
 
     /**
      * @see TelecomManager#getAllPhoneAccountsCount
diff --git a/tests/Internal/src/android/app/WallpaperColorsTest.java b/tests/Internal/src/android/app/WallpaperColorsTest.java
index 45d3dade..9ffb236 100644
--- a/tests/Internal/src/android/app/WallpaperColorsTest.java
+++ b/tests/Internal/src/android/app/WallpaperColorsTest.java
@@ -20,6 +20,7 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
+import android.os.Parcel;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -106,4 +107,26 @@
         // This would crash:
         canvas.drawBitmap(image, 0, 0, new Paint());
     }
+
+    /**
+     * Parcelled WallpaperColors object should equal the original.
+     */
+    @Test
+    public void testParcelUnparcel() {
+        Bitmap image = Bitmap.createBitmap(300, 300, Bitmap.Config.ARGB_8888);
+        WallpaperColors colors = WallpaperColors.fromBitmap(image);
+        Parcel parcel = Parcel.obtain();
+        colors.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        WallpaperColors reconstructed = new WallpaperColors(parcel);
+        parcel.recycle();
+        Assert.assertEquals("WallpaperColors recreated from Parcel should equal original",
+                colors, reconstructed);
+        Assert.assertEquals("getAllColors() on WallpaperColors recreated from Parcel should"
+                        + "return the same as the original",
+                colors.getAllColors(), reconstructed.getAllColors());
+        Assert.assertEquals("getMainColors() on WallpaperColors recreated from Parcel should"
+                        + "return the same as the original",
+                colors.getMainColors(), reconstructed.getMainColors());
+    }
 }
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 4ce78aa..dc338ae 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -20,6 +20,8 @@
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -70,6 +72,14 @@
     public static final String GATEWAY_CONNECTION_NAME_PREFIX = "gatewayConnectionName-";
     private static int sGatewayConnectionConfigCount = 0;
 
+    private static VcnGatewayConnectionConfig buildTestConfig(
+            String gatewayConnectionName, IkeTunnelConnectionParams tunnelConnectionParams) {
+        return buildTestConfigWithExposedCaps(
+                new VcnGatewayConnectionConfig.Builder(
+                        gatewayConnectionName, tunnelConnectionParams),
+                EXPOSED_CAPS);
+    }
+
     // Public for use in VcnGatewayConnectionTest
     public static VcnGatewayConnectionConfig buildTestConfig() {
         return buildTestConfigWithExposedCaps(EXPOSED_CAPS);
@@ -83,10 +93,9 @@
                 TUNNEL_CONNECTION_PARAMS);
     }
 
-    // Public for use in VcnGatewayConnectionTest
-    public static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(int... exposedCaps) {
-        final VcnGatewayConnectionConfig.Builder builder =
-                newBuilder().setRetryIntervalsMillis(RETRY_INTERVALS_MS).setMaxMtu(MAX_MTU);
+    private static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(
+            VcnGatewayConnectionConfig.Builder builder, int... exposedCaps) {
+        builder.setRetryIntervalsMillis(RETRY_INTERVALS_MS).setMaxMtu(MAX_MTU);
 
         for (int caps : exposedCaps) {
             builder.addExposedCapability(caps);
@@ -95,6 +104,11 @@
         return builder.build();
     }
 
+    // Public for use in VcnGatewayConnectionTest
+    public static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(int... exposedCaps) {
+        return buildTestConfigWithExposedCaps(newBuilder(), exposedCaps);
+    }
+
     @Test
     public void testBuilderRequiresNonNullGatewayConnectionName() {
         try {
@@ -193,4 +207,46 @@
 
         assertEquals(config, new VcnGatewayConnectionConfig(config.toPersistableBundle()));
     }
+
+    private static IkeTunnelConnectionParams buildTunnelConnectionParams(String ikePsk) {
+        final IkeSessionParams ikeParams =
+                IkeSessionParamsUtilsTest.createBuilderMinimum()
+                        .setAuthPsk(ikePsk.getBytes())
+                        .build();
+        return TunnelConnectionParamsUtilsTest.buildTestParams(ikeParams);
+    }
+
+    @Test
+    public void testTunnelConnectionParamsEquals() throws Exception {
+        final String connectionName = "testTunnelConnectionParamsEquals.connectionName";
+        final String psk = "testTunnelConnectionParamsEquals.psk";
+
+        final IkeTunnelConnectionParams tunnelParams = buildTunnelConnectionParams(psk);
+        final VcnGatewayConnectionConfig config = buildTestConfig(connectionName, tunnelParams);
+
+        final IkeTunnelConnectionParams anotherTunnelParams = buildTunnelConnectionParams(psk);
+        final VcnGatewayConnectionConfig anotherConfig =
+                buildTestConfig(connectionName, anotherTunnelParams);
+
+        assertNotSame(tunnelParams, anotherTunnelParams);
+        assertEquals(tunnelParams, anotherTunnelParams);
+        assertEquals(config, anotherConfig);
+    }
+
+    @Test
+    public void testTunnelConnectionParamsNotEquals() throws Exception {
+        final String connectionName = "testTunnelConnectionParamsNotEquals.connectionName";
+
+        final IkeTunnelConnectionParams tunnelParams =
+                buildTunnelConnectionParams("testTunnelConnectionParamsNotEquals.pskA");
+        final VcnGatewayConnectionConfig config = buildTestConfig(connectionName, tunnelParams);
+
+        final IkeTunnelConnectionParams anotherTunnelParams =
+                buildTunnelConnectionParams("testTunnelConnectionParamsNotEquals.pskB");
+        final VcnGatewayConnectionConfig anotherConfig =
+                buildTestConfig(connectionName, anotherTunnelParams);
+
+        assertNotEquals(tunnelParams, anotherTunnelParams);
+        assertNotEquals(config, anotherConfig);
+    }
 }
diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java
index f681ee1..5d2f9d7 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java
@@ -242,6 +242,27 @@
         verifyUpdateSubscriptionSnapshotNotifiesGatewayConnections(VCN_STATUS_CODE_SAFE_MODE);
     }
 
+    @Test
+    public void testSubscriptionSnapshotUpdatesMobileDataState() {
+        final NetworkRequestListener requestListener = verifyAndGetRequestListener();
+        startVcnGatewayWithCapabilities(requestListener, TEST_CAPS[0]);
+
+        // Expect mobile data enabled from setUp()
+        assertTrue(mVcn.isMobileDataEnabled());
+
+        final TelephonySubscriptionSnapshot updatedSnapshot =
+                mock(TelephonySubscriptionSnapshot.class);
+        doReturn(TEST_SUB_IDS_IN_GROUP)
+                .when(updatedSnapshot)
+                .getAllSubIdsInGroup(eq(TEST_SUB_GROUP));
+        doReturn(false).when(mTelephonyManager).isDataEnabled();
+
+        mVcn.updateSubscriptionSnapshot(updatedSnapshot);
+        mTestLooper.dispatchAll();
+
+        assertFalse(mVcn.isMobileDataEnabled());
+    }
+
     private void triggerVcnRequestListeners(NetworkRequestListener requestListener) {
         for (final int[] caps : TEST_CAPS) {
             startVcnGatewayWithCapabilities(requestListener, caps);